├── .github ├── FUNDING.yml └── workflows │ ├── gh-pages.yml │ ├── publish.yml │ └── test.yml ├── .gitignore ├── LICENSE ├── README.md ├── benchmarks ├── elm.json └── src │ ├── CompactEncodeBenchmark.elm │ ├── Decode.elm │ ├── Encode.elm │ ├── EncodeBenchmark.elm │ ├── FasterCompactEncodeBenchmark.elm │ ├── FasterDecodeBenchmark.elm │ ├── FasterEncodeBenchmark.elm │ ├── JsonBenchmark.elm │ └── SlowDecodeBenchmark.elm ├── elm.json ├── examples ├── elm.json ├── pod.png └── src │ ├── Pod.elm │ ├── Pod.obj.txt │ ├── Pod.png │ ├── Tubes.elm │ └── Viewer.elm ├── flake.lock ├── flake.nix ├── review ├── elm.json └── src │ └── ReviewConfig.elm ├── scripts ├── elm-publish.sh └── gh-pages.sh ├── src └── Obj │ ├── Decode.elm │ └── Encode.elm └── tests ├── AdvancedDecoding.elm ├── Encoding.elm ├── MetadataAndFiltering.elm ├── Parsing.elm └── Primitives.elm /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: w0rm 2 | -------------------------------------------------------------------------------- /.github/workflows/gh-pages.yml: -------------------------------------------------------------------------------- 1 | name: GitHub Pages 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | 7 | jobs: 8 | gh-pages: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - run: git config --global user.name "Andrey Kuzmin" 12 | - run: git config --global user.email "hi@unsoundscapes.com" 13 | 14 | - uses: actions/checkout@v3 15 | - uses: cachix/install-nix-action@v22 16 | with: 17 | github_access_token: ${{ secrets.GITHUB_TOKEN }} 18 | - run: nix develop --command elm-format --validate . 19 | - run: nix develop --command elm-review 20 | - run: nix develop --command elm-test 21 | 22 | - uses: actions/checkout@v3 23 | with: 24 | path: gh-pages 25 | ref: gh-pages 26 | - run: nix develop --command ./scripts/gh-pages.sh 27 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | version: 7 | description: "Confirm the new version" 8 | required: true 9 | branches: [main] 10 | 11 | jobs: 12 | test-and-publish: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - run: git config --global user.name "Andrey Kuzmin" 16 | - run: git config --global user.email "hi@unsoundscapes.com" 17 | 18 | - uses: actions/checkout@v3 19 | - uses: cachix/install-nix-action@v22 20 | with: 21 | github_access_token: ${{ secrets.GITHUB_TOKEN }} 22 | - run: nix develop --command elm-format --validate . 23 | - run: nix develop --command elm-review 24 | - run: nix develop --command elm-test 25 | 26 | - run: nix develop --command ./scripts/elm-publish.sh ${{ github.event.inputs.version }} 27 | 28 | - uses: actions/checkout@v3 29 | with: 30 | path: gh-pages 31 | ref: gh-pages 32 | - run: nix develop --command ./scripts/gh-pages.sh ${{ github.event.inputs.version }} 33 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | pull_request: 5 | branches: [main] 6 | 7 | jobs: 8 | elm-test: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v3 12 | - uses: cachix/install-nix-action@v22 13 | with: 14 | github_access_token: ${{ secrets.GITHUB_TOKEN }} 15 | - run: nix develop --command elm-format --validate . 16 | - run: nix develop --command elm-review 17 | - run: nix develop --command elm-test 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | elm-stuff 2 | .DS_Store 3 | gh-pages 4 | release 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020, Andrey Kuzmin 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # elm-obj-file 2 | 3 | An Elm package to encode and decode 3D geometry in the [OBJ file format](https://en.wikipedia.org/wiki/Wavefront_.obj_file). Meshes are returned as [`TriangularMesh`](https://package.elm-lang.org/packages/ianmackenzie/elm-triangular-mesh/latest) values, which makes them easy to render with [elm-3d-scene](https://package.elm-lang.org/packages/ianmackenzie/elm-3d-scene/latest) but can also be used with any other 3D graphics system. You could even take the geometric data and use it for 3D printing, [physics simulations](https://package.elm-lang.org/packages/w0rm/elm-physics/latest/), [finite element analysis](https://en.wikipedia.org/wiki/Finite_element_method) or whatever other crazy thing you want to do =) 4 | 5 | ![The “Pod” model by Kolja Wilcke](https://unsoundscapes.com/elm-obj-file/examples/pod.png) 6 | 7 | _The “Pod” model by [@01k](https://mobile.twitter.com/01k) rendered with `elm-3d-scene`. [See it live here](https://unsoundscapes.com/elm-obj-file/examples/pod/)._ 8 | 9 | Make sure to check [the viewer example](https://unsoundscapes.com/elm-obj-file/examples/viewer/) that lets you preview OBJ files. 10 | 11 | The examples source code [can be found here](https://github.com/w0rm/elm-obj-file/tree/main/examples). 12 | 13 | ```elm 14 | {-| Load a mesh from an HTTP request. -} 15 | getMesh : Cmd Msg 16 | getMesh = 17 | Http.get 18 | { url = "Pod.obj.txt" 19 | , expect = 20 | Obj.Decode.expectObj GotMesh 21 | Length.centimeters 22 | Obj.Decode.texturedFaces 23 | } 24 | ``` 25 | 26 | _Note the .txt extension: this is currently required to serve files from `elm reactor`._ 27 | 28 | ## Blender Workflow 29 | 30 | To export an OBJ file from Blender choose `File - Export - Wavefront (.obj)`. We recommend the following settings: 31 | 32 | - **Include:** only check “Objects as OBJ Objects”; 33 | - **Transform:** use scale `1.00`, “Y Forward” and “Z Up” to match the Blender coordinate system; 34 | - **Geometry:** only check “Apply Modifiers”, check “Write Normals” for `Obj.Decode.faces` and `Obj.Decode.texturedFaces`, “Include UVs” for `Obj.Decode.texturedTriangles` and `Obj.Decode.texturedFaces`, optionally check “Write Materials” if you want to decode material names. 35 | 36 | Blender collections are not preserved in OBJ groups. To decode individual meshes from the same file, you should rely on the `object` filter. The object name, that Blender produces, is a concatenation of the corresponding object and geometry. For example, the “Pod Body” object that contains “Mesh.001” can be decoded with `Obj.Decode.object "Pod_Body_Mesh.001"`. 37 | 38 | If you want to use the shadow generation functionality from `elm-3d-scene`, your mesh needs to be watertight. Blender has the 3D Print Toolbox add-on, that lets you detect non manifold edges and fix them by clicking the “Make Manifold” button. 39 | 40 | ## OBJ Format Support 41 | 42 | - [x] different combinations of positions, normal vectors and UV (texture coordinates); 43 | - [x] face elements `f`; 44 | - [x] line elements `l`; 45 | - [x] points elements `p`; 46 | - [x] object names `o`; 47 | - [x] group names `g`; 48 | - [x] material names `usemtl`; 49 | - [ ] smoothing groups `s`; 50 | - [ ] free-form curves and surfaces and related data; 51 | - [ ] miscellaneous display and rendering data attributes, e.g. `mtllib`. 52 | -------------------------------------------------------------------------------- /benchmarks/elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "application", 3 | "source-directories": [ 4 | "src", 5 | "../src" 6 | ], 7 | "elm-version": "0.19.1", 8 | "dependencies": { 9 | "direct": { 10 | "elm/browser": "1.0.2", 11 | "elm/core": "1.0.5", 12 | "elm/html": "1.0.0", 13 | "elm/http": "2.0.0", 14 | "elm/json": "1.1.3", 15 | "elm-explorations/benchmark": "1.0.2", 16 | "ianmackenzie/elm-3d-scene": "1.0.1", 17 | "ianmackenzie/elm-geometry": "3.6.0", 18 | "ianmackenzie/elm-triangular-mesh": "1.0.4", 19 | "ianmackenzie/elm-units": "2.6.0" 20 | }, 21 | "indirect": { 22 | "BrianHicks/elm-trend": "2.1.3", 23 | "avh4/elm-color": "1.0.0", 24 | "elm/bytes": "1.0.8", 25 | "elm/file": "1.0.5", 26 | "elm/regex": "1.0.0", 27 | "elm/time": "1.0.0", 28 | "elm/url": "1.0.0", 29 | "elm/virtual-dom": "1.0.2", 30 | "elm-explorations/linear-algebra": "1.0.3", 31 | "elm-explorations/webgl": "1.1.1", 32 | "ianmackenzie/elm-1d-parameter": "1.0.1", 33 | "ianmackenzie/elm-3d-camera": "3.1.0", 34 | "ianmackenzie/elm-float-extra": "1.1.0", 35 | "ianmackenzie/elm-geometry-linear-algebra-interop": "2.0.2", 36 | "ianmackenzie/elm-interval": "2.0.0", 37 | "ianmackenzie/elm-units-interval": "1.1.0", 38 | "mdgriffith/style-elements": "5.0.2", 39 | "robinheghan/murmur3": "1.0.0" 40 | } 41 | }, 42 | "test-dependencies": { 43 | "direct": {}, 44 | "indirect": {} 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /benchmarks/src/CompactEncodeBenchmark.elm: -------------------------------------------------------------------------------- 1 | module CompactEncodeBenchmark exposing (main) 2 | 3 | import Benchmark 4 | import Benchmark.Runner exposing (BenchmarkProgram, program) 5 | import Encode 6 | import Length 7 | import Obj.Decode 8 | import Obj.Encode 9 | import Point3d exposing (Point3d) 10 | import Quantity exposing (Unitless) 11 | import TriangularMesh 12 | import Vector3d exposing (Vector3d) 13 | 14 | 15 | main : BenchmarkProgram 16 | main = 17 | program <| 18 | Benchmark.compare "encodeCompact" 19 | "old texturedFaces" 20 | (\_ -> Encode.encodeCompact Length.inMeters [ Encode.texturedFaces mesh ]) 21 | "texturedFaces" 22 | (\_ -> Obj.Encode.encodeCompact Length.inMeters [ Obj.Encode.texturedFaces mesh ]) 23 | 24 | 25 | mesh : TriangularMesh.TriangularMesh { position : Point3d Length.Meters Obj.Decode.ObjCoordinates, normal : Vector3d Unitless Obj.Decode.ObjCoordinates, uv : ( Float, Float ) } 26 | mesh = 27 | """# Blender v2.90 (sub 0) OBJ File: 'pod.blend' 28 | # www.blender.org 29 | mtllib swivel.mtl 30 | o swivel_Cube 31 | v -0.126193 0.126193 -0.126193 32 | v -0.126193 0.126193 0.126193 33 | v -0.126193 -0.126193 -0.126193 34 | v -0.126193 -0.126193 0.126193 35 | v -0.126193 -0.083240 0.371962 36 | v -0.126193 0.083240 0.371962 37 | v -0.072635 -0.099014 0.622851 38 | v -0.095658 0.173416 0.604848 39 | v -0.450367 -0.036624 0.608745 40 | v -0.450367 0.083251 0.608745 41 | v -0.099626 0.169929 0.551847 42 | v -0.100987 -0.167685 0.551676 43 | v -0.431990 0.083209 0.538414 44 | v -0.431990 -0.083240 0.538414 45 | v -0.127480 -0.084779 0.486781 46 | v -0.414873 -0.083240 0.487081 47 | v -0.127532 0.084811 0.486723 48 | v -0.414873 0.083240 0.487081 49 | v -0.175413 0.083240 0.371962 50 | v -0.117468 0.039116 0.622763 51 | v -0.117485 -0.037224 0.622764 52 | v -0.175413 -0.083240 0.371962 53 | v -0.175521 -0.084431 0.537027 54 | v -0.177127 0.085744 0.537040 55 | v -0.173570 -0.080307 0.488848 56 | v -0.173506 0.080296 0.488910 57 | v -0.133616 -0.142871 0.604835 58 | v -0.178537 0.084174 0.604768 59 | v -0.072577 0.100842 0.622667 60 | v -0.194789 -0.037439 0.604883 61 | v -0.072605 -0.099013 0.576465 62 | v -0.072605 0.100853 0.576465 63 | v -0.117478 0.039091 0.576465 64 | v -0.117478 -0.037251 0.576465 65 | v -0.309591 -0.083362 0.538497 66 | v -0.309593 0.083226 0.538414 67 | v -0.309593 -0.083240 0.487081 68 | v -0.309593 0.083240 0.487081 69 | v -0.312497 0.083791 0.608768 70 | v -0.312499 -0.036504 0.608665 71 | v -0.417499 -0.124548 0.984481 72 | v -0.417515 0.082219 0.984493 73 | v -0.389121 -0.124548 0.984924 74 | v -0.471796 -0.037171 0.697587 75 | v -0.489879 -0.125019 0.787013 76 | v -0.489973 0.082211 0.787488 77 | v -0.471754 0.082224 0.697111 78 | v -0.386174 0.082734 0.789073 79 | v -0.369237 0.082709 0.713454 80 | v -0.386162 -0.124093 0.789559 81 | v -0.369197 -0.038121 0.712988 82 | v -0.491039 -0.124553 0.860510 83 | v -0.490986 0.082214 0.860510 84 | v -0.387186 0.082738 0.862095 85 | v -0.387239 -0.124553 0.862104 86 | v -0.389147 0.082742 0.984914 87 | v -0.490905 0.154364 0.787464 88 | v -0.491548 -0.124552 0.893756 89 | v -0.387106 0.154888 0.789049 90 | v -0.418552 0.224260 0.984463 91 | v -0.390157 0.224723 0.984865 92 | v -0.492165 0.294940 0.860484 93 | v -0.388365 0.295463 0.862069 94 | v -0.387722 0.082739 0.895341 95 | v -0.387749 -0.124552 0.895350 96 | v -0.491532 0.082215 0.893743 97 | v -0.492636 0.294918 0.893725 98 | v -0.388849 0.295488 0.895333 99 | v 0.126193 0.126193 -0.126193 100 | v 0.126193 0.126193 0.126193 101 | v 0.126193 -0.126193 -0.126193 102 | v 0.126193 -0.126193 0.126193 103 | v 0.126193 -0.083240 0.371962 104 | v 0.126193 0.083240 0.371962 105 | v 0.072635 -0.099014 0.622851 106 | v 0.095658 0.173416 0.604848 107 | v 0.450367 -0.036624 0.608745 108 | v 0.450367 0.083251 0.608745 109 | v 0.000000 -0.126193 -0.126193 110 | v 0.000000 -0.126193 0.126193 111 | v 0.000000 0.126193 -0.126193 112 | v 0.000000 0.126193 0.126193 113 | v 0.000000 -0.083240 0.371962 114 | v 0.000000 0.083240 0.371962 115 | v 0.000000 -0.194564 0.604526 116 | v -0.000000 0.198825 0.604963 117 | v 0.099626 0.169929 0.551847 118 | v 0.100987 -0.167685 0.551676 119 | v 0.431990 0.083209 0.538414 120 | v 0.431990 -0.083240 0.538414 121 | v 0.000000 -0.200860 0.553661 122 | v -0.000000 0.202025 0.553459 123 | v 0.127480 -0.084779 0.486781 124 | v 0.414873 -0.083240 0.487081 125 | v 0.127532 0.084811 0.486723 126 | v 0.414873 0.083240 0.487081 127 | v 0.000000 -0.084560 0.485030 128 | v 0.000000 0.084512 0.485052 129 | v 0.175413 0.083240 0.371962 130 | v 0.117468 0.039116 0.622763 131 | v 0.117485 -0.037224 0.622764 132 | v 0.175413 -0.083240 0.371962 133 | v 0.175521 -0.084431 0.537027 134 | v 0.177127 0.085744 0.537040 135 | v 0.173570 -0.080307 0.488848 136 | v 0.173506 0.080296 0.488910 137 | v 0.133616 -0.142871 0.604835 138 | v 0.000000 -0.122648 0.622936 139 | v 0.178537 0.084174 0.604768 140 | v 0.072577 0.100842 0.622667 141 | v 0.194789 -0.037439 0.604883 142 | v -0.000000 0.124407 0.622607 143 | v 0.072605 -0.099013 0.576465 144 | v 0.072605 0.100853 0.576465 145 | v 0.000000 -0.122604 0.576465 146 | v 0.000000 0.124444 0.576465 147 | v 0.117478 0.039091 0.576465 148 | v 0.117478 -0.037251 0.576465 149 | v 0.309591 -0.083362 0.538497 150 | v 0.309593 0.083226 0.538414 151 | v 0.309593 -0.083240 0.487081 152 | v 0.309593 0.083240 0.487081 153 | v 0.312497 0.083791 0.608768 154 | v 0.312499 -0.036504 0.608665 155 | v 0.417499 -0.124548 0.984481 156 | v 0.417515 0.082219 0.984493 157 | v 0.389121 -0.124548 0.984924 158 | v 0.471796 -0.037171 0.697587 159 | v 0.489879 -0.125019 0.787013 160 | v 0.489973 0.082211 0.787488 161 | v 0.471754 0.082224 0.697111 162 | v 0.386174 0.082734 0.789073 163 | v 0.369237 0.082709 0.713454 164 | v 0.386162 -0.124093 0.789559 165 | v 0.369197 -0.038121 0.712988 166 | v 0.491039 -0.124553 0.860510 167 | v 0.490986 0.082214 0.860510 168 | v 0.387186 0.082738 0.862095 169 | v 0.387239 -0.124553 0.862104 170 | v 0.389147 0.082742 0.984914 171 | v 0.490905 0.154364 0.787464 172 | v 0.491548 -0.124552 0.893756 173 | v 0.387106 0.154888 0.789049 174 | v 0.418552 0.224260 0.984463 175 | v 0.390157 0.224723 0.984865 176 | v 0.492165 0.294940 0.860484 177 | v 0.388365 0.295463 0.862069 178 | v 0.387722 0.082739 0.895341 179 | v 0.387749 -0.124552 0.895350 180 | v 0.491532 0.082215 0.893743 181 | v 0.492636 0.294918 0.893725 182 | v 0.388849 0.295488 0.895333 183 | vt 0.106151 0.902035 184 | vt 0.128224 0.902035 185 | vt 0.128224 0.913071 186 | vt 0.106151 0.913071 187 | vt 0.128224 0.935143 188 | vt 0.106151 0.935143 189 | vt 0.321678 0.922000 190 | vt 0.332714 0.922000 191 | vt 0.332714 0.944072 192 | vt 0.321678 0.944072 193 | vt 0.354786 0.944072 194 | vt 0.354786 0.922000 195 | vt 0.354786 0.922000 196 | vt 0.354786 0.944072 197 | vt 0.354786 0.955108 198 | vt 0.354786 0.955108 199 | vt 0.354786 0.944072 200 | vt 0.354786 0.944072 201 | vt 0.354786 0.955108 202 | vt 0.354786 0.944072 203 | vt 0.354786 0.922000 204 | vt 0.354786 0.922000 205 | vt 0.354786 0.944072 206 | vt 0.354786 0.944072 207 | vt 0.354786 0.922000 208 | vt 0.354786 0.922000 209 | vt 0.354786 0.944072 210 | vt 0.354786 0.922000 211 | vt 0.354786 0.922000 212 | vt 0.354786 0.922000 213 | vt 0.354786 0.910963 214 | vt 0.354786 0.910963 215 | vt 0.354786 0.910963 216 | vt 0.354786 0.910963 217 | vt 0.128224 0.946180 218 | vt 0.106151 0.946180 219 | vt 0.354786 0.922000 220 | vt 0.354786 0.910963 221 | vt 0.354786 0.922000 222 | vt 0.354786 0.922000 223 | vt 0.354786 0.944072 224 | vt 0.354786 0.955108 225 | vt 0.354786 0.944072 226 | vt 0.354786 0.944072 227 | vt 0.354786 0.944072 228 | vt 0.354786 0.944072 229 | vt 0.354786 0.944072 230 | vt 0.354786 0.944072 231 | vt 0.354786 0.922000 232 | vt 0.354786 0.922000 233 | vt 0.354786 0.944072 234 | vt 0.354786 0.944072 235 | vt 0.354786 0.922000 236 | vt 0.354786 0.922000 237 | vt 0.354786 0.922000 238 | vt 0.354786 0.922000 239 | vt 0.354786 0.922000 240 | vt 0.354786 0.922000 241 | vt 0.354786 0.922000 242 | vt 0.365822 0.922000 243 | vt 0.365822 0.922000 244 | vt 0.354786 0.944072 245 | vt 0.354786 0.944072 246 | vt 0.365822 0.944072 247 | vt 0.365822 0.944072 248 | vt 0.120411 0.908607 249 | vt 0.131447 0.908607 250 | vt 0.131447 0.930679 251 | vt 0.120411 0.930679 252 | vt 0.120411 0.930679 253 | vt 0.120411 0.908607 254 | vt 0.365822 0.922000 255 | vt 0.354786 0.944072 256 | vt 0.354786 0.944072 257 | vt 0.365822 0.944072 258 | vt 0.354786 0.922000 259 | vt 0.354786 0.944072 260 | vt 0.354786 0.922000 261 | vt 0.354786 0.944072 262 | vt 0.354786 0.922000 263 | vt 0.354786 0.922000 264 | vt 0.354786 0.922000 265 | vt 0.354786 0.922000 266 | vt 0.354786 0.944072 267 | vt 0.354786 0.944072 268 | vt 0.354786 0.944072 269 | vt 0.354786 0.944072 270 | vt 0.354786 0.922000 271 | vt 0.354786 0.922000 272 | vt 0.354786 0.944072 273 | vt 0.354786 0.944072 274 | vt 0.354786 0.944072 275 | vt 0.354786 0.944072 276 | vt 0.354786 0.944072 277 | vt 0.354786 0.944072 278 | vt 0.354786 0.944072 279 | vt 0.354786 0.944072 280 | vt 0.354786 0.944072 281 | vt 0.354786 0.944072 282 | vt 0.106151 0.913071 283 | vt 0.128224 0.913071 284 | vt 0.106151 0.935143 285 | vt 0.128224 0.935143 286 | vt 0.332714 0.944072 287 | vt 0.332714 0.922000 288 | vt 0.354786 0.944072 289 | vt 0.354786 0.944072 290 | vt 0.354786 0.922000 291 | vt 0.354786 0.922000 292 | vt 0.354786 0.944072 293 | vt 0.354786 0.944072 294 | vt 0.354786 0.944072 295 | vt 0.354786 0.944072 296 | vt 0.354786 0.922000 297 | vt 0.354786 0.922000 298 | vt 0.354786 0.944072 299 | vt 0.354786 0.944072 300 | vt 0.354786 0.922000 301 | vt 0.354786 0.922000 302 | vt 0.354786 0.922000 303 | vt 0.354786 0.922000 304 | vt 0.354786 0.922000 305 | vt 0.354786 0.922000 306 | vt 0.354786 0.922000 307 | vt 0.354786 0.922000 308 | vt 0.354786 0.922000 309 | vt 0.354786 0.944072 310 | vt 0.354786 0.955108 311 | vt 0.354786 0.944072 312 | vt 0.354786 0.944072 313 | vt 0.354786 0.944072 314 | vt 0.354786 0.944072 315 | vt 0.354786 0.944072 316 | vt 0.354786 0.944072 317 | vt 0.354786 0.922000 318 | vt 0.354786 0.922000 319 | vt 0.354786 0.944072 320 | vt 0.354786 0.944072 321 | vt 0.354786 0.922000 322 | vt 0.354786 0.922000 323 | vt 0.354786 0.922000 324 | vt 0.354786 0.922000 325 | vt 0.354786 0.922000 326 | vt 0.354786 0.922000 327 | vt 0.354786 0.944072 328 | vt 0.354786 0.944072 329 | vt 0.120411 0.908607 330 | vt 0.120411 0.930679 331 | vt 0.120411 0.908607 332 | vt 0.120411 0.930679 333 | vt 0.354786 0.944072 334 | vt 0.354786 0.944072 335 | vt 0.354786 0.922000 336 | vt 0.354786 0.944072 337 | vt 0.354786 0.922000 338 | vt 0.354786 0.944072 339 | vt 0.354786 0.922000 340 | vt 0.354786 0.922000 341 | vt 0.354786 0.922000 342 | vt 0.354786 0.922000 343 | vt 0.354786 0.944072 344 | vt 0.354786 0.944072 345 | vt 0.354786 0.944072 346 | vt 0.354786 0.944072 347 | vt 0.354786 0.922000 348 | vt 0.354786 0.922000 349 | vt 0.354786 0.944072 350 | vt 0.354786 0.944072 351 | vt 0.354786 0.944072 352 | vt 0.354786 0.944072 353 | vt 0.354786 0.944072 354 | vt 0.354786 0.944072 355 | vt 0.354786 0.944072 356 | vt 0.354786 0.944072 357 | vt 0.354786 0.944072 358 | vt 0.354786 0.944072 359 | vn 0.0000 -1.0000 0.0000 360 | vn -1.0000 0.0000 0.0000 361 | vn 0.0000 0.0000 -1.0000 362 | vn 0.0000 0.9851 0.1722 363 | vn -0.2564 0.9655 -0.0443 364 | vn -0.9675 0.0000 -0.2528 365 | vn -0.7748 0.0000 0.6322 366 | vn -0.0000 -0.8326 0.5539 367 | vn -0.3120 -0.9429 0.1167 368 | vn 0.0000 -0.9851 0.1722 369 | vn 0.0000 1.0000 0.0000 370 | vn -0.0110 -0.5082 -0.8612 371 | vn -0.0005 -1.0000 -0.0013 372 | vn -0.9486 0.0000 -0.3163 373 | vn -0.0090 0.6095 -0.7928 374 | vn -0.0021 1.0000 -0.0046 375 | vn 0.0011 0.9999 -0.0125 376 | vn 0.0213 0.9995 0.0248 377 | vn 0.0212 -0.9995 0.0247 378 | vn 0.0008 -0.9999 -0.0125 379 | vn -0.0001 1.0000 0.0004 380 | vn -0.0982 0.6248 -0.7746 381 | vn -0.0954 -0.9951 0.0265 382 | vn -0.7344 0.6787 0.0048 383 | vn -0.5746 -0.6104 -0.5452 384 | vn -0.8293 -0.4810 -0.2845 385 | vn 0.8092 0.5876 0.0002 386 | vn 0.0000 0.9997 0.0252 387 | vn -0.0919 -0.2545 0.9627 388 | vn -0.1888 0.1599 0.9689 389 | vn -0.2093 -0.1325 0.9688 390 | vn -0.0656 0.2243 0.9723 391 | vn 0.0216 0.9998 0.0003 392 | vn -0.2446 0.0207 0.9694 393 | vn 0.0000 0.0000 1.0000 394 | vn 0.3092 0.9510 0.0006 395 | vn 1.0000 -0.0001 -0.0000 396 | vn 0.3089 -0.9511 -0.0006 397 | vn 0.8089 -0.5880 -0.0002 398 | vn -0.0107 0.9999 0.0069 399 | vn -0.0089 -0.9963 -0.0856 400 | vn -0.6511 0.0000 -0.7590 401 | vn 0.0052 -0.8265 0.5628 402 | vn 0.0309 -0.0020 0.9995 403 | vn -0.0152 -0.0000 0.9999 404 | vn 0.9999 0.0000 0.0156 405 | vn -0.0027 -0.9999 -0.0123 406 | vn 0.0242 -0.6676 -0.7442 407 | vn 0.8789 -0.0010 0.4770 408 | vn 0.9761 0.0002 0.2175 409 | vn -0.0049 0.9999 0.0089 410 | vn -0.0048 1.0000 -0.0011 411 | vn -0.9720 -0.0003 -0.2349 412 | vn -0.9802 -0.0002 -0.1980 413 | vn 0.0045 -1.0000 0.0001 414 | vn 0.9999 -0.0001 0.0144 415 | vn -0.9999 -0.0001 -0.0148 416 | vn 0.0153 -0.0000 -0.9999 417 | vn 0.0112 0.4610 -0.8873 418 | vn -0.0156 0.7836 0.6210 419 | vn -0.7756 -0.0046 0.6312 420 | vn 0.9999 0.0054 0.0153 421 | vn 0.9999 0.0074 0.0084 422 | vn -0.0145 0.0002 0.9999 423 | vn -0.9999 -0.0074 -0.0084 424 | vn 0.0153 -0.0001 -0.9999 425 | vn 0.9998 0.0060 0.0176 426 | vn -0.9999 -0.0054 -0.0153 427 | vn -0.0053 1.0000 -0.0001 428 | vn 0.9999 -0.0002 0.0157 429 | vn -0.9999 0.0002 -0.0159 430 | vn 1.0000 0.0000 0.0000 431 | vn 0.2564 0.9655 -0.0443 432 | vn 0.9675 0.0000 -0.2528 433 | vn 0.7748 0.0000 0.6322 434 | vn 0.3524 -0.9121 0.2095 435 | vn 0.1965 -0.6425 -0.7406 436 | vn 0.0005 -1.0000 -0.0013 437 | vn 0.9486 0.0000 -0.3163 438 | vn 0.1926 0.6353 -0.7479 439 | vn 0.0021 1.0000 -0.0046 440 | vn -0.0011 0.9999 -0.0125 441 | vn -0.0213 0.9995 0.0248 442 | vn -0.0212 -0.9995 0.0247 443 | vn -0.0008 -0.9999 -0.0125 444 | vn 0.0001 1.0000 0.0004 445 | vn 0.7177 0.6838 -0.1314 446 | vn 0.0000 -0.9997 0.0251 447 | vn 0.7344 0.6787 0.0048 448 | vn 0.1001 -0.9910 -0.0889 449 | vn 0.8605 -0.4993 0.1014 450 | vn -0.8092 0.5876 0.0002 451 | vn 0.0964 0.9950 0.0266 452 | vn 0.0919 -0.2545 0.9627 453 | vn 0.1888 0.1599 0.9689 454 | vn 0.2093 -0.1325 0.9688 455 | vn 0.0656 0.2243 0.9723 456 | vn -0.0216 0.9998 0.0003 457 | vn 0.2446 0.0207 0.9694 458 | vn -0.3092 0.9510 0.0006 459 | vn -1.0000 -0.0001 -0.0000 460 | vn -0.3089 -0.9511 -0.0006 461 | vn -0.8089 -0.5880 -0.0002 462 | vn 0.0107 0.9999 0.0069 463 | vn -0.0216 -0.9998 -0.0024 464 | vn 0.6511 0.0000 -0.7590 465 | vn -0.0052 -0.8265 0.5628 466 | vn -0.0309 -0.0020 0.9995 467 | vn 0.0152 -0.0000 0.9999 468 | vn -0.9999 0.0000 0.0156 469 | vn 0.0027 -0.9999 -0.0123 470 | vn -0.0235 -0.7156 -0.6982 471 | vn -0.8789 -0.0010 0.4770 472 | vn -0.9761 0.0002 0.2175 473 | vn 0.0049 0.9999 0.0089 474 | vn 0.0048 1.0000 -0.0011 475 | vn 0.9720 -0.0003 -0.2349 476 | vn 0.9802 -0.0002 -0.1980 477 | vn -0.0045 -1.0000 0.0001 478 | vn -0.9999 -0.0001 0.0144 479 | vn 0.9999 -0.0001 -0.0148 480 | vn 0.0153 0.0000 0.9999 481 | vn -0.0112 0.4610 -0.8873 482 | vn 0.0156 0.7836 0.6210 483 | vn 0.7756 -0.0046 0.6312 484 | vn -0.9999 0.0054 0.0153 485 | vn -0.9999 0.0074 0.0084 486 | vn 0.0145 0.0002 0.9999 487 | vn 0.9999 -0.0074 -0.0084 488 | vn -0.0153 -0.0001 -0.9999 489 | vn -0.9998 0.0060 0.0176 490 | vn 0.9999 -0.0054 -0.0153 491 | vn 0.0053 1.0000 -0.0001 492 | vn -0.9999 -0.0002 0.0157 493 | vn 0.9999 0.0002 -0.0159 494 | vn -0.1037 -0.6728 -0.7325 495 | vn 0.0089 -0.9963 -0.0856 496 | vn 0.0201 0.9933 -0.1139 497 | vn 0.0000 0.9999 -0.0137 498 | vn 0.7269 -0.6740 -0.1316 499 | vn 0.5746 -0.6104 -0.5452 500 | vn 0.0972 -0.9952 -0.0144 501 | vn 0.0982 0.6248 -0.7746 502 | vn 0.0101 0.5031 -0.8642 503 | vn 0.0110 -0.5082 -0.8612 504 | vn 0.3120 -0.9429 0.1167 505 | vn 0.3070 0.9499 0.0590 506 | vn 0.0965 -0.7197 -0.6875 507 | vn 0.0216 -0.9998 -0.0024 508 | vn -0.0201 0.9933 -0.1139 509 | vn -0.0984 0.9950 -0.0148 510 | vn -0.7454 -0.6281 0.2233 511 | vn -0.1001 -0.9910 -0.0889 512 | vn 0.0000 -0.9999 -0.0134 513 | vn -0.7177 0.6838 -0.1314 514 | vn -0.1465 0.4977 -0.8549 515 | vn -0.1965 -0.6425 -0.7406 516 | vn -0.3524 -0.9121 0.2095 517 | vn -0.3070 0.9499 0.0590 518 | usemtl generic 519 | s off 520 | f 79/1/1 80/2/1 4/3/1 3/4/1 521 | f 3/4/2 4/3/2 2/5/2 1/6/2 522 | f 79/7/3 3/8/3 1/9/3 81/10/3 523 | f 2/11/2 4/12/2 5/13/2 6/14/2 524 | f 82/15/4 2/11/4 6/14/4 84/16/4 525 | f 11/17/5 8/18/5 86/19/5 526 | f 13/20/6 14/21/6 9/22/6 10/23/6 527 | f 66/24/7 58/25/7 41/26/7 42/27/7 528 | f 35/28/8 40/29/8 9/22/8 14/21/8 529 | f 12/30/9 91/31/9 85/32/9 530 | f 4/12/10 80/33/10 83/34/10 5/13/10 531 | f 1/6/11 2/5/11 82/35/11 81/36/11 532 | f 15/37/12 97/38/12 91/31/12 533 | f 37/39/13 35/28/13 14/21/13 16/40/13 534 | f 18/41/14 16/40/14 14/21/14 13/20/14 535 | f 98/42/15 17/43/15 11/17/15 536 | f 39/44/16 36/45/16 13/20/16 10/23/16 537 | f 84/16/17 6/14/17 17/43/17 98/42/17 538 | f 26/46/18 19/47/18 38/48/18 539 | f 22/49/19 25/50/19 37/39/19 540 | f 5/13/20 83/34/20 97/38/20 15/37/20 541 | f 36/45/21 38/48/21 18/41/21 13/20/21 542 | f 11/17/22 17/43/22 26/46/22 543 | f 15/37/23 25/50/23 22/49/23 544 | f 8/18/24 11/17/24 24/51/24 28/52/24 545 | f 15/37/25 12/30/25 23/53/25 546 | f 6/14/3 5/13/3 22/49/3 19/47/3 547 | f 12/30/26 27/54/26 30/55/26 548 | f 21/56/27 7/57/27 31/58/27 34/59/27 549 | f 6/14/28 19/47/28 26/46/28 550 | f 108/60/29 7/57/29 27/54/29 85/61/29 551 | f 20/62/30 29/63/30 8/18/30 28/52/30 552 | f 7/57/31 21/56/31 30/55/31 27/54/31 553 | f 29/63/32 112/64/32 86/65/32 8/18/32 554 | f 26/46/33 38/48/33 36/45/33 555 | f 21/56/34 20/62/34 28/52/34 30/55/34 556 | f 31/66/35 115/67/35 116/68/35 32/69/35 557 | f 31/66/35 32/69/35 33/70/35 34/71/35 558 | f 7/57/36 108/60/36 115/72/36 31/58/36 559 | f 20/62/37 21/56/37 34/59/37 33/73/37 560 | f 112/64/38 29/63/38 32/74/38 116/75/38 561 | f 29/63/39 20/62/39 33/73/39 32/74/39 562 | f 28/52/40 24/51/40 36/45/40 39/44/40 563 | f 25/50/41 23/53/41 35/28/41 564 | f 19/47/42 22/49/42 37/39/42 38/48/42 565 | f 23/53/43 30/55/43 40/29/43 35/28/43 566 | f 30/55/44 28/52/44 39/44/44 40/29/44 567 | f 18/41/3 38/48/3 37/39/3 16/40/3 568 | f 43/76/45 56/77/45 42/27/45 41/26/45 569 | f 65/78/46 64/79/46 56/77/46 43/76/46 570 | f 58/25/1 65/78/1 43/76/1 41/26/1 571 | f 9/22/47 40/29/47 51/80/47 44/81/47 572 | f 51/80/48 50/82/48 45/83/48 573 | f 40/29/49 39/44/49 49/84/49 51/80/49 574 | f 51/80/50 49/84/50 48/85/50 50/82/50 575 | f 39/44/51 10/23/51 47/86/51 49/84/51 576 | f 49/84/52 47/86/52 46/87/52 48/85/52 577 | f 10/23/53 9/22/53 44/81/53 47/86/53 578 | f 47/86/54 44/81/54 45/83/54 46/87/54 579 | f 45/83/55 50/82/55 55/88/55 52/89/55 580 | f 50/82/56 48/85/56 54/90/56 55/88/56 581 | f 46/87/57 45/83/57 52/89/57 53/91/57 582 | f 53/91/58 62/92/58 63/93/58 54/90/58 583 | f 59/94/59 57/95/59 62/92/59 63/93/59 584 | f 68/96/60 67/97/60 60/98/60 61/99/60 585 | f 66/24/61 42/27/61 60/98/61 67/97/61 586 | f 64/79/62 54/90/62 63/93/62 68/96/62 587 | f 54/90/63 48/85/63 59/94/63 63/93/63 588 | f 42/27/64 56/77/64 61/99/64 60/98/64 589 | f 46/87/65 53/91/65 62/92/65 57/95/65 590 | f 48/85/66 46/87/66 57/95/66 59/94/66 591 | f 56/77/67 64/79/67 68/96/67 61/99/67 592 | f 53/91/68 66/24/68 67/97/68 62/92/68 593 | f 63/93/69 62/92/69 67/97/69 68/96/69 594 | f 52/89/1 55/88/1 65/78/1 58/25/1 595 | f 55/88/70 54/90/70 64/79/70 65/78/70 596 | f 53/91/71 52/89/71 58/25/71 66/24/71 597 | f 79/1/1 71/100/1 72/101/1 80/2/1 598 | f 71/100/72 69/102/72 70/103/72 72/101/72 599 | f 79/7/3 81/10/3 69/104/3 71/105/3 600 | f 70/106/72 74/107/72 73/108/72 72/109/72 601 | f 82/15/4 84/16/4 74/107/4 70/106/4 602 | f 86/19/73 76/110/73 87/111/73 603 | f 89/112/74 78/113/74 77/114/74 90/115/74 604 | f 150/116/75 126/117/75 125/118/75 142/119/75 605 | f 119/120/8 90/115/8 77/114/8 124/121/8 606 | f 88/122/76 107/123/76 85/32/76 607 | f 72/109/10 73/108/10 83/34/10 80/33/10 608 | f 69/102/11 81/36/11 82/35/11 70/103/11 609 | f 93/124/77 88/122/77 91/31/77 610 | f 121/125/78 94/126/78 90/115/78 119/120/78 611 | f 96/127/79 89/112/79 90/115/79 94/126/79 612 | f 92/128/80 87/111/80 95/129/80 613 | f 123/130/81 78/113/81 89/112/81 120/131/81 614 | f 84/16/82 98/42/82 95/129/82 74/107/82 615 | f 106/132/83 122/133/83 99/134/83 616 | f 102/135/84 121/125/84 105/136/84 617 | f 73/108/85 93/124/85 97/38/85 83/34/85 618 | f 120/131/86 89/112/86 96/127/86 122/133/86 619 | f 87/111/87 104/137/87 106/132/87 620 | f 73/108/88 102/135/88 105/136/88 621 | f 76/110/89 109/138/89 104/137/89 87/111/89 622 | f 93/124/90 105/136/90 103/139/90 623 | f 74/107/3 99/134/3 102/135/3 73/108/3 624 | f 103/139/91 111/140/91 107/123/91 625 | f 101/141/92 118/142/92 113/143/92 75/144/92 626 | f 95/129/93 106/132/93 99/134/93 627 | f 108/60/94 85/61/94 107/123/94 75/144/94 628 | f 100/145/95 109/138/95 76/110/95 110/146/95 629 | f 75/144/96 107/123/96 111/140/96 101/141/96 630 | f 110/146/97 76/110/97 86/65/97 112/64/97 631 | f 120/131/98 122/133/98 106/132/98 632 | f 101/141/99 111/140/99 109/138/99 100/145/99 633 | f 113/147/35 114/148/35 116/68/35 115/67/35 634 | f 113/147/35 118/149/35 117/150/35 114/148/35 635 | f 75/144/100 113/143/100 115/72/100 108/60/100 636 | f 100/145/101 117/151/101 118/142/101 101/141/101 637 | f 112/64/102 116/75/102 114/152/102 110/146/102 638 | f 110/146/103 114/152/103 117/151/103 100/145/103 639 | f 109/138/104 123/130/104 120/131/104 104/137/104 640 | f 105/136/105 121/125/105 119/120/105 641 | f 99/134/106 122/133/106 121/125/106 102/135/106 642 | f 103/139/107 119/120/107 124/121/107 111/140/107 643 | f 111/140/108 124/121/108 123/130/108 109/138/108 644 | f 96/127/3 94/126/3 121/125/3 122/133/3 645 | f 127/153/109 125/118/109 126/117/109 140/154/109 646 | f 149/155/110 127/153/110 140/154/110 148/156/110 647 | f 142/119/1 125/118/1 127/153/1 149/155/1 648 | f 77/114/111 128/157/111 135/158/111 124/121/111 649 | f 128/157/112 129/159/112 134/160/112 650 | f 124/121/113 135/158/113 133/161/113 123/130/113 651 | f 135/158/114 134/160/114 132/162/114 133/161/114 652 | f 123/130/115 133/161/115 131/163/115 78/113/115 653 | f 133/161/116 132/162/116 130/164/116 131/163/116 654 | f 78/113/117 131/163/117 128/157/117 77/114/117 655 | f 131/163/118 130/164/118 129/159/118 128/157/118 656 | f 129/159/119 136/165/119 139/166/119 134/160/119 657 | f 134/160/120 139/166/120 138/167/120 132/162/120 658 | f 130/164/121 137/168/121 136/165/121 129/159/121 659 | f 137/168/122 146/169/122 147/170/122 138/167/122 660 | f 143/171/123 147/170/123 146/169/123 141/172/123 661 | f 152/173/124 145/174/124 144/175/124 151/176/124 662 | f 150/116/125 151/176/125 144/175/125 126/117/125 663 | f 148/156/126 152/173/126 147/170/126 138/167/126 664 | f 138/167/127 147/170/127 143/171/127 132/162/127 665 | f 126/117/128 144/175/128 145/174/128 140/154/128 666 | f 130/164/129 141/172/129 146/169/129 137/168/129 667 | f 132/162/130 143/171/130 141/172/130 130/164/130 668 | f 140/154/131 145/174/131 152/173/131 148/156/131 669 | f 137/168/132 146/169/132 151/176/132 150/116/132 670 | f 147/170/133 152/173/133 151/176/133 146/169/133 671 | f 136/165/1 142/119/1 149/155/1 139/166/1 672 | f 139/166/134 149/155/134 148/156/134 138/167/134 673 | f 137/168/135 150/116/135 142/119/135 136/165/135 674 | f 135/158/136 128/157/136 134/160/136 675 | f 103/139/137 105/136/137 119/120/137 676 | f 104/137/138 120/131/138 106/132/138 677 | f 74/107/139 95/129/139 99/134/139 678 | f 88/122/140 103/139/140 107/123/140 679 | f 88/122/141 93/124/141 103/139/141 680 | f 93/124/142 73/108/142 105/136/142 681 | f 95/129/143 87/111/143 106/132/143 682 | f 98/42/144 92/128/144 95/129/144 683 | f 97/38/145 93/124/145 91/31/145 684 | f 91/31/146 88/122/146 85/32/146 685 | f 92/128/147 86/19/147 87/111/147 686 | f 44/81/148 51/80/148 45/83/148 687 | f 37/39/149 25/50/149 35/28/149 688 | f 24/51/150 26/46/150 36/45/150 689 | f 17/43/151 6/14/151 26/46/151 690 | f 23/53/152 12/30/152 30/55/152 691 | f 25/50/153 15/37/153 23/53/153 692 | f 5/13/154 15/37/154 22/49/154 693 | f 24/51/155 11/17/155 26/46/155 694 | f 92/128/156 98/42/156 11/17/156 695 | f 12/30/157 15/37/157 91/31/157 696 | f 27/54/158 12/30/158 85/32/158 697 | f 92/128/159 11/17/159 86/19/159 698 | """ 699 | |> Obj.Decode.decodeString Length.meters Obj.Decode.texturedFaces 700 | |> Result.withDefault TriangularMesh.empty 701 | -------------------------------------------------------------------------------- /benchmarks/src/EncodeBenchmark.elm: -------------------------------------------------------------------------------- 1 | module EncodeBenchmark exposing (main) 2 | 3 | import Benchmark 4 | import Benchmark.Runner exposing (BenchmarkProgram, program) 5 | import Encode 6 | import Length 7 | import Obj.Decode 8 | import Obj.Encode 9 | import Point3d exposing (Point3d) 10 | import Quantity exposing (Unitless) 11 | import TriangularMesh 12 | import Vector3d exposing (Vector3d) 13 | 14 | 15 | main : BenchmarkProgram 16 | main = 17 | program <| 18 | Benchmark.compare "encode" 19 | "old texturedFaces" 20 | (\_ -> Encode.encode Length.inMeters (Encode.texturedFaces mesh)) 21 | "texturedFaces" 22 | (\_ -> Obj.Encode.encode Length.inMeters (Obj.Encode.texturedFaces mesh)) 23 | 24 | 25 | mesh : TriangularMesh.TriangularMesh { position : Point3d Length.Meters Obj.Decode.ObjCoordinates, normal : Vector3d Unitless Obj.Decode.ObjCoordinates, uv : ( Float, Float ) } 26 | mesh = 27 | """# Blender v2.90 (sub 0) OBJ File: 'pod.blend' 28 | # www.blender.org 29 | mtllib swivel.mtl 30 | o swivel_Cube 31 | v -0.126193 0.126193 -0.126193 32 | v -0.126193 0.126193 0.126193 33 | v -0.126193 -0.126193 -0.126193 34 | v -0.126193 -0.126193 0.126193 35 | v -0.126193 -0.083240 0.371962 36 | v -0.126193 0.083240 0.371962 37 | v -0.072635 -0.099014 0.622851 38 | v -0.095658 0.173416 0.604848 39 | v -0.450367 -0.036624 0.608745 40 | v -0.450367 0.083251 0.608745 41 | v -0.099626 0.169929 0.551847 42 | v -0.100987 -0.167685 0.551676 43 | v -0.431990 0.083209 0.538414 44 | v -0.431990 -0.083240 0.538414 45 | v -0.127480 -0.084779 0.486781 46 | v -0.414873 -0.083240 0.487081 47 | v -0.127532 0.084811 0.486723 48 | v -0.414873 0.083240 0.487081 49 | v -0.175413 0.083240 0.371962 50 | v -0.117468 0.039116 0.622763 51 | v -0.117485 -0.037224 0.622764 52 | v -0.175413 -0.083240 0.371962 53 | v -0.175521 -0.084431 0.537027 54 | v -0.177127 0.085744 0.537040 55 | v -0.173570 -0.080307 0.488848 56 | v -0.173506 0.080296 0.488910 57 | v -0.133616 -0.142871 0.604835 58 | v -0.178537 0.084174 0.604768 59 | v -0.072577 0.100842 0.622667 60 | v -0.194789 -0.037439 0.604883 61 | v -0.072605 -0.099013 0.576465 62 | v -0.072605 0.100853 0.576465 63 | v -0.117478 0.039091 0.576465 64 | v -0.117478 -0.037251 0.576465 65 | v -0.309591 -0.083362 0.538497 66 | v -0.309593 0.083226 0.538414 67 | v -0.309593 -0.083240 0.487081 68 | v -0.309593 0.083240 0.487081 69 | v -0.312497 0.083791 0.608768 70 | v -0.312499 -0.036504 0.608665 71 | v -0.417499 -0.124548 0.984481 72 | v -0.417515 0.082219 0.984493 73 | v -0.389121 -0.124548 0.984924 74 | v -0.471796 -0.037171 0.697587 75 | v -0.489879 -0.125019 0.787013 76 | v -0.489973 0.082211 0.787488 77 | v -0.471754 0.082224 0.697111 78 | v -0.386174 0.082734 0.789073 79 | v -0.369237 0.082709 0.713454 80 | v -0.386162 -0.124093 0.789559 81 | v -0.369197 -0.038121 0.712988 82 | v -0.491039 -0.124553 0.860510 83 | v -0.490986 0.082214 0.860510 84 | v -0.387186 0.082738 0.862095 85 | v -0.387239 -0.124553 0.862104 86 | v -0.389147 0.082742 0.984914 87 | v -0.490905 0.154364 0.787464 88 | v -0.491548 -0.124552 0.893756 89 | v -0.387106 0.154888 0.789049 90 | v -0.418552 0.224260 0.984463 91 | v -0.390157 0.224723 0.984865 92 | v -0.492165 0.294940 0.860484 93 | v -0.388365 0.295463 0.862069 94 | v -0.387722 0.082739 0.895341 95 | v -0.387749 -0.124552 0.895350 96 | v -0.491532 0.082215 0.893743 97 | v -0.492636 0.294918 0.893725 98 | v -0.388849 0.295488 0.895333 99 | v 0.126193 0.126193 -0.126193 100 | v 0.126193 0.126193 0.126193 101 | v 0.126193 -0.126193 -0.126193 102 | v 0.126193 -0.126193 0.126193 103 | v 0.126193 -0.083240 0.371962 104 | v 0.126193 0.083240 0.371962 105 | v 0.072635 -0.099014 0.622851 106 | v 0.095658 0.173416 0.604848 107 | v 0.450367 -0.036624 0.608745 108 | v 0.450367 0.083251 0.608745 109 | v 0.000000 -0.126193 -0.126193 110 | v 0.000000 -0.126193 0.126193 111 | v 0.000000 0.126193 -0.126193 112 | v 0.000000 0.126193 0.126193 113 | v 0.000000 -0.083240 0.371962 114 | v 0.000000 0.083240 0.371962 115 | v 0.000000 -0.194564 0.604526 116 | v -0.000000 0.198825 0.604963 117 | v 0.099626 0.169929 0.551847 118 | v 0.100987 -0.167685 0.551676 119 | v 0.431990 0.083209 0.538414 120 | v 0.431990 -0.083240 0.538414 121 | v 0.000000 -0.200860 0.553661 122 | v -0.000000 0.202025 0.553459 123 | v 0.127480 -0.084779 0.486781 124 | v 0.414873 -0.083240 0.487081 125 | v 0.127532 0.084811 0.486723 126 | v 0.414873 0.083240 0.487081 127 | v 0.000000 -0.084560 0.485030 128 | v 0.000000 0.084512 0.485052 129 | v 0.175413 0.083240 0.371962 130 | v 0.117468 0.039116 0.622763 131 | v 0.117485 -0.037224 0.622764 132 | v 0.175413 -0.083240 0.371962 133 | v 0.175521 -0.084431 0.537027 134 | v 0.177127 0.085744 0.537040 135 | v 0.173570 -0.080307 0.488848 136 | v 0.173506 0.080296 0.488910 137 | v 0.133616 -0.142871 0.604835 138 | v 0.000000 -0.122648 0.622936 139 | v 0.178537 0.084174 0.604768 140 | v 0.072577 0.100842 0.622667 141 | v 0.194789 -0.037439 0.604883 142 | v -0.000000 0.124407 0.622607 143 | v 0.072605 -0.099013 0.576465 144 | v 0.072605 0.100853 0.576465 145 | v 0.000000 -0.122604 0.576465 146 | v 0.000000 0.124444 0.576465 147 | v 0.117478 0.039091 0.576465 148 | v 0.117478 -0.037251 0.576465 149 | v 0.309591 -0.083362 0.538497 150 | v 0.309593 0.083226 0.538414 151 | v 0.309593 -0.083240 0.487081 152 | v 0.309593 0.083240 0.487081 153 | v 0.312497 0.083791 0.608768 154 | v 0.312499 -0.036504 0.608665 155 | v 0.417499 -0.124548 0.984481 156 | v 0.417515 0.082219 0.984493 157 | v 0.389121 -0.124548 0.984924 158 | v 0.471796 -0.037171 0.697587 159 | v 0.489879 -0.125019 0.787013 160 | v 0.489973 0.082211 0.787488 161 | v 0.471754 0.082224 0.697111 162 | v 0.386174 0.082734 0.789073 163 | v 0.369237 0.082709 0.713454 164 | v 0.386162 -0.124093 0.789559 165 | v 0.369197 -0.038121 0.712988 166 | v 0.491039 -0.124553 0.860510 167 | v 0.490986 0.082214 0.860510 168 | v 0.387186 0.082738 0.862095 169 | v 0.387239 -0.124553 0.862104 170 | v 0.389147 0.082742 0.984914 171 | v 0.490905 0.154364 0.787464 172 | v 0.491548 -0.124552 0.893756 173 | v 0.387106 0.154888 0.789049 174 | v 0.418552 0.224260 0.984463 175 | v 0.390157 0.224723 0.984865 176 | v 0.492165 0.294940 0.860484 177 | v 0.388365 0.295463 0.862069 178 | v 0.387722 0.082739 0.895341 179 | v 0.387749 -0.124552 0.895350 180 | v 0.491532 0.082215 0.893743 181 | v 0.492636 0.294918 0.893725 182 | v 0.388849 0.295488 0.895333 183 | vt 0.106151 0.902035 184 | vt 0.128224 0.902035 185 | vt 0.128224 0.913071 186 | vt 0.106151 0.913071 187 | vt 0.128224 0.935143 188 | vt 0.106151 0.935143 189 | vt 0.321678 0.922000 190 | vt 0.332714 0.922000 191 | vt 0.332714 0.944072 192 | vt 0.321678 0.944072 193 | vt 0.354786 0.944072 194 | vt 0.354786 0.922000 195 | vt 0.354786 0.922000 196 | vt 0.354786 0.944072 197 | vt 0.354786 0.955108 198 | vt 0.354786 0.955108 199 | vt 0.354786 0.944072 200 | vt 0.354786 0.944072 201 | vt 0.354786 0.955108 202 | vt 0.354786 0.944072 203 | vt 0.354786 0.922000 204 | vt 0.354786 0.922000 205 | vt 0.354786 0.944072 206 | vt 0.354786 0.944072 207 | vt 0.354786 0.922000 208 | vt 0.354786 0.922000 209 | vt 0.354786 0.944072 210 | vt 0.354786 0.922000 211 | vt 0.354786 0.922000 212 | vt 0.354786 0.922000 213 | vt 0.354786 0.910963 214 | vt 0.354786 0.910963 215 | vt 0.354786 0.910963 216 | vt 0.354786 0.910963 217 | vt 0.128224 0.946180 218 | vt 0.106151 0.946180 219 | vt 0.354786 0.922000 220 | vt 0.354786 0.910963 221 | vt 0.354786 0.922000 222 | vt 0.354786 0.922000 223 | vt 0.354786 0.944072 224 | vt 0.354786 0.955108 225 | vt 0.354786 0.944072 226 | vt 0.354786 0.944072 227 | vt 0.354786 0.944072 228 | vt 0.354786 0.944072 229 | vt 0.354786 0.944072 230 | vt 0.354786 0.944072 231 | vt 0.354786 0.922000 232 | vt 0.354786 0.922000 233 | vt 0.354786 0.944072 234 | vt 0.354786 0.944072 235 | vt 0.354786 0.922000 236 | vt 0.354786 0.922000 237 | vt 0.354786 0.922000 238 | vt 0.354786 0.922000 239 | vt 0.354786 0.922000 240 | vt 0.354786 0.922000 241 | vt 0.354786 0.922000 242 | vt 0.365822 0.922000 243 | vt 0.365822 0.922000 244 | vt 0.354786 0.944072 245 | vt 0.354786 0.944072 246 | vt 0.365822 0.944072 247 | vt 0.365822 0.944072 248 | vt 0.120411 0.908607 249 | vt 0.131447 0.908607 250 | vt 0.131447 0.930679 251 | vt 0.120411 0.930679 252 | vt 0.120411 0.930679 253 | vt 0.120411 0.908607 254 | vt 0.365822 0.922000 255 | vt 0.354786 0.944072 256 | vt 0.354786 0.944072 257 | vt 0.365822 0.944072 258 | vt 0.354786 0.922000 259 | vt 0.354786 0.944072 260 | vt 0.354786 0.922000 261 | vt 0.354786 0.944072 262 | vt 0.354786 0.922000 263 | vt 0.354786 0.922000 264 | vt 0.354786 0.922000 265 | vt 0.354786 0.922000 266 | vt 0.354786 0.944072 267 | vt 0.354786 0.944072 268 | vt 0.354786 0.944072 269 | vt 0.354786 0.944072 270 | vt 0.354786 0.922000 271 | vt 0.354786 0.922000 272 | vt 0.354786 0.944072 273 | vt 0.354786 0.944072 274 | vt 0.354786 0.944072 275 | vt 0.354786 0.944072 276 | vt 0.354786 0.944072 277 | vt 0.354786 0.944072 278 | vt 0.354786 0.944072 279 | vt 0.354786 0.944072 280 | vt 0.354786 0.944072 281 | vt 0.354786 0.944072 282 | vt 0.106151 0.913071 283 | vt 0.128224 0.913071 284 | vt 0.106151 0.935143 285 | vt 0.128224 0.935143 286 | vt 0.332714 0.944072 287 | vt 0.332714 0.922000 288 | vt 0.354786 0.944072 289 | vt 0.354786 0.944072 290 | vt 0.354786 0.922000 291 | vt 0.354786 0.922000 292 | vt 0.354786 0.944072 293 | vt 0.354786 0.944072 294 | vt 0.354786 0.944072 295 | vt 0.354786 0.944072 296 | vt 0.354786 0.922000 297 | vt 0.354786 0.922000 298 | vt 0.354786 0.944072 299 | vt 0.354786 0.944072 300 | vt 0.354786 0.922000 301 | vt 0.354786 0.922000 302 | vt 0.354786 0.922000 303 | vt 0.354786 0.922000 304 | vt 0.354786 0.922000 305 | vt 0.354786 0.922000 306 | vt 0.354786 0.922000 307 | vt 0.354786 0.922000 308 | vt 0.354786 0.922000 309 | vt 0.354786 0.944072 310 | vt 0.354786 0.955108 311 | vt 0.354786 0.944072 312 | vt 0.354786 0.944072 313 | vt 0.354786 0.944072 314 | vt 0.354786 0.944072 315 | vt 0.354786 0.944072 316 | vt 0.354786 0.944072 317 | vt 0.354786 0.922000 318 | vt 0.354786 0.922000 319 | vt 0.354786 0.944072 320 | vt 0.354786 0.944072 321 | vt 0.354786 0.922000 322 | vt 0.354786 0.922000 323 | vt 0.354786 0.922000 324 | vt 0.354786 0.922000 325 | vt 0.354786 0.922000 326 | vt 0.354786 0.922000 327 | vt 0.354786 0.944072 328 | vt 0.354786 0.944072 329 | vt 0.120411 0.908607 330 | vt 0.120411 0.930679 331 | vt 0.120411 0.908607 332 | vt 0.120411 0.930679 333 | vt 0.354786 0.944072 334 | vt 0.354786 0.944072 335 | vt 0.354786 0.922000 336 | vt 0.354786 0.944072 337 | vt 0.354786 0.922000 338 | vt 0.354786 0.944072 339 | vt 0.354786 0.922000 340 | vt 0.354786 0.922000 341 | vt 0.354786 0.922000 342 | vt 0.354786 0.922000 343 | vt 0.354786 0.944072 344 | vt 0.354786 0.944072 345 | vt 0.354786 0.944072 346 | vt 0.354786 0.944072 347 | vt 0.354786 0.922000 348 | vt 0.354786 0.922000 349 | vt 0.354786 0.944072 350 | vt 0.354786 0.944072 351 | vt 0.354786 0.944072 352 | vt 0.354786 0.944072 353 | vt 0.354786 0.944072 354 | vt 0.354786 0.944072 355 | vt 0.354786 0.944072 356 | vt 0.354786 0.944072 357 | vt 0.354786 0.944072 358 | vt 0.354786 0.944072 359 | vn 0.0000 -1.0000 0.0000 360 | vn -1.0000 0.0000 0.0000 361 | vn 0.0000 0.0000 -1.0000 362 | vn 0.0000 0.9851 0.1722 363 | vn -0.2564 0.9655 -0.0443 364 | vn -0.9675 0.0000 -0.2528 365 | vn -0.7748 0.0000 0.6322 366 | vn -0.0000 -0.8326 0.5539 367 | vn -0.3120 -0.9429 0.1167 368 | vn 0.0000 -0.9851 0.1722 369 | vn 0.0000 1.0000 0.0000 370 | vn -0.0110 -0.5082 -0.8612 371 | vn -0.0005 -1.0000 -0.0013 372 | vn -0.9486 0.0000 -0.3163 373 | vn -0.0090 0.6095 -0.7928 374 | vn -0.0021 1.0000 -0.0046 375 | vn 0.0011 0.9999 -0.0125 376 | vn 0.0213 0.9995 0.0248 377 | vn 0.0212 -0.9995 0.0247 378 | vn 0.0008 -0.9999 -0.0125 379 | vn -0.0001 1.0000 0.0004 380 | vn -0.0982 0.6248 -0.7746 381 | vn -0.0954 -0.9951 0.0265 382 | vn -0.7344 0.6787 0.0048 383 | vn -0.5746 -0.6104 -0.5452 384 | vn -0.8293 -0.4810 -0.2845 385 | vn 0.8092 0.5876 0.0002 386 | vn 0.0000 0.9997 0.0252 387 | vn -0.0919 -0.2545 0.9627 388 | vn -0.1888 0.1599 0.9689 389 | vn -0.2093 -0.1325 0.9688 390 | vn -0.0656 0.2243 0.9723 391 | vn 0.0216 0.9998 0.0003 392 | vn -0.2446 0.0207 0.9694 393 | vn 0.0000 0.0000 1.0000 394 | vn 0.3092 0.9510 0.0006 395 | vn 1.0000 -0.0001 -0.0000 396 | vn 0.3089 -0.9511 -0.0006 397 | vn 0.8089 -0.5880 -0.0002 398 | vn -0.0107 0.9999 0.0069 399 | vn -0.0089 -0.9963 -0.0856 400 | vn -0.6511 0.0000 -0.7590 401 | vn 0.0052 -0.8265 0.5628 402 | vn 0.0309 -0.0020 0.9995 403 | vn -0.0152 -0.0000 0.9999 404 | vn 0.9999 0.0000 0.0156 405 | vn -0.0027 -0.9999 -0.0123 406 | vn 0.0242 -0.6676 -0.7442 407 | vn 0.8789 -0.0010 0.4770 408 | vn 0.9761 0.0002 0.2175 409 | vn -0.0049 0.9999 0.0089 410 | vn -0.0048 1.0000 -0.0011 411 | vn -0.9720 -0.0003 -0.2349 412 | vn -0.9802 -0.0002 -0.1980 413 | vn 0.0045 -1.0000 0.0001 414 | vn 0.9999 -0.0001 0.0144 415 | vn -0.9999 -0.0001 -0.0148 416 | vn 0.0153 -0.0000 -0.9999 417 | vn 0.0112 0.4610 -0.8873 418 | vn -0.0156 0.7836 0.6210 419 | vn -0.7756 -0.0046 0.6312 420 | vn 0.9999 0.0054 0.0153 421 | vn 0.9999 0.0074 0.0084 422 | vn -0.0145 0.0002 0.9999 423 | vn -0.9999 -0.0074 -0.0084 424 | vn 0.0153 -0.0001 -0.9999 425 | vn 0.9998 0.0060 0.0176 426 | vn -0.9999 -0.0054 -0.0153 427 | vn -0.0053 1.0000 -0.0001 428 | vn 0.9999 -0.0002 0.0157 429 | vn -0.9999 0.0002 -0.0159 430 | vn 1.0000 0.0000 0.0000 431 | vn 0.2564 0.9655 -0.0443 432 | vn 0.9675 0.0000 -0.2528 433 | vn 0.7748 0.0000 0.6322 434 | vn 0.3524 -0.9121 0.2095 435 | vn 0.1965 -0.6425 -0.7406 436 | vn 0.0005 -1.0000 -0.0013 437 | vn 0.9486 0.0000 -0.3163 438 | vn 0.1926 0.6353 -0.7479 439 | vn 0.0021 1.0000 -0.0046 440 | vn -0.0011 0.9999 -0.0125 441 | vn -0.0213 0.9995 0.0248 442 | vn -0.0212 -0.9995 0.0247 443 | vn -0.0008 -0.9999 -0.0125 444 | vn 0.0001 1.0000 0.0004 445 | vn 0.7177 0.6838 -0.1314 446 | vn 0.0000 -0.9997 0.0251 447 | vn 0.7344 0.6787 0.0048 448 | vn 0.1001 -0.9910 -0.0889 449 | vn 0.8605 -0.4993 0.1014 450 | vn -0.8092 0.5876 0.0002 451 | vn 0.0964 0.9950 0.0266 452 | vn 0.0919 -0.2545 0.9627 453 | vn 0.1888 0.1599 0.9689 454 | vn 0.2093 -0.1325 0.9688 455 | vn 0.0656 0.2243 0.9723 456 | vn -0.0216 0.9998 0.0003 457 | vn 0.2446 0.0207 0.9694 458 | vn -0.3092 0.9510 0.0006 459 | vn -1.0000 -0.0001 -0.0000 460 | vn -0.3089 -0.9511 -0.0006 461 | vn -0.8089 -0.5880 -0.0002 462 | vn 0.0107 0.9999 0.0069 463 | vn -0.0216 -0.9998 -0.0024 464 | vn 0.6511 0.0000 -0.7590 465 | vn -0.0052 -0.8265 0.5628 466 | vn -0.0309 -0.0020 0.9995 467 | vn 0.0152 -0.0000 0.9999 468 | vn -0.9999 0.0000 0.0156 469 | vn 0.0027 -0.9999 -0.0123 470 | vn -0.0235 -0.7156 -0.6982 471 | vn -0.8789 -0.0010 0.4770 472 | vn -0.9761 0.0002 0.2175 473 | vn 0.0049 0.9999 0.0089 474 | vn 0.0048 1.0000 -0.0011 475 | vn 0.9720 -0.0003 -0.2349 476 | vn 0.9802 -0.0002 -0.1980 477 | vn -0.0045 -1.0000 0.0001 478 | vn -0.9999 -0.0001 0.0144 479 | vn 0.9999 -0.0001 -0.0148 480 | vn 0.0153 0.0000 0.9999 481 | vn -0.0112 0.4610 -0.8873 482 | vn 0.0156 0.7836 0.6210 483 | vn 0.7756 -0.0046 0.6312 484 | vn -0.9999 0.0054 0.0153 485 | vn -0.9999 0.0074 0.0084 486 | vn 0.0145 0.0002 0.9999 487 | vn 0.9999 -0.0074 -0.0084 488 | vn -0.0153 -0.0001 -0.9999 489 | vn -0.9998 0.0060 0.0176 490 | vn 0.9999 -0.0054 -0.0153 491 | vn 0.0053 1.0000 -0.0001 492 | vn -0.9999 -0.0002 0.0157 493 | vn 0.9999 0.0002 -0.0159 494 | vn -0.1037 -0.6728 -0.7325 495 | vn 0.0089 -0.9963 -0.0856 496 | vn 0.0201 0.9933 -0.1139 497 | vn 0.0000 0.9999 -0.0137 498 | vn 0.7269 -0.6740 -0.1316 499 | vn 0.5746 -0.6104 -0.5452 500 | vn 0.0972 -0.9952 -0.0144 501 | vn 0.0982 0.6248 -0.7746 502 | vn 0.0101 0.5031 -0.8642 503 | vn 0.0110 -0.5082 -0.8612 504 | vn 0.3120 -0.9429 0.1167 505 | vn 0.3070 0.9499 0.0590 506 | vn 0.0965 -0.7197 -0.6875 507 | vn 0.0216 -0.9998 -0.0024 508 | vn -0.0201 0.9933 -0.1139 509 | vn -0.0984 0.9950 -0.0148 510 | vn -0.7454 -0.6281 0.2233 511 | vn -0.1001 -0.9910 -0.0889 512 | vn 0.0000 -0.9999 -0.0134 513 | vn -0.7177 0.6838 -0.1314 514 | vn -0.1465 0.4977 -0.8549 515 | vn -0.1965 -0.6425 -0.7406 516 | vn -0.3524 -0.9121 0.2095 517 | vn -0.3070 0.9499 0.0590 518 | usemtl generic 519 | s off 520 | f 79/1/1 80/2/1 4/3/1 3/4/1 521 | f 3/4/2 4/3/2 2/5/2 1/6/2 522 | f 79/7/3 3/8/3 1/9/3 81/10/3 523 | f 2/11/2 4/12/2 5/13/2 6/14/2 524 | f 82/15/4 2/11/4 6/14/4 84/16/4 525 | f 11/17/5 8/18/5 86/19/5 526 | f 13/20/6 14/21/6 9/22/6 10/23/6 527 | f 66/24/7 58/25/7 41/26/7 42/27/7 528 | f 35/28/8 40/29/8 9/22/8 14/21/8 529 | f 12/30/9 91/31/9 85/32/9 530 | f 4/12/10 80/33/10 83/34/10 5/13/10 531 | f 1/6/11 2/5/11 82/35/11 81/36/11 532 | f 15/37/12 97/38/12 91/31/12 533 | f 37/39/13 35/28/13 14/21/13 16/40/13 534 | f 18/41/14 16/40/14 14/21/14 13/20/14 535 | f 98/42/15 17/43/15 11/17/15 536 | f 39/44/16 36/45/16 13/20/16 10/23/16 537 | f 84/16/17 6/14/17 17/43/17 98/42/17 538 | f 26/46/18 19/47/18 38/48/18 539 | f 22/49/19 25/50/19 37/39/19 540 | f 5/13/20 83/34/20 97/38/20 15/37/20 541 | f 36/45/21 38/48/21 18/41/21 13/20/21 542 | f 11/17/22 17/43/22 26/46/22 543 | f 15/37/23 25/50/23 22/49/23 544 | f 8/18/24 11/17/24 24/51/24 28/52/24 545 | f 15/37/25 12/30/25 23/53/25 546 | f 6/14/3 5/13/3 22/49/3 19/47/3 547 | f 12/30/26 27/54/26 30/55/26 548 | f 21/56/27 7/57/27 31/58/27 34/59/27 549 | f 6/14/28 19/47/28 26/46/28 550 | f 108/60/29 7/57/29 27/54/29 85/61/29 551 | f 20/62/30 29/63/30 8/18/30 28/52/30 552 | f 7/57/31 21/56/31 30/55/31 27/54/31 553 | f 29/63/32 112/64/32 86/65/32 8/18/32 554 | f 26/46/33 38/48/33 36/45/33 555 | f 21/56/34 20/62/34 28/52/34 30/55/34 556 | f 31/66/35 115/67/35 116/68/35 32/69/35 557 | f 31/66/35 32/69/35 33/70/35 34/71/35 558 | f 7/57/36 108/60/36 115/72/36 31/58/36 559 | f 20/62/37 21/56/37 34/59/37 33/73/37 560 | f 112/64/38 29/63/38 32/74/38 116/75/38 561 | f 29/63/39 20/62/39 33/73/39 32/74/39 562 | f 28/52/40 24/51/40 36/45/40 39/44/40 563 | f 25/50/41 23/53/41 35/28/41 564 | f 19/47/42 22/49/42 37/39/42 38/48/42 565 | f 23/53/43 30/55/43 40/29/43 35/28/43 566 | f 30/55/44 28/52/44 39/44/44 40/29/44 567 | f 18/41/3 38/48/3 37/39/3 16/40/3 568 | f 43/76/45 56/77/45 42/27/45 41/26/45 569 | f 65/78/46 64/79/46 56/77/46 43/76/46 570 | f 58/25/1 65/78/1 43/76/1 41/26/1 571 | f 9/22/47 40/29/47 51/80/47 44/81/47 572 | f 51/80/48 50/82/48 45/83/48 573 | f 40/29/49 39/44/49 49/84/49 51/80/49 574 | f 51/80/50 49/84/50 48/85/50 50/82/50 575 | f 39/44/51 10/23/51 47/86/51 49/84/51 576 | f 49/84/52 47/86/52 46/87/52 48/85/52 577 | f 10/23/53 9/22/53 44/81/53 47/86/53 578 | f 47/86/54 44/81/54 45/83/54 46/87/54 579 | f 45/83/55 50/82/55 55/88/55 52/89/55 580 | f 50/82/56 48/85/56 54/90/56 55/88/56 581 | f 46/87/57 45/83/57 52/89/57 53/91/57 582 | f 53/91/58 62/92/58 63/93/58 54/90/58 583 | f 59/94/59 57/95/59 62/92/59 63/93/59 584 | f 68/96/60 67/97/60 60/98/60 61/99/60 585 | f 66/24/61 42/27/61 60/98/61 67/97/61 586 | f 64/79/62 54/90/62 63/93/62 68/96/62 587 | f 54/90/63 48/85/63 59/94/63 63/93/63 588 | f 42/27/64 56/77/64 61/99/64 60/98/64 589 | f 46/87/65 53/91/65 62/92/65 57/95/65 590 | f 48/85/66 46/87/66 57/95/66 59/94/66 591 | f 56/77/67 64/79/67 68/96/67 61/99/67 592 | f 53/91/68 66/24/68 67/97/68 62/92/68 593 | f 63/93/69 62/92/69 67/97/69 68/96/69 594 | f 52/89/1 55/88/1 65/78/1 58/25/1 595 | f 55/88/70 54/90/70 64/79/70 65/78/70 596 | f 53/91/71 52/89/71 58/25/71 66/24/71 597 | f 79/1/1 71/100/1 72/101/1 80/2/1 598 | f 71/100/72 69/102/72 70/103/72 72/101/72 599 | f 79/7/3 81/10/3 69/104/3 71/105/3 600 | f 70/106/72 74/107/72 73/108/72 72/109/72 601 | f 82/15/4 84/16/4 74/107/4 70/106/4 602 | f 86/19/73 76/110/73 87/111/73 603 | f 89/112/74 78/113/74 77/114/74 90/115/74 604 | f 150/116/75 126/117/75 125/118/75 142/119/75 605 | f 119/120/8 90/115/8 77/114/8 124/121/8 606 | f 88/122/76 107/123/76 85/32/76 607 | f 72/109/10 73/108/10 83/34/10 80/33/10 608 | f 69/102/11 81/36/11 82/35/11 70/103/11 609 | f 93/124/77 88/122/77 91/31/77 610 | f 121/125/78 94/126/78 90/115/78 119/120/78 611 | f 96/127/79 89/112/79 90/115/79 94/126/79 612 | f 92/128/80 87/111/80 95/129/80 613 | f 123/130/81 78/113/81 89/112/81 120/131/81 614 | f 84/16/82 98/42/82 95/129/82 74/107/82 615 | f 106/132/83 122/133/83 99/134/83 616 | f 102/135/84 121/125/84 105/136/84 617 | f 73/108/85 93/124/85 97/38/85 83/34/85 618 | f 120/131/86 89/112/86 96/127/86 122/133/86 619 | f 87/111/87 104/137/87 106/132/87 620 | f 73/108/88 102/135/88 105/136/88 621 | f 76/110/89 109/138/89 104/137/89 87/111/89 622 | f 93/124/90 105/136/90 103/139/90 623 | f 74/107/3 99/134/3 102/135/3 73/108/3 624 | f 103/139/91 111/140/91 107/123/91 625 | f 101/141/92 118/142/92 113/143/92 75/144/92 626 | f 95/129/93 106/132/93 99/134/93 627 | f 108/60/94 85/61/94 107/123/94 75/144/94 628 | f 100/145/95 109/138/95 76/110/95 110/146/95 629 | f 75/144/96 107/123/96 111/140/96 101/141/96 630 | f 110/146/97 76/110/97 86/65/97 112/64/97 631 | f 120/131/98 122/133/98 106/132/98 632 | f 101/141/99 111/140/99 109/138/99 100/145/99 633 | f 113/147/35 114/148/35 116/68/35 115/67/35 634 | f 113/147/35 118/149/35 117/150/35 114/148/35 635 | f 75/144/100 113/143/100 115/72/100 108/60/100 636 | f 100/145/101 117/151/101 118/142/101 101/141/101 637 | f 112/64/102 116/75/102 114/152/102 110/146/102 638 | f 110/146/103 114/152/103 117/151/103 100/145/103 639 | f 109/138/104 123/130/104 120/131/104 104/137/104 640 | f 105/136/105 121/125/105 119/120/105 641 | f 99/134/106 122/133/106 121/125/106 102/135/106 642 | f 103/139/107 119/120/107 124/121/107 111/140/107 643 | f 111/140/108 124/121/108 123/130/108 109/138/108 644 | f 96/127/3 94/126/3 121/125/3 122/133/3 645 | f 127/153/109 125/118/109 126/117/109 140/154/109 646 | f 149/155/110 127/153/110 140/154/110 148/156/110 647 | f 142/119/1 125/118/1 127/153/1 149/155/1 648 | f 77/114/111 128/157/111 135/158/111 124/121/111 649 | f 128/157/112 129/159/112 134/160/112 650 | f 124/121/113 135/158/113 133/161/113 123/130/113 651 | f 135/158/114 134/160/114 132/162/114 133/161/114 652 | f 123/130/115 133/161/115 131/163/115 78/113/115 653 | f 133/161/116 132/162/116 130/164/116 131/163/116 654 | f 78/113/117 131/163/117 128/157/117 77/114/117 655 | f 131/163/118 130/164/118 129/159/118 128/157/118 656 | f 129/159/119 136/165/119 139/166/119 134/160/119 657 | f 134/160/120 139/166/120 138/167/120 132/162/120 658 | f 130/164/121 137/168/121 136/165/121 129/159/121 659 | f 137/168/122 146/169/122 147/170/122 138/167/122 660 | f 143/171/123 147/170/123 146/169/123 141/172/123 661 | f 152/173/124 145/174/124 144/175/124 151/176/124 662 | f 150/116/125 151/176/125 144/175/125 126/117/125 663 | f 148/156/126 152/173/126 147/170/126 138/167/126 664 | f 138/167/127 147/170/127 143/171/127 132/162/127 665 | f 126/117/128 144/175/128 145/174/128 140/154/128 666 | f 130/164/129 141/172/129 146/169/129 137/168/129 667 | f 132/162/130 143/171/130 141/172/130 130/164/130 668 | f 140/154/131 145/174/131 152/173/131 148/156/131 669 | f 137/168/132 146/169/132 151/176/132 150/116/132 670 | f 147/170/133 152/173/133 151/176/133 146/169/133 671 | f 136/165/1 142/119/1 149/155/1 139/166/1 672 | f 139/166/134 149/155/134 148/156/134 138/167/134 673 | f 137/168/135 150/116/135 142/119/135 136/165/135 674 | f 135/158/136 128/157/136 134/160/136 675 | f 103/139/137 105/136/137 119/120/137 676 | f 104/137/138 120/131/138 106/132/138 677 | f 74/107/139 95/129/139 99/134/139 678 | f 88/122/140 103/139/140 107/123/140 679 | f 88/122/141 93/124/141 103/139/141 680 | f 93/124/142 73/108/142 105/136/142 681 | f 95/129/143 87/111/143 106/132/143 682 | f 98/42/144 92/128/144 95/129/144 683 | f 97/38/145 93/124/145 91/31/145 684 | f 91/31/146 88/122/146 85/32/146 685 | f 92/128/147 86/19/147 87/111/147 686 | f 44/81/148 51/80/148 45/83/148 687 | f 37/39/149 25/50/149 35/28/149 688 | f 24/51/150 26/46/150 36/45/150 689 | f 17/43/151 6/14/151 26/46/151 690 | f 23/53/152 12/30/152 30/55/152 691 | f 25/50/153 15/37/153 23/53/153 692 | f 5/13/154 15/37/154 22/49/154 693 | f 24/51/155 11/17/155 26/46/155 694 | f 92/128/156 98/42/156 11/17/156 695 | f 12/30/157 15/37/157 91/31/157 696 | f 27/54/158 12/30/158 85/32/158 697 | f 92/128/159 11/17/159 86/19/159 698 | """ 699 | |> Obj.Decode.decodeString Length.meters Obj.Decode.texturedFaces 700 | |> Result.withDefault TriangularMesh.empty 701 | -------------------------------------------------------------------------------- /benchmarks/src/FasterCompactEncodeBenchmark.elm: -------------------------------------------------------------------------------- 1 | module FasterCompactEncodeBenchmark exposing (main) 2 | 3 | import Array 4 | import Benchmark 5 | import Benchmark.Runner exposing (BenchmarkProgram, program) 6 | import Encode 7 | import Length exposing (Meters) 8 | import Obj.Encode 9 | import Point3d exposing (Point3d) 10 | import Quantity exposing (Unitless) 11 | import TriangularMesh exposing (TriangularMesh) 12 | import Vector3d exposing (Vector3d) 13 | 14 | 15 | main : BenchmarkProgram 16 | main = 17 | program <| 18 | Benchmark.compare "encodeCompact" 19 | "old texturedTriangles" 20 | (\_ -> Encode.encodeCompact Length.inMeters [ Encode.texturedTriangles mesh ]) 21 | "texturedTriangles" 22 | (\_ -> Obj.Encode.encodeCompact Length.inMeters [ Obj.Encode.texturedTriangles mesh ]) 23 | 24 | 25 | mesh : TriangularMesh { position : Point3d Meters coords, normal : Vector3d Unitless coords, uv : ( Float, Float ) } 26 | mesh = 27 | let 28 | vertices = 29 | Array.fromList 30 | [ { position = Point3d.meters -4.5 4.5 0, uv = ( 0, 1 ), normal = Vector3d.unitless 0 0 1 } 31 | , { position = Point3d.meters 4.5 4.5 0, uv = ( 1, 1 ), normal = Vector3d.unitless 0 0 1 } 32 | , { position = Point3d.meters 4.5 -4.5 0, uv = ( 1, 0 ), normal = Vector3d.unitless 0 0 1 } 33 | , { position = Point3d.meters -3.5 3.5 0, uv = ( 0, 1 ), normal = Vector3d.unitless 0 0 1 } 34 | , { position = Point3d.meters 3.5 -3.5 0, uv = ( 1, 0 ), normal = Vector3d.unitless 0 0 1 } 35 | , { position = Point3d.meters -3.5 -3.5 0, uv = ( 0, 0 ), normal = Vector3d.unitless 0 0 1 } 36 | , { position = Point3d.meters -4.5 4.5 0, uv = ( 0.5, 1 ), normal = Vector3d.unitless 1 0 0 } 37 | , { position = Point3d.meters 4.5 -4.5 0, uv = ( 1, 0.5 ), normal = Vector3d.unitless 1 0 0 } 38 | , { position = Point3d.meters -4.5 -4.5 0, uv = ( 0.5, 0.5 ), normal = Vector3d.unitless 0 1 0 } 39 | ] 40 | 41 | faceIndices = 42 | [ ( 0, 1, 2 ), ( 3, 4, 5 ), ( 6, 7, 8 ) ] 43 | in 44 | TriangularMesh.indexed vertices faceIndices 45 | -------------------------------------------------------------------------------- /benchmarks/src/FasterDecodeBenchmark.elm: -------------------------------------------------------------------------------- 1 | module FasterDecodeBenchmark exposing (main) 2 | 3 | import Benchmark 4 | import Benchmark.Runner exposing (BenchmarkProgram, program) 5 | import Decode 6 | import Length 7 | import Obj.Decode 8 | 9 | 10 | main : BenchmarkProgram 11 | main = 12 | program <| 13 | Benchmark.compare "decode" 14 | "old triangles" 15 | (\_ -> Decode.decodeString Length.meters Decode.triangles obj |> Result.map (always ())) 16 | "triangles" 17 | (\_ -> Obj.Decode.decodeString Length.meters Obj.Decode.triangles obj |> Result.map (always ())) 18 | 19 | 20 | obj : String 21 | obj = 22 | """# Blender v2.83.3 OBJ File: 'cube' 23 | # www.blender.org 24 | v 1.000000 1.000000 -1.000000 25 | v 1.000000 -1.000000 -1.000000 26 | v 1.000000 1.000000 1.000000 27 | v 1.000000 -1.000000 1.000000 28 | v -1.000000 1.000000 -1.000000 29 | v -1.000000 -1.000000 -1.000000 30 | v -1.000000 1.000000 1.000000 31 | v -1.000000 -1.000000 1.000000 32 | f 1 5 7 3 33 | f 4 3 7 8 34 | f 8 7 5 6 35 | f 6 2 4 8 36 | f 2 1 3 4 37 | f 6 5 1 2 38 | """ 39 | -------------------------------------------------------------------------------- /benchmarks/src/FasterEncodeBenchmark.elm: -------------------------------------------------------------------------------- 1 | module FasterEncodeBenchmark exposing (main) 2 | 3 | import Array 4 | import Benchmark 5 | import Benchmark.Runner exposing (BenchmarkProgram, program) 6 | import Encode 7 | import Length exposing (Meters) 8 | import Obj.Encode 9 | import Point3d exposing (Point3d) 10 | import Quantity exposing (Unitless) 11 | import TriangularMesh exposing (TriangularMesh) 12 | import Vector3d exposing (Vector3d) 13 | 14 | 15 | main : BenchmarkProgram 16 | main = 17 | program <| 18 | Benchmark.compare "encode" 19 | "old texturedTriangles" 20 | (\_ -> Encode.encode Length.inMeters (Encode.texturedTriangles mesh)) 21 | "texturedTriangles" 22 | (\_ -> Obj.Encode.encode Length.inMeters (Obj.Encode.texturedTriangles mesh)) 23 | 24 | 25 | mesh : TriangularMesh { position : Point3d Meters coords, normal : Vector3d Unitless coords, uv : ( Float, Float ) } 26 | mesh = 27 | let 28 | vertices = 29 | Array.fromList 30 | [ { position = Point3d.meters -4.5 4.5 0, uv = ( 0, 1 ), normal = Vector3d.unitless 0 0 1 } 31 | , { position = Point3d.meters 4.5 4.5 0, uv = ( 1, 1 ), normal = Vector3d.unitless 0 0 1 } 32 | , { position = Point3d.meters 4.5 -4.5 0, uv = ( 1, 0 ), normal = Vector3d.unitless 0 0 1 } 33 | , { position = Point3d.meters -3.5 3.5 0, uv = ( 0, 1 ), normal = Vector3d.unitless 0 0 1 } 34 | , { position = Point3d.meters 3.5 -3.5 0, uv = ( 1, 0 ), normal = Vector3d.unitless 0 0 1 } 35 | , { position = Point3d.meters -3.5 -3.5 0, uv = ( 0, 0 ), normal = Vector3d.unitless 0 0 1 } 36 | , { position = Point3d.meters -4.5 4.5 0, uv = ( 0.5, 1 ), normal = Vector3d.unitless 1 0 0 } 37 | , { position = Point3d.meters 4.5 -4.5 0, uv = ( 1, 0.5 ), normal = Vector3d.unitless 1 0 0 } 38 | , { position = Point3d.meters -4.5 -4.5 0, uv = ( 0.5, 0.5 ), normal = Vector3d.unitless 0 1 0 } 39 | ] 40 | 41 | faceIndices = 42 | [ ( 0, 1, 2 ), ( 3, 4, 5 ), ( 6, 7, 8 ) ] 43 | in 44 | TriangularMesh.indexed vertices faceIndices 45 | -------------------------------------------------------------------------------- /benchmarks/src/JsonBenchmark.elm: -------------------------------------------------------------------------------- 1 | module JsonBenchmark exposing (main) 2 | 3 | import Benchmark 4 | import Benchmark.Runner exposing (BenchmarkProgram, program) 5 | import Json.Decode 6 | import Json.Encode 7 | import Length 8 | import Obj.Decode 9 | import Scene3d.Mesh 10 | import Scene3d.Mesh.Decode 11 | import Scene3d.Mesh.Encode 12 | 13 | 14 | {-| Benchmark against Json.Decode using Scene3d.Mesh.Decode 15 | -} 16 | main : BenchmarkProgram 17 | main = 18 | let 19 | json = 20 | Obj.Decode.decodeString Length.meters Obj.Decode.texturedFaces obj 21 | |> Result.map Scene3d.Mesh.texturedFaces 22 | |> Result.map Scene3d.Mesh.Encode.mesh 23 | |> Result.map (Json.Encode.encode 2) 24 | |> Result.withDefault "shouldn't happen" 25 | in 26 | program <| 27 | Benchmark.compare "decode" 28 | "JSON texturedFaces" 29 | (\_ -> Json.Decode.decodeString Scene3d.Mesh.Decode.texturedFaces json) 30 | "OBJ texturedFaces" 31 | (\_ -> 32 | Obj.Decode.decodeString Length.meters Obj.Decode.texturedFaces obj 33 | |> Result.map Scene3d.Mesh.texturedFaces 34 | ) 35 | 36 | 37 | obj : String 38 | obj = 39 | """# Blender v2.90 (sub 0) OBJ File: 'pod.blend' 40 | # www.blender.org 41 | mtllib swivel.mtl 42 | o swivel_Cube 43 | v -0.126193 0.126193 -0.126193 44 | v -0.126193 0.126193 0.126193 45 | v -0.126193 -0.126193 -0.126193 46 | v -0.126193 -0.126193 0.126193 47 | v -0.126193 -0.083240 0.371962 48 | v -0.126193 0.083240 0.371962 49 | v -0.072635 -0.099014 0.622851 50 | v -0.095658 0.173416 0.604848 51 | v -0.450367 -0.036624 0.608745 52 | v -0.450367 0.083251 0.608745 53 | v -0.099626 0.169929 0.551847 54 | v -0.100987 -0.167685 0.551676 55 | v -0.431990 0.083209 0.538414 56 | v -0.431990 -0.083240 0.538414 57 | v -0.127480 -0.084779 0.486781 58 | v -0.414873 -0.083240 0.487081 59 | v -0.127532 0.084811 0.486723 60 | v -0.414873 0.083240 0.487081 61 | v -0.175413 0.083240 0.371962 62 | v -0.117468 0.039116 0.622763 63 | v -0.117485 -0.037224 0.622764 64 | v -0.175413 -0.083240 0.371962 65 | v -0.175521 -0.084431 0.537027 66 | v -0.177127 0.085744 0.537040 67 | v -0.173570 -0.080307 0.488848 68 | v -0.173506 0.080296 0.488910 69 | v -0.133616 -0.142871 0.604835 70 | v -0.178537 0.084174 0.604768 71 | v -0.072577 0.100842 0.622667 72 | v -0.194789 -0.037439 0.604883 73 | v -0.072605 -0.099013 0.576465 74 | v -0.072605 0.100853 0.576465 75 | v -0.117478 0.039091 0.576465 76 | v -0.117478 -0.037251 0.576465 77 | v -0.309591 -0.083362 0.538497 78 | v -0.309593 0.083226 0.538414 79 | v -0.309593 -0.083240 0.487081 80 | v -0.309593 0.083240 0.487081 81 | v -0.312497 0.083791 0.608768 82 | v -0.312499 -0.036504 0.608665 83 | v -0.417499 -0.124548 0.984481 84 | v -0.417515 0.082219 0.984493 85 | v -0.389121 -0.124548 0.984924 86 | v -0.471796 -0.037171 0.697587 87 | v -0.489879 -0.125019 0.787013 88 | v -0.489973 0.082211 0.787488 89 | v -0.471754 0.082224 0.697111 90 | v -0.386174 0.082734 0.789073 91 | v -0.369237 0.082709 0.713454 92 | v -0.386162 -0.124093 0.789559 93 | v -0.369197 -0.038121 0.712988 94 | v -0.491039 -0.124553 0.860510 95 | v -0.490986 0.082214 0.860510 96 | v -0.387186 0.082738 0.862095 97 | v -0.387239 -0.124553 0.862104 98 | v -0.389147 0.082742 0.984914 99 | v -0.490905 0.154364 0.787464 100 | v -0.491548 -0.124552 0.893756 101 | v -0.387106 0.154888 0.789049 102 | v -0.418552 0.224260 0.984463 103 | v -0.390157 0.224723 0.984865 104 | v -0.492165 0.294940 0.860484 105 | v -0.388365 0.295463 0.862069 106 | v -0.387722 0.082739 0.895341 107 | v -0.387749 -0.124552 0.895350 108 | v -0.491532 0.082215 0.893743 109 | v -0.492636 0.294918 0.893725 110 | v -0.388849 0.295488 0.895333 111 | v 0.126193 0.126193 -0.126193 112 | v 0.126193 0.126193 0.126193 113 | v 0.126193 -0.126193 -0.126193 114 | v 0.126193 -0.126193 0.126193 115 | v 0.126193 -0.083240 0.371962 116 | v 0.126193 0.083240 0.371962 117 | v 0.072635 -0.099014 0.622851 118 | v 0.095658 0.173416 0.604848 119 | v 0.450367 -0.036624 0.608745 120 | v 0.450367 0.083251 0.608745 121 | v 0.000000 -0.126193 -0.126193 122 | v 0.000000 -0.126193 0.126193 123 | v 0.000000 0.126193 -0.126193 124 | v 0.000000 0.126193 0.126193 125 | v 0.000000 -0.083240 0.371962 126 | v 0.000000 0.083240 0.371962 127 | v 0.000000 -0.194564 0.604526 128 | v -0.000000 0.198825 0.604963 129 | v 0.099626 0.169929 0.551847 130 | v 0.100987 -0.167685 0.551676 131 | v 0.431990 0.083209 0.538414 132 | v 0.431990 -0.083240 0.538414 133 | v 0.000000 -0.200860 0.553661 134 | v -0.000000 0.202025 0.553459 135 | v 0.127480 -0.084779 0.486781 136 | v 0.414873 -0.083240 0.487081 137 | v 0.127532 0.084811 0.486723 138 | v 0.414873 0.083240 0.487081 139 | v 0.000000 -0.084560 0.485030 140 | v 0.000000 0.084512 0.485052 141 | v 0.175413 0.083240 0.371962 142 | v 0.117468 0.039116 0.622763 143 | v 0.117485 -0.037224 0.622764 144 | v 0.175413 -0.083240 0.371962 145 | v 0.175521 -0.084431 0.537027 146 | v 0.177127 0.085744 0.537040 147 | v 0.173570 -0.080307 0.488848 148 | v 0.173506 0.080296 0.488910 149 | v 0.133616 -0.142871 0.604835 150 | v 0.000000 -0.122648 0.622936 151 | v 0.178537 0.084174 0.604768 152 | v 0.072577 0.100842 0.622667 153 | v 0.194789 -0.037439 0.604883 154 | v -0.000000 0.124407 0.622607 155 | v 0.072605 -0.099013 0.576465 156 | v 0.072605 0.100853 0.576465 157 | v 0.000000 -0.122604 0.576465 158 | v 0.000000 0.124444 0.576465 159 | v 0.117478 0.039091 0.576465 160 | v 0.117478 -0.037251 0.576465 161 | v 0.309591 -0.083362 0.538497 162 | v 0.309593 0.083226 0.538414 163 | v 0.309593 -0.083240 0.487081 164 | v 0.309593 0.083240 0.487081 165 | v 0.312497 0.083791 0.608768 166 | v 0.312499 -0.036504 0.608665 167 | v 0.417499 -0.124548 0.984481 168 | v 0.417515 0.082219 0.984493 169 | v 0.389121 -0.124548 0.984924 170 | v 0.471796 -0.037171 0.697587 171 | v 0.489879 -0.125019 0.787013 172 | v 0.489973 0.082211 0.787488 173 | v 0.471754 0.082224 0.697111 174 | v 0.386174 0.082734 0.789073 175 | v 0.369237 0.082709 0.713454 176 | v 0.386162 -0.124093 0.789559 177 | v 0.369197 -0.038121 0.712988 178 | v 0.491039 -0.124553 0.860510 179 | v 0.490986 0.082214 0.860510 180 | v 0.387186 0.082738 0.862095 181 | v 0.387239 -0.124553 0.862104 182 | v 0.389147 0.082742 0.984914 183 | v 0.490905 0.154364 0.787464 184 | v 0.491548 -0.124552 0.893756 185 | v 0.387106 0.154888 0.789049 186 | v 0.418552 0.224260 0.984463 187 | v 0.390157 0.224723 0.984865 188 | v 0.492165 0.294940 0.860484 189 | v 0.388365 0.295463 0.862069 190 | v 0.387722 0.082739 0.895341 191 | v 0.387749 -0.124552 0.895350 192 | v 0.491532 0.082215 0.893743 193 | v 0.492636 0.294918 0.893725 194 | v 0.388849 0.295488 0.895333 195 | vt 0.106151 0.902035 196 | vt 0.128224 0.902035 197 | vt 0.128224 0.913071 198 | vt 0.106151 0.913071 199 | vt 0.128224 0.935143 200 | vt 0.106151 0.935143 201 | vt 0.321678 0.922000 202 | vt 0.332714 0.922000 203 | vt 0.332714 0.944072 204 | vt 0.321678 0.944072 205 | vt 0.354786 0.944072 206 | vt 0.354786 0.922000 207 | vt 0.354786 0.922000 208 | vt 0.354786 0.944072 209 | vt 0.354786 0.955108 210 | vt 0.354786 0.955108 211 | vt 0.354786 0.944072 212 | vt 0.354786 0.944072 213 | vt 0.354786 0.955108 214 | vt 0.354786 0.944072 215 | vt 0.354786 0.922000 216 | vt 0.354786 0.922000 217 | vt 0.354786 0.944072 218 | vt 0.354786 0.944072 219 | vt 0.354786 0.922000 220 | vt 0.354786 0.922000 221 | vt 0.354786 0.944072 222 | vt 0.354786 0.922000 223 | vt 0.354786 0.922000 224 | vt 0.354786 0.922000 225 | vt 0.354786 0.910963 226 | vt 0.354786 0.910963 227 | vt 0.354786 0.910963 228 | vt 0.354786 0.910963 229 | vt 0.128224 0.946180 230 | vt 0.106151 0.946180 231 | vt 0.354786 0.922000 232 | vt 0.354786 0.910963 233 | vt 0.354786 0.922000 234 | vt 0.354786 0.922000 235 | vt 0.354786 0.944072 236 | vt 0.354786 0.955108 237 | vt 0.354786 0.944072 238 | vt 0.354786 0.944072 239 | vt 0.354786 0.944072 240 | vt 0.354786 0.944072 241 | vt 0.354786 0.944072 242 | vt 0.354786 0.944072 243 | vt 0.354786 0.922000 244 | vt 0.354786 0.922000 245 | vt 0.354786 0.944072 246 | vt 0.354786 0.944072 247 | vt 0.354786 0.922000 248 | vt 0.354786 0.922000 249 | vt 0.354786 0.922000 250 | vt 0.354786 0.922000 251 | vt 0.354786 0.922000 252 | vt 0.354786 0.922000 253 | vt 0.354786 0.922000 254 | vt 0.365822 0.922000 255 | vt 0.365822 0.922000 256 | vt 0.354786 0.944072 257 | vt 0.354786 0.944072 258 | vt 0.365822 0.944072 259 | vt 0.365822 0.944072 260 | vt 0.120411 0.908607 261 | vt 0.131447 0.908607 262 | vt 0.131447 0.930679 263 | vt 0.120411 0.930679 264 | vt 0.120411 0.930679 265 | vt 0.120411 0.908607 266 | vt 0.365822 0.922000 267 | vt 0.354786 0.944072 268 | vt 0.354786 0.944072 269 | vt 0.365822 0.944072 270 | vt 0.354786 0.922000 271 | vt 0.354786 0.944072 272 | vt 0.354786 0.922000 273 | vt 0.354786 0.944072 274 | vt 0.354786 0.922000 275 | vt 0.354786 0.922000 276 | vt 0.354786 0.922000 277 | vt 0.354786 0.922000 278 | vt 0.354786 0.944072 279 | vt 0.354786 0.944072 280 | vt 0.354786 0.944072 281 | vt 0.354786 0.944072 282 | vt 0.354786 0.922000 283 | vt 0.354786 0.922000 284 | vt 0.354786 0.944072 285 | vt 0.354786 0.944072 286 | vt 0.354786 0.944072 287 | vt 0.354786 0.944072 288 | vt 0.354786 0.944072 289 | vt 0.354786 0.944072 290 | vt 0.354786 0.944072 291 | vt 0.354786 0.944072 292 | vt 0.354786 0.944072 293 | vt 0.354786 0.944072 294 | vt 0.106151 0.913071 295 | vt 0.128224 0.913071 296 | vt 0.106151 0.935143 297 | vt 0.128224 0.935143 298 | vt 0.332714 0.944072 299 | vt 0.332714 0.922000 300 | vt 0.354786 0.944072 301 | vt 0.354786 0.944072 302 | vt 0.354786 0.922000 303 | vt 0.354786 0.922000 304 | vt 0.354786 0.944072 305 | vt 0.354786 0.944072 306 | vt 0.354786 0.944072 307 | vt 0.354786 0.944072 308 | vt 0.354786 0.922000 309 | vt 0.354786 0.922000 310 | vt 0.354786 0.944072 311 | vt 0.354786 0.944072 312 | vt 0.354786 0.922000 313 | vt 0.354786 0.922000 314 | vt 0.354786 0.922000 315 | vt 0.354786 0.922000 316 | vt 0.354786 0.922000 317 | vt 0.354786 0.922000 318 | vt 0.354786 0.922000 319 | vt 0.354786 0.922000 320 | vt 0.354786 0.922000 321 | vt 0.354786 0.944072 322 | vt 0.354786 0.955108 323 | vt 0.354786 0.944072 324 | vt 0.354786 0.944072 325 | vt 0.354786 0.944072 326 | vt 0.354786 0.944072 327 | vt 0.354786 0.944072 328 | vt 0.354786 0.944072 329 | vt 0.354786 0.922000 330 | vt 0.354786 0.922000 331 | vt 0.354786 0.944072 332 | vt 0.354786 0.944072 333 | vt 0.354786 0.922000 334 | vt 0.354786 0.922000 335 | vt 0.354786 0.922000 336 | vt 0.354786 0.922000 337 | vt 0.354786 0.922000 338 | vt 0.354786 0.922000 339 | vt 0.354786 0.944072 340 | vt 0.354786 0.944072 341 | vt 0.120411 0.908607 342 | vt 0.120411 0.930679 343 | vt 0.120411 0.908607 344 | vt 0.120411 0.930679 345 | vt 0.354786 0.944072 346 | vt 0.354786 0.944072 347 | vt 0.354786 0.922000 348 | vt 0.354786 0.944072 349 | vt 0.354786 0.922000 350 | vt 0.354786 0.944072 351 | vt 0.354786 0.922000 352 | vt 0.354786 0.922000 353 | vt 0.354786 0.922000 354 | vt 0.354786 0.922000 355 | vt 0.354786 0.944072 356 | vt 0.354786 0.944072 357 | vt 0.354786 0.944072 358 | vt 0.354786 0.944072 359 | vt 0.354786 0.922000 360 | vt 0.354786 0.922000 361 | vt 0.354786 0.944072 362 | vt 0.354786 0.944072 363 | vt 0.354786 0.944072 364 | vt 0.354786 0.944072 365 | vt 0.354786 0.944072 366 | vt 0.354786 0.944072 367 | vt 0.354786 0.944072 368 | vt 0.354786 0.944072 369 | vt 0.354786 0.944072 370 | vt 0.354786 0.944072 371 | vn 0.0000 -1.0000 0.0000 372 | vn -1.0000 0.0000 0.0000 373 | vn 0.0000 0.0000 -1.0000 374 | vn 0.0000 0.9851 0.1722 375 | vn -0.2564 0.9655 -0.0443 376 | vn -0.9675 0.0000 -0.2528 377 | vn -0.7748 0.0000 0.6322 378 | vn -0.0000 -0.8326 0.5539 379 | vn -0.3120 -0.9429 0.1167 380 | vn 0.0000 -0.9851 0.1722 381 | vn 0.0000 1.0000 0.0000 382 | vn -0.0110 -0.5082 -0.8612 383 | vn -0.0005 -1.0000 -0.0013 384 | vn -0.9486 0.0000 -0.3163 385 | vn -0.0090 0.6095 -0.7928 386 | vn -0.0021 1.0000 -0.0046 387 | vn 0.0011 0.9999 -0.0125 388 | vn 0.0213 0.9995 0.0248 389 | vn 0.0212 -0.9995 0.0247 390 | vn 0.0008 -0.9999 -0.0125 391 | vn -0.0001 1.0000 0.0004 392 | vn -0.0982 0.6248 -0.7746 393 | vn -0.0954 -0.9951 0.0265 394 | vn -0.7344 0.6787 0.0048 395 | vn -0.5746 -0.6104 -0.5452 396 | vn -0.8293 -0.4810 -0.2845 397 | vn 0.8092 0.5876 0.0002 398 | vn 0.0000 0.9997 0.0252 399 | vn -0.0919 -0.2545 0.9627 400 | vn -0.1888 0.1599 0.9689 401 | vn -0.2093 -0.1325 0.9688 402 | vn -0.0656 0.2243 0.9723 403 | vn 0.0216 0.9998 0.0003 404 | vn -0.2446 0.0207 0.9694 405 | vn 0.0000 0.0000 1.0000 406 | vn 0.3092 0.9510 0.0006 407 | vn 1.0000 -0.0001 -0.0000 408 | vn 0.3089 -0.9511 -0.0006 409 | vn 0.8089 -0.5880 -0.0002 410 | vn -0.0107 0.9999 0.0069 411 | vn -0.0089 -0.9963 -0.0856 412 | vn -0.6511 0.0000 -0.7590 413 | vn 0.0052 -0.8265 0.5628 414 | vn 0.0309 -0.0020 0.9995 415 | vn -0.0152 -0.0000 0.9999 416 | vn 0.9999 0.0000 0.0156 417 | vn -0.0027 -0.9999 -0.0123 418 | vn 0.0242 -0.6676 -0.7442 419 | vn 0.8789 -0.0010 0.4770 420 | vn 0.9761 0.0002 0.2175 421 | vn -0.0049 0.9999 0.0089 422 | vn -0.0048 1.0000 -0.0011 423 | vn -0.9720 -0.0003 -0.2349 424 | vn -0.9802 -0.0002 -0.1980 425 | vn 0.0045 -1.0000 0.0001 426 | vn 0.9999 -0.0001 0.0144 427 | vn -0.9999 -0.0001 -0.0148 428 | vn 0.0153 -0.0000 -0.9999 429 | vn 0.0112 0.4610 -0.8873 430 | vn -0.0156 0.7836 0.6210 431 | vn -0.7756 -0.0046 0.6312 432 | vn 0.9999 0.0054 0.0153 433 | vn 0.9999 0.0074 0.0084 434 | vn -0.0145 0.0002 0.9999 435 | vn -0.9999 -0.0074 -0.0084 436 | vn 0.0153 -0.0001 -0.9999 437 | vn 0.9998 0.0060 0.0176 438 | vn -0.9999 -0.0054 -0.0153 439 | vn -0.0053 1.0000 -0.0001 440 | vn 0.9999 -0.0002 0.0157 441 | vn -0.9999 0.0002 -0.0159 442 | vn 1.0000 0.0000 0.0000 443 | vn 0.2564 0.9655 -0.0443 444 | vn 0.9675 0.0000 -0.2528 445 | vn 0.7748 0.0000 0.6322 446 | vn 0.3524 -0.9121 0.2095 447 | vn 0.1965 -0.6425 -0.7406 448 | vn 0.0005 -1.0000 -0.0013 449 | vn 0.9486 0.0000 -0.3163 450 | vn 0.1926 0.6353 -0.7479 451 | vn 0.0021 1.0000 -0.0046 452 | vn -0.0011 0.9999 -0.0125 453 | vn -0.0213 0.9995 0.0248 454 | vn -0.0212 -0.9995 0.0247 455 | vn -0.0008 -0.9999 -0.0125 456 | vn 0.0001 1.0000 0.0004 457 | vn 0.7177 0.6838 -0.1314 458 | vn 0.0000 -0.9997 0.0251 459 | vn 0.7344 0.6787 0.0048 460 | vn 0.1001 -0.9910 -0.0889 461 | vn 0.8605 -0.4993 0.1014 462 | vn -0.8092 0.5876 0.0002 463 | vn 0.0964 0.9950 0.0266 464 | vn 0.0919 -0.2545 0.9627 465 | vn 0.1888 0.1599 0.9689 466 | vn 0.2093 -0.1325 0.9688 467 | vn 0.0656 0.2243 0.9723 468 | vn -0.0216 0.9998 0.0003 469 | vn 0.2446 0.0207 0.9694 470 | vn -0.3092 0.9510 0.0006 471 | vn -1.0000 -0.0001 -0.0000 472 | vn -0.3089 -0.9511 -0.0006 473 | vn -0.8089 -0.5880 -0.0002 474 | vn 0.0107 0.9999 0.0069 475 | vn -0.0216 -0.9998 -0.0024 476 | vn 0.6511 0.0000 -0.7590 477 | vn -0.0052 -0.8265 0.5628 478 | vn -0.0309 -0.0020 0.9995 479 | vn 0.0152 -0.0000 0.9999 480 | vn -0.9999 0.0000 0.0156 481 | vn 0.0027 -0.9999 -0.0123 482 | vn -0.0235 -0.7156 -0.6982 483 | vn -0.8789 -0.0010 0.4770 484 | vn -0.9761 0.0002 0.2175 485 | vn 0.0049 0.9999 0.0089 486 | vn 0.0048 1.0000 -0.0011 487 | vn 0.9720 -0.0003 -0.2349 488 | vn 0.9802 -0.0002 -0.1980 489 | vn -0.0045 -1.0000 0.0001 490 | vn -0.9999 -0.0001 0.0144 491 | vn 0.9999 -0.0001 -0.0148 492 | vn 0.0153 0.0000 0.9999 493 | vn -0.0112 0.4610 -0.8873 494 | vn 0.0156 0.7836 0.6210 495 | vn 0.7756 -0.0046 0.6312 496 | vn -0.9999 0.0054 0.0153 497 | vn -0.9999 0.0074 0.0084 498 | vn 0.0145 0.0002 0.9999 499 | vn 0.9999 -0.0074 -0.0084 500 | vn -0.0153 -0.0001 -0.9999 501 | vn -0.9998 0.0060 0.0176 502 | vn 0.9999 -0.0054 -0.0153 503 | vn 0.0053 1.0000 -0.0001 504 | vn -0.9999 -0.0002 0.0157 505 | vn 0.9999 0.0002 -0.0159 506 | vn -0.1037 -0.6728 -0.7325 507 | vn 0.0089 -0.9963 -0.0856 508 | vn 0.0201 0.9933 -0.1139 509 | vn 0.0000 0.9999 -0.0137 510 | vn 0.7269 -0.6740 -0.1316 511 | vn 0.5746 -0.6104 -0.5452 512 | vn 0.0972 -0.9952 -0.0144 513 | vn 0.0982 0.6248 -0.7746 514 | vn 0.0101 0.5031 -0.8642 515 | vn 0.0110 -0.5082 -0.8612 516 | vn 0.3120 -0.9429 0.1167 517 | vn 0.3070 0.9499 0.0590 518 | vn 0.0965 -0.7197 -0.6875 519 | vn 0.0216 -0.9998 -0.0024 520 | vn -0.0201 0.9933 -0.1139 521 | vn -0.0984 0.9950 -0.0148 522 | vn -0.7454 -0.6281 0.2233 523 | vn -0.1001 -0.9910 -0.0889 524 | vn 0.0000 -0.9999 -0.0134 525 | vn -0.7177 0.6838 -0.1314 526 | vn -0.1465 0.4977 -0.8549 527 | vn -0.1965 -0.6425 -0.7406 528 | vn -0.3524 -0.9121 0.2095 529 | vn -0.3070 0.9499 0.0590 530 | usemtl generic 531 | s off 532 | f 79/1/1 80/2/1 4/3/1 3/4/1 533 | f 3/4/2 4/3/2 2/5/2 1/6/2 534 | f 79/7/3 3/8/3 1/9/3 81/10/3 535 | f 2/11/2 4/12/2 5/13/2 6/14/2 536 | f 82/15/4 2/11/4 6/14/4 84/16/4 537 | f 11/17/5 8/18/5 86/19/5 538 | f 13/20/6 14/21/6 9/22/6 10/23/6 539 | f 66/24/7 58/25/7 41/26/7 42/27/7 540 | f 35/28/8 40/29/8 9/22/8 14/21/8 541 | f 12/30/9 91/31/9 85/32/9 542 | f 4/12/10 80/33/10 83/34/10 5/13/10 543 | f 1/6/11 2/5/11 82/35/11 81/36/11 544 | f 15/37/12 97/38/12 91/31/12 545 | f 37/39/13 35/28/13 14/21/13 16/40/13 546 | f 18/41/14 16/40/14 14/21/14 13/20/14 547 | f 98/42/15 17/43/15 11/17/15 548 | f 39/44/16 36/45/16 13/20/16 10/23/16 549 | f 84/16/17 6/14/17 17/43/17 98/42/17 550 | f 26/46/18 19/47/18 38/48/18 551 | f 22/49/19 25/50/19 37/39/19 552 | f 5/13/20 83/34/20 97/38/20 15/37/20 553 | f 36/45/21 38/48/21 18/41/21 13/20/21 554 | f 11/17/22 17/43/22 26/46/22 555 | f 15/37/23 25/50/23 22/49/23 556 | f 8/18/24 11/17/24 24/51/24 28/52/24 557 | f 15/37/25 12/30/25 23/53/25 558 | f 6/14/3 5/13/3 22/49/3 19/47/3 559 | f 12/30/26 27/54/26 30/55/26 560 | f 21/56/27 7/57/27 31/58/27 34/59/27 561 | f 6/14/28 19/47/28 26/46/28 562 | f 108/60/29 7/57/29 27/54/29 85/61/29 563 | f 20/62/30 29/63/30 8/18/30 28/52/30 564 | f 7/57/31 21/56/31 30/55/31 27/54/31 565 | f 29/63/32 112/64/32 86/65/32 8/18/32 566 | f 26/46/33 38/48/33 36/45/33 567 | f 21/56/34 20/62/34 28/52/34 30/55/34 568 | f 31/66/35 115/67/35 116/68/35 32/69/35 569 | f 31/66/35 32/69/35 33/70/35 34/71/35 570 | f 7/57/36 108/60/36 115/72/36 31/58/36 571 | f 20/62/37 21/56/37 34/59/37 33/73/37 572 | f 112/64/38 29/63/38 32/74/38 116/75/38 573 | f 29/63/39 20/62/39 33/73/39 32/74/39 574 | f 28/52/40 24/51/40 36/45/40 39/44/40 575 | f 25/50/41 23/53/41 35/28/41 576 | f 19/47/42 22/49/42 37/39/42 38/48/42 577 | f 23/53/43 30/55/43 40/29/43 35/28/43 578 | f 30/55/44 28/52/44 39/44/44 40/29/44 579 | f 18/41/3 38/48/3 37/39/3 16/40/3 580 | f 43/76/45 56/77/45 42/27/45 41/26/45 581 | f 65/78/46 64/79/46 56/77/46 43/76/46 582 | f 58/25/1 65/78/1 43/76/1 41/26/1 583 | f 9/22/47 40/29/47 51/80/47 44/81/47 584 | f 51/80/48 50/82/48 45/83/48 585 | f 40/29/49 39/44/49 49/84/49 51/80/49 586 | f 51/80/50 49/84/50 48/85/50 50/82/50 587 | f 39/44/51 10/23/51 47/86/51 49/84/51 588 | f 49/84/52 47/86/52 46/87/52 48/85/52 589 | f 10/23/53 9/22/53 44/81/53 47/86/53 590 | f 47/86/54 44/81/54 45/83/54 46/87/54 591 | f 45/83/55 50/82/55 55/88/55 52/89/55 592 | f 50/82/56 48/85/56 54/90/56 55/88/56 593 | f 46/87/57 45/83/57 52/89/57 53/91/57 594 | f 53/91/58 62/92/58 63/93/58 54/90/58 595 | f 59/94/59 57/95/59 62/92/59 63/93/59 596 | f 68/96/60 67/97/60 60/98/60 61/99/60 597 | f 66/24/61 42/27/61 60/98/61 67/97/61 598 | f 64/79/62 54/90/62 63/93/62 68/96/62 599 | f 54/90/63 48/85/63 59/94/63 63/93/63 600 | f 42/27/64 56/77/64 61/99/64 60/98/64 601 | f 46/87/65 53/91/65 62/92/65 57/95/65 602 | f 48/85/66 46/87/66 57/95/66 59/94/66 603 | f 56/77/67 64/79/67 68/96/67 61/99/67 604 | f 53/91/68 66/24/68 67/97/68 62/92/68 605 | f 63/93/69 62/92/69 67/97/69 68/96/69 606 | f 52/89/1 55/88/1 65/78/1 58/25/1 607 | f 55/88/70 54/90/70 64/79/70 65/78/70 608 | f 53/91/71 52/89/71 58/25/71 66/24/71 609 | f 79/1/1 71/100/1 72/101/1 80/2/1 610 | f 71/100/72 69/102/72 70/103/72 72/101/72 611 | f 79/7/3 81/10/3 69/104/3 71/105/3 612 | f 70/106/72 74/107/72 73/108/72 72/109/72 613 | f 82/15/4 84/16/4 74/107/4 70/106/4 614 | f 86/19/73 76/110/73 87/111/73 615 | f 89/112/74 78/113/74 77/114/74 90/115/74 616 | f 150/116/75 126/117/75 125/118/75 142/119/75 617 | f 119/120/8 90/115/8 77/114/8 124/121/8 618 | f 88/122/76 107/123/76 85/32/76 619 | f 72/109/10 73/108/10 83/34/10 80/33/10 620 | f 69/102/11 81/36/11 82/35/11 70/103/11 621 | f 93/124/77 88/122/77 91/31/77 622 | f 121/125/78 94/126/78 90/115/78 119/120/78 623 | f 96/127/79 89/112/79 90/115/79 94/126/79 624 | f 92/128/80 87/111/80 95/129/80 625 | f 123/130/81 78/113/81 89/112/81 120/131/81 626 | f 84/16/82 98/42/82 95/129/82 74/107/82 627 | f 106/132/83 122/133/83 99/134/83 628 | f 102/135/84 121/125/84 105/136/84 629 | f 73/108/85 93/124/85 97/38/85 83/34/85 630 | f 120/131/86 89/112/86 96/127/86 122/133/86 631 | f 87/111/87 104/137/87 106/132/87 632 | f 73/108/88 102/135/88 105/136/88 633 | f 76/110/89 109/138/89 104/137/89 87/111/89 634 | f 93/124/90 105/136/90 103/139/90 635 | f 74/107/3 99/134/3 102/135/3 73/108/3 636 | f 103/139/91 111/140/91 107/123/91 637 | f 101/141/92 118/142/92 113/143/92 75/144/92 638 | f 95/129/93 106/132/93 99/134/93 639 | f 108/60/94 85/61/94 107/123/94 75/144/94 640 | f 100/145/95 109/138/95 76/110/95 110/146/95 641 | f 75/144/96 107/123/96 111/140/96 101/141/96 642 | f 110/146/97 76/110/97 86/65/97 112/64/97 643 | f 120/131/98 122/133/98 106/132/98 644 | f 101/141/99 111/140/99 109/138/99 100/145/99 645 | f 113/147/35 114/148/35 116/68/35 115/67/35 646 | f 113/147/35 118/149/35 117/150/35 114/148/35 647 | f 75/144/100 113/143/100 115/72/100 108/60/100 648 | f 100/145/101 117/151/101 118/142/101 101/141/101 649 | f 112/64/102 116/75/102 114/152/102 110/146/102 650 | f 110/146/103 114/152/103 117/151/103 100/145/103 651 | f 109/138/104 123/130/104 120/131/104 104/137/104 652 | f 105/136/105 121/125/105 119/120/105 653 | f 99/134/106 122/133/106 121/125/106 102/135/106 654 | f 103/139/107 119/120/107 124/121/107 111/140/107 655 | f 111/140/108 124/121/108 123/130/108 109/138/108 656 | f 96/127/3 94/126/3 121/125/3 122/133/3 657 | f 127/153/109 125/118/109 126/117/109 140/154/109 658 | f 149/155/110 127/153/110 140/154/110 148/156/110 659 | f 142/119/1 125/118/1 127/153/1 149/155/1 660 | f 77/114/111 128/157/111 135/158/111 124/121/111 661 | f 128/157/112 129/159/112 134/160/112 662 | f 124/121/113 135/158/113 133/161/113 123/130/113 663 | f 135/158/114 134/160/114 132/162/114 133/161/114 664 | f 123/130/115 133/161/115 131/163/115 78/113/115 665 | f 133/161/116 132/162/116 130/164/116 131/163/116 666 | f 78/113/117 131/163/117 128/157/117 77/114/117 667 | f 131/163/118 130/164/118 129/159/118 128/157/118 668 | f 129/159/119 136/165/119 139/166/119 134/160/119 669 | f 134/160/120 139/166/120 138/167/120 132/162/120 670 | f 130/164/121 137/168/121 136/165/121 129/159/121 671 | f 137/168/122 146/169/122 147/170/122 138/167/122 672 | f 143/171/123 147/170/123 146/169/123 141/172/123 673 | f 152/173/124 145/174/124 144/175/124 151/176/124 674 | f 150/116/125 151/176/125 144/175/125 126/117/125 675 | f 148/156/126 152/173/126 147/170/126 138/167/126 676 | f 138/167/127 147/170/127 143/171/127 132/162/127 677 | f 126/117/128 144/175/128 145/174/128 140/154/128 678 | f 130/164/129 141/172/129 146/169/129 137/168/129 679 | f 132/162/130 143/171/130 141/172/130 130/164/130 680 | f 140/154/131 145/174/131 152/173/131 148/156/131 681 | f 137/168/132 146/169/132 151/176/132 150/116/132 682 | f 147/170/133 152/173/133 151/176/133 146/169/133 683 | f 136/165/1 142/119/1 149/155/1 139/166/1 684 | f 139/166/134 149/155/134 148/156/134 138/167/134 685 | f 137/168/135 150/116/135 142/119/135 136/165/135 686 | f 135/158/136 128/157/136 134/160/136 687 | f 103/139/137 105/136/137 119/120/137 688 | f 104/137/138 120/131/138 106/132/138 689 | f 74/107/139 95/129/139 99/134/139 690 | f 88/122/140 103/139/140 107/123/140 691 | f 88/122/141 93/124/141 103/139/141 692 | f 93/124/142 73/108/142 105/136/142 693 | f 95/129/143 87/111/143 106/132/143 694 | f 98/42/144 92/128/144 95/129/144 695 | f 97/38/145 93/124/145 91/31/145 696 | f 91/31/146 88/122/146 85/32/146 697 | f 92/128/147 86/19/147 87/111/147 698 | f 44/81/148 51/80/148 45/83/148 699 | f 37/39/149 25/50/149 35/28/149 700 | f 24/51/150 26/46/150 36/45/150 701 | f 17/43/151 6/14/151 26/46/151 702 | f 23/53/152 12/30/152 30/55/152 703 | f 25/50/153 15/37/153 23/53/153 704 | f 5/13/154 15/37/154 22/49/154 705 | f 24/51/155 11/17/155 26/46/155 706 | f 92/128/156 98/42/156 11/17/156 707 | f 12/30/157 15/37/157 91/31/157 708 | f 27/54/158 12/30/158 85/32/158 709 | f 92/128/159 11/17/159 86/19/159 710 | """ 711 | -------------------------------------------------------------------------------- /benchmarks/src/SlowDecodeBenchmark.elm: -------------------------------------------------------------------------------- 1 | module SlowDecodeBenchmark exposing (main) 2 | 3 | import Benchmark 4 | import Benchmark.Runner exposing (BenchmarkProgram, program) 5 | import Decode 6 | import Length 7 | import Obj.Decode 8 | 9 | 10 | main : BenchmarkProgram 11 | main = 12 | program <| 13 | Benchmark.compare "decode" 14 | "old texturedFaces" 15 | (\_ -> Decode.decodeString Length.meters Decode.texturedFaces obj |> Result.map (always ())) 16 | "texturedFaces" 17 | (\_ -> Obj.Decode.decodeString Length.meters Obj.Decode.texturedFaces obj |> Result.map (always ())) 18 | 19 | 20 | obj : String 21 | obj = 22 | """# Blender v2.90 (sub 0) OBJ File: 'pod.blend' 23 | # www.blender.org 24 | mtllib swivel.mtl 25 | o swivel_Cube 26 | v -0.126193 0.126193 -0.126193 27 | v -0.126193 0.126193 0.126193 28 | v -0.126193 -0.126193 -0.126193 29 | v -0.126193 -0.126193 0.126193 30 | v -0.126193 -0.083240 0.371962 31 | v -0.126193 0.083240 0.371962 32 | v -0.072635 -0.099014 0.622851 33 | v -0.095658 0.173416 0.604848 34 | v -0.450367 -0.036624 0.608745 35 | v -0.450367 0.083251 0.608745 36 | v -0.099626 0.169929 0.551847 37 | v -0.100987 -0.167685 0.551676 38 | v -0.431990 0.083209 0.538414 39 | v -0.431990 -0.083240 0.538414 40 | v -0.127480 -0.084779 0.486781 41 | v -0.414873 -0.083240 0.487081 42 | v -0.127532 0.084811 0.486723 43 | v -0.414873 0.083240 0.487081 44 | v -0.175413 0.083240 0.371962 45 | v -0.117468 0.039116 0.622763 46 | v -0.117485 -0.037224 0.622764 47 | v -0.175413 -0.083240 0.371962 48 | v -0.175521 -0.084431 0.537027 49 | v -0.177127 0.085744 0.537040 50 | v -0.173570 -0.080307 0.488848 51 | v -0.173506 0.080296 0.488910 52 | v -0.133616 -0.142871 0.604835 53 | v -0.178537 0.084174 0.604768 54 | v -0.072577 0.100842 0.622667 55 | v -0.194789 -0.037439 0.604883 56 | v -0.072605 -0.099013 0.576465 57 | v -0.072605 0.100853 0.576465 58 | v -0.117478 0.039091 0.576465 59 | v -0.117478 -0.037251 0.576465 60 | v -0.309591 -0.083362 0.538497 61 | v -0.309593 0.083226 0.538414 62 | v -0.309593 -0.083240 0.487081 63 | v -0.309593 0.083240 0.487081 64 | v -0.312497 0.083791 0.608768 65 | v -0.312499 -0.036504 0.608665 66 | v -0.417499 -0.124548 0.984481 67 | v -0.417515 0.082219 0.984493 68 | v -0.389121 -0.124548 0.984924 69 | v -0.471796 -0.037171 0.697587 70 | v -0.489879 -0.125019 0.787013 71 | v -0.489973 0.082211 0.787488 72 | v -0.471754 0.082224 0.697111 73 | v -0.386174 0.082734 0.789073 74 | v -0.369237 0.082709 0.713454 75 | v -0.386162 -0.124093 0.789559 76 | v -0.369197 -0.038121 0.712988 77 | v -0.491039 -0.124553 0.860510 78 | v -0.490986 0.082214 0.860510 79 | v -0.387186 0.082738 0.862095 80 | v -0.387239 -0.124553 0.862104 81 | v -0.389147 0.082742 0.984914 82 | v -0.490905 0.154364 0.787464 83 | v -0.491548 -0.124552 0.893756 84 | v -0.387106 0.154888 0.789049 85 | v -0.418552 0.224260 0.984463 86 | v -0.390157 0.224723 0.984865 87 | v -0.492165 0.294940 0.860484 88 | v -0.388365 0.295463 0.862069 89 | v -0.387722 0.082739 0.895341 90 | v -0.387749 -0.124552 0.895350 91 | v -0.491532 0.082215 0.893743 92 | v -0.492636 0.294918 0.893725 93 | v -0.388849 0.295488 0.895333 94 | v 0.126193 0.126193 -0.126193 95 | v 0.126193 0.126193 0.126193 96 | v 0.126193 -0.126193 -0.126193 97 | v 0.126193 -0.126193 0.126193 98 | v 0.126193 -0.083240 0.371962 99 | v 0.126193 0.083240 0.371962 100 | v 0.072635 -0.099014 0.622851 101 | v 0.095658 0.173416 0.604848 102 | v 0.450367 -0.036624 0.608745 103 | v 0.450367 0.083251 0.608745 104 | v 0.000000 -0.126193 -0.126193 105 | v 0.000000 -0.126193 0.126193 106 | v 0.000000 0.126193 -0.126193 107 | v 0.000000 0.126193 0.126193 108 | v 0.000000 -0.083240 0.371962 109 | v 0.000000 0.083240 0.371962 110 | v 0.000000 -0.194564 0.604526 111 | v -0.000000 0.198825 0.604963 112 | v 0.099626 0.169929 0.551847 113 | v 0.100987 -0.167685 0.551676 114 | v 0.431990 0.083209 0.538414 115 | v 0.431990 -0.083240 0.538414 116 | v 0.000000 -0.200860 0.553661 117 | v -0.000000 0.202025 0.553459 118 | v 0.127480 -0.084779 0.486781 119 | v 0.414873 -0.083240 0.487081 120 | v 0.127532 0.084811 0.486723 121 | v 0.414873 0.083240 0.487081 122 | v 0.000000 -0.084560 0.485030 123 | v 0.000000 0.084512 0.485052 124 | v 0.175413 0.083240 0.371962 125 | v 0.117468 0.039116 0.622763 126 | v 0.117485 -0.037224 0.622764 127 | v 0.175413 -0.083240 0.371962 128 | v 0.175521 -0.084431 0.537027 129 | v 0.177127 0.085744 0.537040 130 | v 0.173570 -0.080307 0.488848 131 | v 0.173506 0.080296 0.488910 132 | v 0.133616 -0.142871 0.604835 133 | v 0.000000 -0.122648 0.622936 134 | v 0.178537 0.084174 0.604768 135 | v 0.072577 0.100842 0.622667 136 | v 0.194789 -0.037439 0.604883 137 | v -0.000000 0.124407 0.622607 138 | v 0.072605 -0.099013 0.576465 139 | v 0.072605 0.100853 0.576465 140 | v 0.000000 -0.122604 0.576465 141 | v 0.000000 0.124444 0.576465 142 | v 0.117478 0.039091 0.576465 143 | v 0.117478 -0.037251 0.576465 144 | v 0.309591 -0.083362 0.538497 145 | v 0.309593 0.083226 0.538414 146 | v 0.309593 -0.083240 0.487081 147 | v 0.309593 0.083240 0.487081 148 | v 0.312497 0.083791 0.608768 149 | v 0.312499 -0.036504 0.608665 150 | v 0.417499 -0.124548 0.984481 151 | v 0.417515 0.082219 0.984493 152 | v 0.389121 -0.124548 0.984924 153 | v 0.471796 -0.037171 0.697587 154 | v 0.489879 -0.125019 0.787013 155 | v 0.489973 0.082211 0.787488 156 | v 0.471754 0.082224 0.697111 157 | v 0.386174 0.082734 0.789073 158 | v 0.369237 0.082709 0.713454 159 | v 0.386162 -0.124093 0.789559 160 | v 0.369197 -0.038121 0.712988 161 | v 0.491039 -0.124553 0.860510 162 | v 0.490986 0.082214 0.860510 163 | v 0.387186 0.082738 0.862095 164 | v 0.387239 -0.124553 0.862104 165 | v 0.389147 0.082742 0.984914 166 | v 0.490905 0.154364 0.787464 167 | v 0.491548 -0.124552 0.893756 168 | v 0.387106 0.154888 0.789049 169 | v 0.418552 0.224260 0.984463 170 | v 0.390157 0.224723 0.984865 171 | v 0.492165 0.294940 0.860484 172 | v 0.388365 0.295463 0.862069 173 | v 0.387722 0.082739 0.895341 174 | v 0.387749 -0.124552 0.895350 175 | v 0.491532 0.082215 0.893743 176 | v 0.492636 0.294918 0.893725 177 | v 0.388849 0.295488 0.895333 178 | vt 0.106151 0.902035 179 | vt 0.128224 0.902035 180 | vt 0.128224 0.913071 181 | vt 0.106151 0.913071 182 | vt 0.128224 0.935143 183 | vt 0.106151 0.935143 184 | vt 0.321678 0.922000 185 | vt 0.332714 0.922000 186 | vt 0.332714 0.944072 187 | vt 0.321678 0.944072 188 | vt 0.354786 0.944072 189 | vt 0.354786 0.922000 190 | vt 0.354786 0.922000 191 | vt 0.354786 0.944072 192 | vt 0.354786 0.955108 193 | vt 0.354786 0.955108 194 | vt 0.354786 0.944072 195 | vt 0.354786 0.944072 196 | vt 0.354786 0.955108 197 | vt 0.354786 0.944072 198 | vt 0.354786 0.922000 199 | vt 0.354786 0.922000 200 | vt 0.354786 0.944072 201 | vt 0.354786 0.944072 202 | vt 0.354786 0.922000 203 | vt 0.354786 0.922000 204 | vt 0.354786 0.944072 205 | vt 0.354786 0.922000 206 | vt 0.354786 0.922000 207 | vt 0.354786 0.922000 208 | vt 0.354786 0.910963 209 | vt 0.354786 0.910963 210 | vt 0.354786 0.910963 211 | vt 0.354786 0.910963 212 | vt 0.128224 0.946180 213 | vt 0.106151 0.946180 214 | vt 0.354786 0.922000 215 | vt 0.354786 0.910963 216 | vt 0.354786 0.922000 217 | vt 0.354786 0.922000 218 | vt 0.354786 0.944072 219 | vt 0.354786 0.955108 220 | vt 0.354786 0.944072 221 | vt 0.354786 0.944072 222 | vt 0.354786 0.944072 223 | vt 0.354786 0.944072 224 | vt 0.354786 0.944072 225 | vt 0.354786 0.944072 226 | vt 0.354786 0.922000 227 | vt 0.354786 0.922000 228 | vt 0.354786 0.944072 229 | vt 0.354786 0.944072 230 | vt 0.354786 0.922000 231 | vt 0.354786 0.922000 232 | vt 0.354786 0.922000 233 | vt 0.354786 0.922000 234 | vt 0.354786 0.922000 235 | vt 0.354786 0.922000 236 | vt 0.354786 0.922000 237 | vt 0.365822 0.922000 238 | vt 0.365822 0.922000 239 | vt 0.354786 0.944072 240 | vt 0.354786 0.944072 241 | vt 0.365822 0.944072 242 | vt 0.365822 0.944072 243 | vt 0.120411 0.908607 244 | vt 0.131447 0.908607 245 | vt 0.131447 0.930679 246 | vt 0.120411 0.930679 247 | vt 0.120411 0.930679 248 | vt 0.120411 0.908607 249 | vt 0.365822 0.922000 250 | vt 0.354786 0.944072 251 | vt 0.354786 0.944072 252 | vt 0.365822 0.944072 253 | vt 0.354786 0.922000 254 | vt 0.354786 0.944072 255 | vt 0.354786 0.922000 256 | vt 0.354786 0.944072 257 | vt 0.354786 0.922000 258 | vt 0.354786 0.922000 259 | vt 0.354786 0.922000 260 | vt 0.354786 0.922000 261 | vt 0.354786 0.944072 262 | vt 0.354786 0.944072 263 | vt 0.354786 0.944072 264 | vt 0.354786 0.944072 265 | vt 0.354786 0.922000 266 | vt 0.354786 0.922000 267 | vt 0.354786 0.944072 268 | vt 0.354786 0.944072 269 | vt 0.354786 0.944072 270 | vt 0.354786 0.944072 271 | vt 0.354786 0.944072 272 | vt 0.354786 0.944072 273 | vt 0.354786 0.944072 274 | vt 0.354786 0.944072 275 | vt 0.354786 0.944072 276 | vt 0.354786 0.944072 277 | vt 0.106151 0.913071 278 | vt 0.128224 0.913071 279 | vt 0.106151 0.935143 280 | vt 0.128224 0.935143 281 | vt 0.332714 0.944072 282 | vt 0.332714 0.922000 283 | vt 0.354786 0.944072 284 | vt 0.354786 0.944072 285 | vt 0.354786 0.922000 286 | vt 0.354786 0.922000 287 | vt 0.354786 0.944072 288 | vt 0.354786 0.944072 289 | vt 0.354786 0.944072 290 | vt 0.354786 0.944072 291 | vt 0.354786 0.922000 292 | vt 0.354786 0.922000 293 | vt 0.354786 0.944072 294 | vt 0.354786 0.944072 295 | vt 0.354786 0.922000 296 | vt 0.354786 0.922000 297 | vt 0.354786 0.922000 298 | vt 0.354786 0.922000 299 | vt 0.354786 0.922000 300 | vt 0.354786 0.922000 301 | vt 0.354786 0.922000 302 | vt 0.354786 0.922000 303 | vt 0.354786 0.922000 304 | vt 0.354786 0.944072 305 | vt 0.354786 0.955108 306 | vt 0.354786 0.944072 307 | vt 0.354786 0.944072 308 | vt 0.354786 0.944072 309 | vt 0.354786 0.944072 310 | vt 0.354786 0.944072 311 | vt 0.354786 0.944072 312 | vt 0.354786 0.922000 313 | vt 0.354786 0.922000 314 | vt 0.354786 0.944072 315 | vt 0.354786 0.944072 316 | vt 0.354786 0.922000 317 | vt 0.354786 0.922000 318 | vt 0.354786 0.922000 319 | vt 0.354786 0.922000 320 | vt 0.354786 0.922000 321 | vt 0.354786 0.922000 322 | vt 0.354786 0.944072 323 | vt 0.354786 0.944072 324 | vt 0.120411 0.908607 325 | vt 0.120411 0.930679 326 | vt 0.120411 0.908607 327 | vt 0.120411 0.930679 328 | vt 0.354786 0.944072 329 | vt 0.354786 0.944072 330 | vt 0.354786 0.922000 331 | vt 0.354786 0.944072 332 | vt 0.354786 0.922000 333 | vt 0.354786 0.944072 334 | vt 0.354786 0.922000 335 | vt 0.354786 0.922000 336 | vt 0.354786 0.922000 337 | vt 0.354786 0.922000 338 | vt 0.354786 0.944072 339 | vt 0.354786 0.944072 340 | vt 0.354786 0.944072 341 | vt 0.354786 0.944072 342 | vt 0.354786 0.922000 343 | vt 0.354786 0.922000 344 | vt 0.354786 0.944072 345 | vt 0.354786 0.944072 346 | vt 0.354786 0.944072 347 | vt 0.354786 0.944072 348 | vt 0.354786 0.944072 349 | vt 0.354786 0.944072 350 | vt 0.354786 0.944072 351 | vt 0.354786 0.944072 352 | vt 0.354786 0.944072 353 | vt 0.354786 0.944072 354 | vn 0.0000 -1.0000 0.0000 355 | vn -1.0000 0.0000 0.0000 356 | vn 0.0000 0.0000 -1.0000 357 | vn 0.0000 0.9851 0.1722 358 | vn -0.2564 0.9655 -0.0443 359 | vn -0.9675 0.0000 -0.2528 360 | vn -0.7748 0.0000 0.6322 361 | vn -0.0000 -0.8326 0.5539 362 | vn -0.3120 -0.9429 0.1167 363 | vn 0.0000 -0.9851 0.1722 364 | vn 0.0000 1.0000 0.0000 365 | vn -0.0110 -0.5082 -0.8612 366 | vn -0.0005 -1.0000 -0.0013 367 | vn -0.9486 0.0000 -0.3163 368 | vn -0.0090 0.6095 -0.7928 369 | vn -0.0021 1.0000 -0.0046 370 | vn 0.0011 0.9999 -0.0125 371 | vn 0.0213 0.9995 0.0248 372 | vn 0.0212 -0.9995 0.0247 373 | vn 0.0008 -0.9999 -0.0125 374 | vn -0.0001 1.0000 0.0004 375 | vn -0.0982 0.6248 -0.7746 376 | vn -0.0954 -0.9951 0.0265 377 | vn -0.7344 0.6787 0.0048 378 | vn -0.5746 -0.6104 -0.5452 379 | vn -0.8293 -0.4810 -0.2845 380 | vn 0.8092 0.5876 0.0002 381 | vn 0.0000 0.9997 0.0252 382 | vn -0.0919 -0.2545 0.9627 383 | vn -0.1888 0.1599 0.9689 384 | vn -0.2093 -0.1325 0.9688 385 | vn -0.0656 0.2243 0.9723 386 | vn 0.0216 0.9998 0.0003 387 | vn -0.2446 0.0207 0.9694 388 | vn 0.0000 0.0000 1.0000 389 | vn 0.3092 0.9510 0.0006 390 | vn 1.0000 -0.0001 -0.0000 391 | vn 0.3089 -0.9511 -0.0006 392 | vn 0.8089 -0.5880 -0.0002 393 | vn -0.0107 0.9999 0.0069 394 | vn -0.0089 -0.9963 -0.0856 395 | vn -0.6511 0.0000 -0.7590 396 | vn 0.0052 -0.8265 0.5628 397 | vn 0.0309 -0.0020 0.9995 398 | vn -0.0152 -0.0000 0.9999 399 | vn 0.9999 0.0000 0.0156 400 | vn -0.0027 -0.9999 -0.0123 401 | vn 0.0242 -0.6676 -0.7442 402 | vn 0.8789 -0.0010 0.4770 403 | vn 0.9761 0.0002 0.2175 404 | vn -0.0049 0.9999 0.0089 405 | vn -0.0048 1.0000 -0.0011 406 | vn -0.9720 -0.0003 -0.2349 407 | vn -0.9802 -0.0002 -0.1980 408 | vn 0.0045 -1.0000 0.0001 409 | vn 0.9999 -0.0001 0.0144 410 | vn -0.9999 -0.0001 -0.0148 411 | vn 0.0153 -0.0000 -0.9999 412 | vn 0.0112 0.4610 -0.8873 413 | vn -0.0156 0.7836 0.6210 414 | vn -0.7756 -0.0046 0.6312 415 | vn 0.9999 0.0054 0.0153 416 | vn 0.9999 0.0074 0.0084 417 | vn -0.0145 0.0002 0.9999 418 | vn -0.9999 -0.0074 -0.0084 419 | vn 0.0153 -0.0001 -0.9999 420 | vn 0.9998 0.0060 0.0176 421 | vn -0.9999 -0.0054 -0.0153 422 | vn -0.0053 1.0000 -0.0001 423 | vn 0.9999 -0.0002 0.0157 424 | vn -0.9999 0.0002 -0.0159 425 | vn 1.0000 0.0000 0.0000 426 | vn 0.2564 0.9655 -0.0443 427 | vn 0.9675 0.0000 -0.2528 428 | vn 0.7748 0.0000 0.6322 429 | vn 0.3524 -0.9121 0.2095 430 | vn 0.1965 -0.6425 -0.7406 431 | vn 0.0005 -1.0000 -0.0013 432 | vn 0.9486 0.0000 -0.3163 433 | vn 0.1926 0.6353 -0.7479 434 | vn 0.0021 1.0000 -0.0046 435 | vn -0.0011 0.9999 -0.0125 436 | vn -0.0213 0.9995 0.0248 437 | vn -0.0212 -0.9995 0.0247 438 | vn -0.0008 -0.9999 -0.0125 439 | vn 0.0001 1.0000 0.0004 440 | vn 0.7177 0.6838 -0.1314 441 | vn 0.0000 -0.9997 0.0251 442 | vn 0.7344 0.6787 0.0048 443 | vn 0.1001 -0.9910 -0.0889 444 | vn 0.8605 -0.4993 0.1014 445 | vn -0.8092 0.5876 0.0002 446 | vn 0.0964 0.9950 0.0266 447 | vn 0.0919 -0.2545 0.9627 448 | vn 0.1888 0.1599 0.9689 449 | vn 0.2093 -0.1325 0.9688 450 | vn 0.0656 0.2243 0.9723 451 | vn -0.0216 0.9998 0.0003 452 | vn 0.2446 0.0207 0.9694 453 | vn -0.3092 0.9510 0.0006 454 | vn -1.0000 -0.0001 -0.0000 455 | vn -0.3089 -0.9511 -0.0006 456 | vn -0.8089 -0.5880 -0.0002 457 | vn 0.0107 0.9999 0.0069 458 | vn -0.0216 -0.9998 -0.0024 459 | vn 0.6511 0.0000 -0.7590 460 | vn -0.0052 -0.8265 0.5628 461 | vn -0.0309 -0.0020 0.9995 462 | vn 0.0152 -0.0000 0.9999 463 | vn -0.9999 0.0000 0.0156 464 | vn 0.0027 -0.9999 -0.0123 465 | vn -0.0235 -0.7156 -0.6982 466 | vn -0.8789 -0.0010 0.4770 467 | vn -0.9761 0.0002 0.2175 468 | vn 0.0049 0.9999 0.0089 469 | vn 0.0048 1.0000 -0.0011 470 | vn 0.9720 -0.0003 -0.2349 471 | vn 0.9802 -0.0002 -0.1980 472 | vn -0.0045 -1.0000 0.0001 473 | vn -0.9999 -0.0001 0.0144 474 | vn 0.9999 -0.0001 -0.0148 475 | vn 0.0153 0.0000 0.9999 476 | vn -0.0112 0.4610 -0.8873 477 | vn 0.0156 0.7836 0.6210 478 | vn 0.7756 -0.0046 0.6312 479 | vn -0.9999 0.0054 0.0153 480 | vn -0.9999 0.0074 0.0084 481 | vn 0.0145 0.0002 0.9999 482 | vn 0.9999 -0.0074 -0.0084 483 | vn -0.0153 -0.0001 -0.9999 484 | vn -0.9998 0.0060 0.0176 485 | vn 0.9999 -0.0054 -0.0153 486 | vn 0.0053 1.0000 -0.0001 487 | vn -0.9999 -0.0002 0.0157 488 | vn 0.9999 0.0002 -0.0159 489 | vn -0.1037 -0.6728 -0.7325 490 | vn 0.0089 -0.9963 -0.0856 491 | vn 0.0201 0.9933 -0.1139 492 | vn 0.0000 0.9999 -0.0137 493 | vn 0.7269 -0.6740 -0.1316 494 | vn 0.5746 -0.6104 -0.5452 495 | vn 0.0972 -0.9952 -0.0144 496 | vn 0.0982 0.6248 -0.7746 497 | vn 0.0101 0.5031 -0.8642 498 | vn 0.0110 -0.5082 -0.8612 499 | vn 0.3120 -0.9429 0.1167 500 | vn 0.3070 0.9499 0.0590 501 | vn 0.0965 -0.7197 -0.6875 502 | vn 0.0216 -0.9998 -0.0024 503 | vn -0.0201 0.9933 -0.1139 504 | vn -0.0984 0.9950 -0.0148 505 | vn -0.7454 -0.6281 0.2233 506 | vn -0.1001 -0.9910 -0.0889 507 | vn 0.0000 -0.9999 -0.0134 508 | vn -0.7177 0.6838 -0.1314 509 | vn -0.1465 0.4977 -0.8549 510 | vn -0.1965 -0.6425 -0.7406 511 | vn -0.3524 -0.9121 0.2095 512 | vn -0.3070 0.9499 0.0590 513 | usemtl generic 514 | s off 515 | f 79/1/1 80/2/1 4/3/1 3/4/1 516 | f 3/4/2 4/3/2 2/5/2 1/6/2 517 | f 79/7/3 3/8/3 1/9/3 81/10/3 518 | f 2/11/2 4/12/2 5/13/2 6/14/2 519 | f 82/15/4 2/11/4 6/14/4 84/16/4 520 | f 11/17/5 8/18/5 86/19/5 521 | f 13/20/6 14/21/6 9/22/6 10/23/6 522 | f 66/24/7 58/25/7 41/26/7 42/27/7 523 | f 35/28/8 40/29/8 9/22/8 14/21/8 524 | f 12/30/9 91/31/9 85/32/9 525 | f 4/12/10 80/33/10 83/34/10 5/13/10 526 | f 1/6/11 2/5/11 82/35/11 81/36/11 527 | f 15/37/12 97/38/12 91/31/12 528 | f 37/39/13 35/28/13 14/21/13 16/40/13 529 | f 18/41/14 16/40/14 14/21/14 13/20/14 530 | f 98/42/15 17/43/15 11/17/15 531 | f 39/44/16 36/45/16 13/20/16 10/23/16 532 | f 84/16/17 6/14/17 17/43/17 98/42/17 533 | f 26/46/18 19/47/18 38/48/18 534 | f 22/49/19 25/50/19 37/39/19 535 | f 5/13/20 83/34/20 97/38/20 15/37/20 536 | f 36/45/21 38/48/21 18/41/21 13/20/21 537 | f 11/17/22 17/43/22 26/46/22 538 | f 15/37/23 25/50/23 22/49/23 539 | f 8/18/24 11/17/24 24/51/24 28/52/24 540 | f 15/37/25 12/30/25 23/53/25 541 | f 6/14/3 5/13/3 22/49/3 19/47/3 542 | f 12/30/26 27/54/26 30/55/26 543 | f 21/56/27 7/57/27 31/58/27 34/59/27 544 | f 6/14/28 19/47/28 26/46/28 545 | f 108/60/29 7/57/29 27/54/29 85/61/29 546 | f 20/62/30 29/63/30 8/18/30 28/52/30 547 | f 7/57/31 21/56/31 30/55/31 27/54/31 548 | f 29/63/32 112/64/32 86/65/32 8/18/32 549 | f 26/46/33 38/48/33 36/45/33 550 | f 21/56/34 20/62/34 28/52/34 30/55/34 551 | f 31/66/35 115/67/35 116/68/35 32/69/35 552 | f 31/66/35 32/69/35 33/70/35 34/71/35 553 | f 7/57/36 108/60/36 115/72/36 31/58/36 554 | f 20/62/37 21/56/37 34/59/37 33/73/37 555 | f 112/64/38 29/63/38 32/74/38 116/75/38 556 | f 29/63/39 20/62/39 33/73/39 32/74/39 557 | f 28/52/40 24/51/40 36/45/40 39/44/40 558 | f 25/50/41 23/53/41 35/28/41 559 | f 19/47/42 22/49/42 37/39/42 38/48/42 560 | f 23/53/43 30/55/43 40/29/43 35/28/43 561 | f 30/55/44 28/52/44 39/44/44 40/29/44 562 | f 18/41/3 38/48/3 37/39/3 16/40/3 563 | f 43/76/45 56/77/45 42/27/45 41/26/45 564 | f 65/78/46 64/79/46 56/77/46 43/76/46 565 | f 58/25/1 65/78/1 43/76/1 41/26/1 566 | f 9/22/47 40/29/47 51/80/47 44/81/47 567 | f 51/80/48 50/82/48 45/83/48 568 | f 40/29/49 39/44/49 49/84/49 51/80/49 569 | f 51/80/50 49/84/50 48/85/50 50/82/50 570 | f 39/44/51 10/23/51 47/86/51 49/84/51 571 | f 49/84/52 47/86/52 46/87/52 48/85/52 572 | f 10/23/53 9/22/53 44/81/53 47/86/53 573 | f 47/86/54 44/81/54 45/83/54 46/87/54 574 | f 45/83/55 50/82/55 55/88/55 52/89/55 575 | f 50/82/56 48/85/56 54/90/56 55/88/56 576 | f 46/87/57 45/83/57 52/89/57 53/91/57 577 | f 53/91/58 62/92/58 63/93/58 54/90/58 578 | f 59/94/59 57/95/59 62/92/59 63/93/59 579 | f 68/96/60 67/97/60 60/98/60 61/99/60 580 | f 66/24/61 42/27/61 60/98/61 67/97/61 581 | f 64/79/62 54/90/62 63/93/62 68/96/62 582 | f 54/90/63 48/85/63 59/94/63 63/93/63 583 | f 42/27/64 56/77/64 61/99/64 60/98/64 584 | f 46/87/65 53/91/65 62/92/65 57/95/65 585 | f 48/85/66 46/87/66 57/95/66 59/94/66 586 | f 56/77/67 64/79/67 68/96/67 61/99/67 587 | f 53/91/68 66/24/68 67/97/68 62/92/68 588 | f 63/93/69 62/92/69 67/97/69 68/96/69 589 | f 52/89/1 55/88/1 65/78/1 58/25/1 590 | f 55/88/70 54/90/70 64/79/70 65/78/70 591 | f 53/91/71 52/89/71 58/25/71 66/24/71 592 | f 79/1/1 71/100/1 72/101/1 80/2/1 593 | f 71/100/72 69/102/72 70/103/72 72/101/72 594 | f 79/7/3 81/10/3 69/104/3 71/105/3 595 | f 70/106/72 74/107/72 73/108/72 72/109/72 596 | f 82/15/4 84/16/4 74/107/4 70/106/4 597 | f 86/19/73 76/110/73 87/111/73 598 | f 89/112/74 78/113/74 77/114/74 90/115/74 599 | f 150/116/75 126/117/75 125/118/75 142/119/75 600 | f 119/120/8 90/115/8 77/114/8 124/121/8 601 | f 88/122/76 107/123/76 85/32/76 602 | f 72/109/10 73/108/10 83/34/10 80/33/10 603 | f 69/102/11 81/36/11 82/35/11 70/103/11 604 | f 93/124/77 88/122/77 91/31/77 605 | f 121/125/78 94/126/78 90/115/78 119/120/78 606 | f 96/127/79 89/112/79 90/115/79 94/126/79 607 | f 92/128/80 87/111/80 95/129/80 608 | f 123/130/81 78/113/81 89/112/81 120/131/81 609 | f 84/16/82 98/42/82 95/129/82 74/107/82 610 | f 106/132/83 122/133/83 99/134/83 611 | f 102/135/84 121/125/84 105/136/84 612 | f 73/108/85 93/124/85 97/38/85 83/34/85 613 | f 120/131/86 89/112/86 96/127/86 122/133/86 614 | f 87/111/87 104/137/87 106/132/87 615 | f 73/108/88 102/135/88 105/136/88 616 | f 76/110/89 109/138/89 104/137/89 87/111/89 617 | f 93/124/90 105/136/90 103/139/90 618 | f 74/107/3 99/134/3 102/135/3 73/108/3 619 | f 103/139/91 111/140/91 107/123/91 620 | f 101/141/92 118/142/92 113/143/92 75/144/92 621 | f 95/129/93 106/132/93 99/134/93 622 | f 108/60/94 85/61/94 107/123/94 75/144/94 623 | f 100/145/95 109/138/95 76/110/95 110/146/95 624 | f 75/144/96 107/123/96 111/140/96 101/141/96 625 | f 110/146/97 76/110/97 86/65/97 112/64/97 626 | f 120/131/98 122/133/98 106/132/98 627 | f 101/141/99 111/140/99 109/138/99 100/145/99 628 | f 113/147/35 114/148/35 116/68/35 115/67/35 629 | f 113/147/35 118/149/35 117/150/35 114/148/35 630 | f 75/144/100 113/143/100 115/72/100 108/60/100 631 | f 100/145/101 117/151/101 118/142/101 101/141/101 632 | f 112/64/102 116/75/102 114/152/102 110/146/102 633 | f 110/146/103 114/152/103 117/151/103 100/145/103 634 | f 109/138/104 123/130/104 120/131/104 104/137/104 635 | f 105/136/105 121/125/105 119/120/105 636 | f 99/134/106 122/133/106 121/125/106 102/135/106 637 | f 103/139/107 119/120/107 124/121/107 111/140/107 638 | f 111/140/108 124/121/108 123/130/108 109/138/108 639 | f 96/127/3 94/126/3 121/125/3 122/133/3 640 | f 127/153/109 125/118/109 126/117/109 140/154/109 641 | f 149/155/110 127/153/110 140/154/110 148/156/110 642 | f 142/119/1 125/118/1 127/153/1 149/155/1 643 | f 77/114/111 128/157/111 135/158/111 124/121/111 644 | f 128/157/112 129/159/112 134/160/112 645 | f 124/121/113 135/158/113 133/161/113 123/130/113 646 | f 135/158/114 134/160/114 132/162/114 133/161/114 647 | f 123/130/115 133/161/115 131/163/115 78/113/115 648 | f 133/161/116 132/162/116 130/164/116 131/163/116 649 | f 78/113/117 131/163/117 128/157/117 77/114/117 650 | f 131/163/118 130/164/118 129/159/118 128/157/118 651 | f 129/159/119 136/165/119 139/166/119 134/160/119 652 | f 134/160/120 139/166/120 138/167/120 132/162/120 653 | f 130/164/121 137/168/121 136/165/121 129/159/121 654 | f 137/168/122 146/169/122 147/170/122 138/167/122 655 | f 143/171/123 147/170/123 146/169/123 141/172/123 656 | f 152/173/124 145/174/124 144/175/124 151/176/124 657 | f 150/116/125 151/176/125 144/175/125 126/117/125 658 | f 148/156/126 152/173/126 147/170/126 138/167/126 659 | f 138/167/127 147/170/127 143/171/127 132/162/127 660 | f 126/117/128 144/175/128 145/174/128 140/154/128 661 | f 130/164/129 141/172/129 146/169/129 137/168/129 662 | f 132/162/130 143/171/130 141/172/130 130/164/130 663 | f 140/154/131 145/174/131 152/173/131 148/156/131 664 | f 137/168/132 146/169/132 151/176/132 150/116/132 665 | f 147/170/133 152/173/133 151/176/133 146/169/133 666 | f 136/165/1 142/119/1 149/155/1 139/166/1 667 | f 139/166/134 149/155/134 148/156/134 138/167/134 668 | f 137/168/135 150/116/135 142/119/135 136/165/135 669 | f 135/158/136 128/157/136 134/160/136 670 | f 103/139/137 105/136/137 119/120/137 671 | f 104/137/138 120/131/138 106/132/138 672 | f 74/107/139 95/129/139 99/134/139 673 | f 88/122/140 103/139/140 107/123/140 674 | f 88/122/141 93/124/141 103/139/141 675 | f 93/124/142 73/108/142 105/136/142 676 | f 95/129/143 87/111/143 106/132/143 677 | f 98/42/144 92/128/144 95/129/144 678 | f 97/38/145 93/124/145 91/31/145 679 | f 91/31/146 88/122/146 85/32/146 680 | f 92/128/147 86/19/147 87/111/147 681 | f 44/81/148 51/80/148 45/83/148 682 | f 37/39/149 25/50/149 35/28/149 683 | f 24/51/150 26/46/150 36/45/150 684 | f 17/43/151 6/14/151 26/46/151 685 | f 23/53/152 12/30/152 30/55/152 686 | f 25/50/153 15/37/153 23/53/153 687 | f 5/13/154 15/37/154 22/49/154 688 | f 24/51/155 11/17/155 26/46/155 689 | f 92/128/156 98/42/156 11/17/156 690 | f 12/30/157 15/37/157 91/31/157 691 | f 27/54/158 12/30/158 85/32/158 692 | f 92/128/159 11/17/159 86/19/159 693 | """ 694 | -------------------------------------------------------------------------------- /elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "package", 3 | "name": "w0rm/elm-obj-file", 4 | "summary": "Encode and decode 3D models in the OBJ file format", 5 | "license": "BSD-3-Clause", 6 | "version": "1.2.1", 7 | "exposed-modules": [ 8 | "Obj.Encode", 9 | "Obj.Decode" 10 | ], 11 | "elm-version": "0.19.0 <= v < 0.20.0", 12 | "dependencies": { 13 | "elm/core": "1.0.0 <= v < 2.0.0", 14 | "elm/http": "2.0.0 <= v < 3.0.0", 15 | "ianmackenzie/elm-geometry": "3.9.0 <= v < 4.0.0", 16 | "ianmackenzie/elm-triangular-mesh": "1.1.0 <= v < 2.0.0", 17 | "ianmackenzie/elm-units": "2.7.0 <= v < 3.0.0" 18 | }, 19 | "test-dependencies": { 20 | "elm-explorations/test": "2.2.0 <= v < 3.0.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /examples/elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "application", 3 | "source-directories": [ 4 | "src", 5 | "../src" 6 | ], 7 | "elm-version": "0.19.1", 8 | "dependencies": { 9 | "direct": { 10 | "avh4/elm-color": "1.0.0", 11 | "elm/browser": "1.0.2", 12 | "elm/core": "1.0.5", 13 | "elm/file": "1.0.5", 14 | "elm/html": "1.0.0", 15 | "elm/http": "2.0.0", 16 | "elm/json": "1.1.3", 17 | "elm-explorations/webgl": "1.1.3", 18 | "folkertdev/one-true-path-experiment": "5.0.2", 19 | "ianmackenzie/elm-3d-camera": "3.1.0", 20 | "ianmackenzie/elm-3d-scene": "1.0.1", 21 | "ianmackenzie/elm-geometry": "3.6.0", 22 | "ianmackenzie/elm-triangular-mesh": "1.1.0", 23 | "ianmackenzie/elm-units": "2.6.0" 24 | }, 25 | "indirect": { 26 | "elm/bytes": "1.0.8", 27 | "elm/parser": "1.1.0", 28 | "elm/svg": "1.0.1", 29 | "elm/time": "1.0.0", 30 | "elm/url": "1.0.0", 31 | "elm/virtual-dom": "1.0.2", 32 | "elm-community/list-extra": "8.3.1", 33 | "elm-explorations/linear-algebra": "1.0.3", 34 | "folkertdev/elm-deque": "3.0.1", 35 | "folkertdev/svg-path-lowlevel": "3.0.0", 36 | "ianmackenzie/elm-1d-parameter": "1.0.1", 37 | "ianmackenzie/elm-float-extra": "1.1.0", 38 | "ianmackenzie/elm-geometry-linear-algebra-interop": "2.0.2", 39 | "ianmackenzie/elm-interval": "2.0.0", 40 | "ianmackenzie/elm-units-interval": "1.1.0" 41 | } 42 | }, 43 | "test-dependencies": { 44 | "direct": {}, 45 | "indirect": {} 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /examples/pod.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w0rm/elm-obj-file/9d08b74a9cb0a8fe7cdab2ae5811b3dd57c0f554/examples/pod.png -------------------------------------------------------------------------------- /examples/src/Pod.elm: -------------------------------------------------------------------------------- 1 | module Pod exposing (main) 2 | 3 | {-| This example demonstrates how to extract multiple meshes with 4 | shadows from an OBJ file and render with elm-3d-scene. 5 | 6 | The “Pod” model is courtesy of Kolja Wilcke 7 | 8 | -} 9 | 10 | import Angle exposing (Angle) 11 | import Browser 12 | import Browser.Events 13 | import Camera3d 14 | import Color exposing (Color) 15 | import Direction3d 16 | import Html exposing (Html) 17 | import Html.Attributes 18 | import Html.Events 19 | import Http 20 | import Json.Decode 21 | import Length 22 | import Obj.Decode exposing (Decoder, ObjCoordinates) 23 | import Pixels exposing (Pixels) 24 | import Point3d 25 | import Quantity exposing (Quantity) 26 | import Scene3d 27 | import Scene3d.Material exposing (Texture) 28 | import Scene3d.Mesh exposing (Shadow, Textured) 29 | import SketchPlane3d 30 | import Task 31 | import Viewpoint3d 32 | import WebGL.Texture 33 | 34 | 35 | {-| Custom filter for all objects that start with a prefix. 36 | -} 37 | objectStartsWith : String -> Decoder a -> Decoder a 38 | objectStartsWith prefix = 39 | Obj.Decode.filter 40 | (\{ object } -> 41 | case object of 42 | Just objectName -> 43 | String.startsWith prefix objectName 44 | 45 | Nothing -> 46 | False 47 | ) 48 | 49 | 50 | {-| Decode a list of meshes for matching object names. 51 | -} 52 | listOfObjects : Decoder a -> Decoder (List a) 53 | listOfObjects decoder = 54 | Obj.Decode.objectNames 55 | |> Obj.Decode.andThen 56 | (\objectNames -> 57 | objectNames 58 | |> List.map (\objectName -> Obj.Decode.object objectName decoder) 59 | |> Obj.Decode.combine 60 | ) 61 | 62 | 63 | type alias MeshWithShadow = 64 | { mesh : Textured ObjCoordinates 65 | , shadow : Shadow ObjCoordinates 66 | } 67 | 68 | 69 | {-| Decode a mesh together with the shadow. 70 | -} 71 | meshWithShadow : Decoder MeshWithShadow 72 | meshWithShadow = 73 | Obj.Decode.map 74 | (\texturedFaces -> 75 | let 76 | mesh = 77 | Scene3d.Mesh.texturedFaces texturedFaces 78 | |> Scene3d.Mesh.cullBackFaces 79 | in 80 | MeshWithShadow mesh (Scene3d.Mesh.shadow mesh) 81 | ) 82 | Obj.Decode.texturedFaces 83 | 84 | 85 | type alias Meshes = 86 | { pod : MeshWithShadow 87 | , guns : List MeshWithShadow 88 | , wheels : List MeshWithShadow 89 | } 90 | 91 | 92 | {-| Maps three decoders to get a decoder of the required meshes. 93 | -} 94 | meshes : Decoder Meshes 95 | meshes = 96 | Obj.Decode.map3 Meshes 97 | (objectStartsWith "pod_" meshWithShadow) 98 | (objectStartsWith "gun_" (listOfObjects meshWithShadow)) 99 | (objectStartsWith "wheel_" (listOfObjects meshWithShadow)) 100 | 101 | 102 | type alias Model = 103 | { material : Maybe (Scene3d.Material.Textured ObjCoordinates) 104 | , meshes : Maybe Meshes 105 | , azimuth : Angle 106 | , elevation : Angle 107 | , zoom : Float 108 | , orbiting : Bool 109 | } 110 | 111 | 112 | type Msg 113 | = LoadedTexture (Result WebGL.Texture.Error (Texture Color)) 114 | | LoadedMeshes (Result Http.Error Meshes) 115 | | MouseDown 116 | | MouseUp 117 | | MouseMove (Quantity Float Pixels) (Quantity Float Pixels) 118 | | MouseWheel Float 119 | 120 | 121 | init : () -> ( Model, Cmd Msg ) 122 | init () = 123 | ( { material = Nothing 124 | , meshes = Nothing 125 | , azimuth = Angle.degrees -45 126 | , elevation = Angle.degrees 35 127 | , orbiting = False 128 | , zoom = 0 129 | } 130 | , Cmd.batch 131 | [ Scene3d.Material.loadWith Scene3d.Material.nearestNeighborFiltering "Pod.png" 132 | |> Task.attempt LoadedTexture 133 | , Http.get 134 | { url = "Pod.obj.txt" -- .txt is required to work with `elm reactor` 135 | , expect = Obj.Decode.expectObj LoadedMeshes Length.meters meshes 136 | } 137 | ] 138 | ) 139 | 140 | 141 | update : Msg -> Model -> ( Model, Cmd Msg ) 142 | update msg model = 143 | case msg of 144 | LoadedTexture result -> 145 | ( { model 146 | | material = 147 | result 148 | |> Result.map Scene3d.Material.texturedMatte 149 | |> Result.toMaybe 150 | } 151 | , Cmd.none 152 | ) 153 | 154 | LoadedMeshes result -> 155 | ( { model | meshes = Result.toMaybe result } 156 | , Cmd.none 157 | ) 158 | 159 | MouseDown -> 160 | ( { model | orbiting = True }, Cmd.none ) 161 | 162 | MouseUp -> 163 | ( { model | orbiting = False }, Cmd.none ) 164 | 165 | MouseMove dx dy -> 166 | if model.orbiting then 167 | let 168 | rotationRate = 169 | Quantity.per Pixels.pixel (Angle.degrees 1) 170 | in 171 | ( { model 172 | | azimuth = 173 | model.azimuth 174 | |> Quantity.minus (Quantity.at rotationRate dx) 175 | , elevation = 176 | model.elevation 177 | |> Quantity.plus (Quantity.at rotationRate dy) 178 | |> Quantity.clamp (Angle.degrees -90) (Angle.degrees 90) 179 | } 180 | , Cmd.none 181 | ) 182 | 183 | else 184 | ( model, Cmd.none ) 185 | 186 | MouseWheel deltaY -> 187 | ( { model | zoom = clamp 0 1 (model.zoom - deltaY * 0.002) }, Cmd.none ) 188 | 189 | 190 | view : Model -> Html Msg 191 | view model = 192 | let 193 | camera = 194 | Camera3d.perspective 195 | { viewpoint = 196 | Viewpoint3d.orbitZ 197 | { focalPoint = Point3d.meters 0 0 1 198 | , azimuth = model.azimuth 199 | , elevation = model.elevation 200 | , distance = Length.meters (16 - model.zoom * 8) 201 | } 202 | , verticalFieldOfView = Angle.degrees 30 203 | } 204 | in 205 | case ( model.material, model.meshes ) of 206 | ( Just material, Just { pod, guns, wheels } ) -> 207 | Html.figure 208 | [ Html.Attributes.style "display" "block" 209 | , Html.Attributes.style "width" "640px" 210 | , Html.Attributes.style "margin" "auto" 211 | , Html.Attributes.style "padding" "20px" 212 | , Html.Events.preventDefaultOn "wheel" 213 | (Json.Decode.map 214 | (\deltaY -> ( MouseWheel deltaY, True )) 215 | (Json.Decode.field "deltaY" Json.Decode.float) 216 | ) 217 | ] 218 | [ Scene3d.sunny 219 | { upDirection = Direction3d.z 220 | , sunlightDirection = 221 | Direction3d.fromAzimuthInAndElevationFrom SketchPlane3d.xy 222 | (Angle.degrees 135) 223 | (Angle.degrees -55) 224 | , shadows = True 225 | , camera = camera 226 | , dimensions = ( Pixels.int 640, Pixels.int 640 ) 227 | , background = Scene3d.backgroundColor Color.lightGray 228 | , clipDepth = Length.meters 0.1 229 | , entities = 230 | [ Scene3d.meshWithShadow material pod.mesh pod.shadow 231 | , case List.head (List.drop 2 guns) of 232 | Just { mesh, shadow } -> 233 | Scene3d.meshWithShadow material mesh shadow 234 | 235 | Nothing -> 236 | Scene3d.nothing 237 | , wheels 238 | |> List.map 239 | (\{ mesh, shadow } -> 240 | Scene3d.meshWithShadow material mesh shadow 241 | ) 242 | |> Scene3d.group 243 | , Scene3d.quad (Scene3d.Material.matte Color.lightBlue) 244 | (Point3d.meters -5 5 -0.02) 245 | (Point3d.meters 5 5 -0.02) 246 | (Point3d.meters 5 -5 -0.02) 247 | (Point3d.meters -5 -5 -0.02) 248 | ] 249 | } 250 | , Html.figcaption [ Html.Attributes.style "font" "14px/1.5 sans-serif" ] 251 | [ Html.p [] 252 | [ Html.text "The “Pod” model is courtesy of " 253 | , Html.a 254 | [ Html.Attributes.href "https://twitter.com/01k" 255 | , Html.Attributes.target "_blank" 256 | ] 257 | [ Html.text "Kolja Wilcke" 258 | ] 259 | ] 260 | ] 261 | ] 262 | 263 | _ -> 264 | Html.text "Loading texture and meshes…" 265 | 266 | 267 | main : Program () Model Msg 268 | main = 269 | Browser.element 270 | { init = init 271 | , update = update 272 | , view = view 273 | , subscriptions = subscriptions 274 | } 275 | 276 | 277 | subscriptions : Model -> Sub Msg 278 | subscriptions model = 279 | if model.orbiting then 280 | Sub.batch 281 | [ Browser.Events.onMouseMove decodeMouseMove 282 | , Browser.Events.onMouseUp (Json.Decode.succeed MouseUp) 283 | ] 284 | 285 | else 286 | Browser.Events.onMouseDown (Json.Decode.succeed MouseDown) 287 | 288 | 289 | decodeMouseMove : Json.Decode.Decoder Msg 290 | decodeMouseMove = 291 | Json.Decode.map2 MouseMove 292 | (Json.Decode.field "movementX" (Json.Decode.map Pixels.float Json.Decode.float)) 293 | (Json.Decode.field "movementY" (Json.Decode.map Pixels.float Json.Decode.float)) 294 | -------------------------------------------------------------------------------- /examples/src/Pod.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w0rm/elm-obj-file/9d08b74a9cb0a8fe7cdab2ae5811b3dd57c0f554/examples/src/Pod.png -------------------------------------------------------------------------------- /examples/src/Tubes.elm: -------------------------------------------------------------------------------- 1 | module Tubes exposing (main) 2 | 3 | {-| This example demonstrates how to save a triangular mesh 4 | in OBJ format with `Obj.Encode.encode`. 5 | -} 6 | 7 | import Angle exposing (Angle) 8 | import Array 9 | import Axis3d 10 | import BoundingBox3d exposing (BoundingBox3d) 11 | import Browser 12 | import Camera3d 13 | import Color 14 | import Direction3d 15 | import File.Download 16 | import Frame3d 17 | import Html exposing (Html) 18 | import Html.Attributes 19 | import Html.Events 20 | import Json.Decode 21 | import Length exposing (Meters) 22 | import Obj.Decode exposing (ObjCoordinates) 23 | import Obj.Encode 24 | import Path 25 | import Pixels exposing (Pixels) 26 | import Point3d exposing (Point3d) 27 | import Quantity exposing (Quantity, Unitless) 28 | import Scene3d 29 | import Scene3d.Material 30 | import Scene3d.Mesh exposing (Shadow, Uniform) 31 | import SubPath 32 | import TriangularMesh exposing (TriangularMesh) 33 | import Vector3d exposing (Vector3d) 34 | import Viewpoint3d 35 | 36 | 37 | type alias Model = 38 | { mesh : Maybe Mesh 39 | , azimuth : Angle 40 | , elevation : Angle 41 | , zoom : Float 42 | , orbiting : Bool 43 | , svgPath : String 44 | } 45 | 46 | 47 | type alias Mesh = 48 | { mesh : Uniform ObjCoordinates 49 | , shadow : Shadow ObjCoordinates 50 | , boundingBox : BoundingBox3d Meters ObjCoordinates 51 | , triangularMesh : 52 | TriangularMesh 53 | { position : Point3d Meters ObjCoordinates 54 | , normal : Vector3d Unitless ObjCoordinates 55 | } 56 | } 57 | 58 | 59 | type Msg 60 | = MouseDown 61 | | MouseUp 62 | | MouseMove (Quantity Float Pixels) (Quantity Float Pixels) 63 | | MouseWheel Float 64 | | TextareaChanged String 65 | | SaveClicked 66 | 67 | 68 | init : () -> ( Model, Cmd Msg ) 69 | init () = 70 | let 71 | svgPath = 72 | "M738.905,301.344c-48.612,0-87.693,32.409-87.693,77.208c0,25.736,19.064,47.66,42.894,47.66c24.783,0,40.988-9.532,40.988-21.924c0-8.579-5.72-12.392-19.064-12.392c-28.596,0-50.519,24.783-50.519,57.191c0,28.596,21.923,50.52,55.285,50.52c38.127,0,64.815-15.251,78.161-34.314M396.709,593.021c-9.532,81.021-66.724,168.715-97.226,168.715c-10.485,0-20.97-20.97-20.97-53.378c0-74.35,36.222-144.886,58.145-144.886c22.876,0,63.864,101.039,63.864,186.826c0,2.859,0,9.531-0.953,13.345M345.237,328.034c-16.204,64.817-104.852,170.622-137.26,170.622c-7.625,0-11.438-6.673-11.438-24.782c0-83.881,41.94-174.436,67.677-174.436c40.034,0,62.911,138.214,60.051,194.452M479.637,743.627c-2.859-12.392-4.766-28.597-4.766-44.801c0-81.975,26.689-134.4,50.519-134.4c18.111,0,36.222,30.502,36.222,77.208c0,23.83-6.672,38.128-15.251,38.128c-6.673,0-12.392-8.578-12.392-30.502c0-35.268,23.83-66.724,46.707-66.724c23.829,0,44.8,38.129,44.8,112.478c0,29.549-5.719,55.285-13.345,67.677M112.657,715.03c0,27.643,18.111,46.706,40.988,46.706c29.548,0,47.66-30.501,47.66-88.646c0-37.175-2.86-73.396-8.579-108.664M421.492,497.703c-5.719-16.205-9.532-41.941-9.532-75.304c0-63.863,24.784-107.71,49.566-107.71c17.157,0,37.175,24.783,37.175,59.098c0,20.017-7.626,33.361-18.11,33.361c-9.532,0-16.205-10.485-16.205-32.408c0-43.847,25.736-75.303,51.473-75.303c31.456,0,61.957,49.567,61.957,129.635c0,21.923-3.812,52.426-8.578,68.631M134.581,311.83c-9.532-7.626-23.83-12.392-42.894-12.392c-48.613,0-89.6,40.987-89.6,106.759c0,60.05,27.643,92.459,64.817,92.459c47.659,0,81.021-28.596,81.021-62.91c0-20.971-9.532-29.549-42.893-29.549c-14.298,0-34.315,3.812-49.566,9.531M444.369,188.868c-0.954-3.813-1.906-11.438-1.906-18.111c0-57.192,34.314-134.4,58.145-134.4c17.157,0,39.08,40.987,39.08,90.553c0,24.783-5.719,35.268-13.344,35.268c-6.673,0-10.485-4.766-10.485-16.204c0-47.66,40.034-109.617,65.771-109.617c22.876,0,45.754,40.987,45.754,122.009c0,20.017-2.86,47.66-6.673,68.63M340.471,36.356c-9.532,51.473-17.157,116.29-17.157,155.371c0,31.455,8.579,42.894,28.596,42.894c26.689,0,75.303-9.532,91.506-16.205M227.994,36.356c-48.613,0-87.694,32.409-87.694,77.208c0,25.736,19.064,47.66,42.893,47.66c24.783,0,40.988-9.532,40.988-21.924c0-8.579-5.719-12.392-19.064-12.392c-28.596,0-50.519,24.783-50.519,57.192c0,28.596,21.923,50.52,55.285,50.52c38.127,0,64.817-15.251,78.162-34.315" 73 | in 74 | ( { mesh = tubes svgPath 75 | , azimuth = Angle.degrees -45 76 | , elevation = Angle.degrees 15 77 | , orbiting = False 78 | , zoom = 0 79 | , svgPath = svgPath 80 | } 81 | , Cmd.none 82 | ) 83 | 84 | 85 | update : Msg -> Model -> ( Model, Cmd Msg ) 86 | update msg model = 87 | case msg of 88 | MouseDown -> 89 | ( { model | orbiting = True }, Cmd.none ) 90 | 91 | MouseUp -> 92 | ( { model | orbiting = False }, Cmd.none ) 93 | 94 | MouseMove dx dy -> 95 | if model.orbiting then 96 | let 97 | rotationRate = 98 | Quantity.per Pixels.pixel (Angle.degrees 1) 99 | in 100 | ( { model 101 | | azimuth = 102 | model.azimuth 103 | |> Quantity.minus (Quantity.at rotationRate dx) 104 | , elevation = 105 | model.elevation 106 | |> Quantity.plus (Quantity.at rotationRate dy) 107 | |> Quantity.clamp (Angle.degrees -90) (Angle.degrees 90) 108 | } 109 | , Cmd.none 110 | ) 111 | 112 | else 113 | ( model, Cmd.none ) 114 | 115 | MouseWheel deltaY -> 116 | ( { model | zoom = clamp 0 1 (model.zoom - deltaY * 0.002) }, Cmd.none ) 117 | 118 | TextareaChanged text -> 119 | ( { model 120 | | svgPath = text 121 | , mesh = tubes text 122 | } 123 | , Cmd.none 124 | ) 125 | 126 | SaveClicked -> 127 | ( model 128 | , case model.mesh of 129 | Just { triangularMesh } -> 130 | File.Download.string "Tubes.obj" 131 | "model/obj" 132 | (Obj.Encode.encode Length.inMeters (Obj.Encode.faces triangularMesh)) 133 | 134 | Nothing -> 135 | Cmd.none 136 | ) 137 | 138 | 139 | view : Model -> Html Msg 140 | view model = 141 | Html.figure 142 | [ Html.Attributes.style "display" "block" 143 | , Html.Attributes.style "width" "640px" 144 | , Html.Attributes.style "margin" "auto" 145 | , Html.Attributes.style "padding" "20px" 146 | , Html.Attributes.style "font" "14px/1.5 sans-serif" 147 | ] 148 | [ Html.div 149 | ([ Html.Attributes.style "width" "640px" 150 | , Html.Attributes.style "height" "640px" 151 | , Html.Attributes.style "position" "relative" 152 | , Html.Attributes.style "background" (Color.toCssString Color.lightGray) 153 | ] 154 | ++ mouseEvents model.orbiting 155 | ) 156 | (case model.mesh of 157 | Just { mesh, shadow, boundingBox } -> 158 | let 159 | focalPoint = 160 | BoundingBox3d.centerPoint boundingBox 161 | 162 | ( x, y, _ ) = 163 | BoundingBox3d.dimensions boundingBox 164 | 165 | distance = 166 | Quantity.max x y 167 | 168 | camera = 169 | Camera3d.perspective 170 | { viewpoint = 171 | Viewpoint3d.orbitZ 172 | { focalPoint = focalPoint 173 | , azimuth = model.azimuth 174 | , elevation = model.elevation 175 | , distance = Quantity.multiplyBy (3 - 2 * model.zoom) distance 176 | } 177 | , verticalFieldOfView = Angle.degrees 30 178 | } 179 | in 180 | [ Scene3d.sunny 181 | { upDirection = Direction3d.z 182 | , sunlightDirection = Direction3d.y 183 | , shadows = True 184 | , camera = camera 185 | , dimensions = ( Pixels.int 640, Pixels.int 640 ) 186 | , background = Scene3d.transparentBackground 187 | , clipDepth = Length.meters 0.1 188 | , entities = 189 | [ Scene3d.meshWithShadow (Scene3d.Material.matte Color.red) mesh shadow 190 | |> Scene3d.rotateAround (Axis3d.through focalPoint Direction3d.x) (Angle.degrees 90) 191 | , Scene3d.quad (Scene3d.Material.matte Color.lightGray) 192 | (Point3d.xyz (Quantity.negate distance) (Length.meters 0.2) distance) 193 | (Point3d.xyz distance (Length.meters 0.2) distance) 194 | (Point3d.xyz distance (Length.meters 0.2) (Quantity.negate distance)) 195 | (Point3d.xyz (Quantity.negate distance) (Length.meters 0.2) (Quantity.negate distance)) 196 | |> Scene3d.translateBy (Vector3d.from Point3d.origin focalPoint) 197 | ] 198 | } 199 | , Html.button 200 | [ Html.Attributes.style "position" "absolute" 201 | , Html.Attributes.style "right" "10px" 202 | , Html.Attributes.style "bottom" "10px" 203 | , Html.Events.onClick SaveClicked 204 | ] 205 | [ Html.text "Save in OBJ format" ] 206 | ] 207 | 208 | _ -> 209 | [] 210 | ) 211 | , Html.figcaption [] 212 | [ Html.p [] 213 | [ Html.text "The “elm game jam” lettering is courtesy of " 214 | , Html.a 215 | [ Html.Attributes.href "https://github.com/kuzminadya" 216 | , Html.Attributes.target "_blank" 217 | ] 218 | [ Html.text "Nadya Kuzmina" 219 | ] 220 | ] 221 | ] 222 | , Html.textarea 223 | [ Html.Events.onInput TextareaChanged 224 | , Html.Attributes.rows 5 225 | , Html.Attributes.style "width" "100%" 226 | , Html.Attributes.style "box-sizing" "border-box" 227 | , Html.Attributes.style "font-family" "monospace" 228 | , Html.Attributes.style "word-break" "break-all" 229 | , Html.Attributes.style "border-color" 230 | (if model.mesh == Nothing then 231 | "red" 232 | 233 | else 234 | "default" 235 | ) 236 | ] 237 | [ Html.text model.svgPath ] 238 | ] 239 | 240 | 241 | main : Program () Model Msg 242 | main = 243 | Browser.element 244 | { init = init 245 | , update = update 246 | , view = view 247 | , subscriptions = always Sub.none 248 | } 249 | 250 | 251 | mouseEvents : Bool -> List (Html.Attribute Msg) 252 | mouseEvents orbiting = 253 | let 254 | mouseWheelEvent = 255 | Html.Events.preventDefaultOn "wheel" 256 | (Json.Decode.map 257 | (\deltaY -> ( MouseWheel deltaY, True )) 258 | (Json.Decode.field "deltaY" Json.Decode.float) 259 | ) 260 | in 261 | if orbiting then 262 | [ Html.Events.on "mousemove" decodeMouseMove 263 | , Html.Events.onMouseUp MouseUp 264 | , mouseWheelEvent 265 | ] 266 | 267 | else 268 | [ Html.Events.onMouseDown MouseDown 269 | , mouseWheelEvent 270 | ] 271 | 272 | 273 | decodeMouseMove : Json.Decode.Decoder Msg 274 | decodeMouseMove = 275 | Json.Decode.map2 MouseMove 276 | (Json.Decode.field "movementX" (Json.Decode.map Pixels.float Json.Decode.float)) 277 | (Json.Decode.field "movementY" (Json.Decode.map Pixels.float Json.Decode.float)) 278 | 279 | 280 | {-| Generate tubes mesh from SVG path 281 | -} 282 | tubes : String -> Maybe Mesh 283 | tubes svgPath = 284 | let 285 | radius = 286 | Length.meters 0.01 287 | 288 | segment = 289 | 5 290 | 291 | slope = 292 | 0 293 | 294 | resolution = 295 | 8 296 | 297 | pathToEntity path result = 298 | path 299 | |> SubPath.arcLengthParameterized 0.01 300 | |> (\parametrization -> 301 | let 302 | length = 303 | SubPath.arcLength parametrization 304 | 305 | subdivisions = 306 | round (length / segment) 307 | 308 | pointOnPath u = 309 | let 310 | ( px, py ) = 311 | SubPath.pointAlong parametrization (length * u) 312 | |> Maybe.withDefault ( 0, 0 ) 313 | 314 | ( tx, ty ) = 315 | SubPath.tangentAlong parametrization (length * u) 316 | |> Maybe.withDefault ( 0, 0 ) 317 | in 318 | { position = Point3d.millimeters px -py (u * slope) 319 | , tangent = Direction3d.unsafe { x = tx, y = -ty, z = 0 } 320 | , normal = Direction3d.z 321 | } 322 | 323 | endCap p = 324 | let 325 | { tangent, position, normal } = 326 | pointOnPath p 327 | 328 | flippedNormal = 329 | if p == 0 then 330 | Direction3d.toVector (Direction3d.reverse tangent) 331 | 332 | else 333 | Direction3d.toVector tangent 334 | in 335 | TriangularMesh.radial 336 | { position = position 337 | , normal = flippedNormal 338 | } 339 | (List.map 340 | (\i -> 341 | let 342 | u = 343 | if p == 0 then 344 | -(toFloat i / toFloat resolution) 345 | 346 | else 347 | toFloat i / toFloat resolution 348 | 349 | frame = 350 | Frame3d.atOrigin 351 | |> Frame3d.translateIn normal radius 352 | |> Frame3d.rotateAround (Axis3d.withDirection tangent position) (Angle.turns u) 353 | in 354 | { position = Point3d.placeIn frame position 355 | , normal = flippedNormal 356 | } 357 | ) 358 | (List.range 0 resolution) 359 | ) 360 | 361 | tube = 362 | TriangularMesh.tube subdivisions 363 | resolution 364 | (\u v -> 365 | let 366 | { tangent, position, normal } = 367 | pointOnPath u 368 | 369 | frame = 370 | Frame3d.atOrigin 371 | |> Frame3d.translateIn normal radius 372 | |> Frame3d.rotateAround (Axis3d.withDirection tangent position) (Angle.turns -v) 373 | in 374 | { position = Point3d.placeIn frame position 375 | , normal = Direction3d.toVector (Direction3d.placeIn frame normal) 376 | } 377 | ) 378 | in 379 | tube :: endCap 0 :: endCap 1 :: result 380 | ) 381 | in 382 | svgPath 383 | |> Path.parse 384 | |> Result.toMaybe 385 | |> Maybe.map 386 | (\result -> 387 | let 388 | triangularMesh = 389 | TriangularMesh.combine (List.foldl pathToEntity [] result) 390 | 391 | boundingBox = 392 | triangularMesh 393 | |> TriangularMesh.vertices 394 | |> Array.toList 395 | |> List.map .position 396 | |> BoundingBox3d.hullN 397 | |> Maybe.withDefault (BoundingBox3d.singleton Point3d.origin) 398 | 399 | mesh = 400 | Scene3d.Mesh.indexedFaces triangularMesh 401 | in 402 | { mesh = Scene3d.Mesh.cullBackFaces mesh 403 | , shadow = Scene3d.Mesh.shadow mesh 404 | , boundingBox = boundingBox 405 | , triangularMesh = triangularMesh 406 | } 407 | ) 408 | -------------------------------------------------------------------------------- /examples/src/Viewer.elm: -------------------------------------------------------------------------------- 1 | module Viewer exposing (main) 2 | 3 | {-| This example demonstrates how to load a mesh from a file. 4 | It can also be used to test the parser :-) 5 | 6 | Now try dragging and dropping some OBJ files from ! 7 | 8 | -} 9 | 10 | import Angle exposing (Angle) 11 | import Array 12 | import BoundingBox3d exposing (BoundingBox3d) 13 | import Browser 14 | import Browser.Events 15 | import Camera3d exposing (Camera3d) 16 | import Color exposing (Color) 17 | import Direction3d 18 | import File exposing (File) 19 | import File.Select 20 | import Html exposing (Attribute, Html) 21 | import Html.Attributes 22 | import Html.Events 23 | import Json.Decode 24 | import Length exposing (Meters) 25 | import Obj.Decode exposing (Decoder, ObjCoordinates) 26 | import Pixels exposing (Pixels) 27 | import Point3d exposing (Point3d) 28 | import Quantity exposing (Quantity) 29 | import Scene3d 30 | import Scene3d.Material exposing (Texture) 31 | import Scene3d.Mesh exposing (Textured, Uniform) 32 | import Task 33 | import TriangularMesh exposing (TriangularMesh) 34 | import Viewpoint3d 35 | import WebGL.Texture exposing (Error(..)) 36 | 37 | 38 | type ViewMesh 39 | = TexturedMesh (Textured ObjCoordinates) 40 | | UniformMesh (Uniform ObjCoordinates) 41 | 42 | 43 | {-| Because we don’t know the exect format of a mesh, we try decoding different 44 | primitives: from the most specific to the most simple one. 45 | -} 46 | meshWithBoundingBoxDecoder : Decoder ( ViewMesh, BoundingBox3d Meters ObjCoordinates ) 47 | meshWithBoundingBoxDecoder = 48 | Obj.Decode.oneOf 49 | [ withBoundingBox .position (Scene3d.Mesh.texturedFaces >> TexturedMesh) Obj.Decode.texturedFaces 50 | , withBoundingBox .position (Scene3d.Mesh.indexedFaces >> UniformMesh) Obj.Decode.faces 51 | , withBoundingBox .position (Scene3d.Mesh.texturedFacets >> TexturedMesh) Obj.Decode.texturedTriangles 52 | , withBoundingBox identity (Scene3d.Mesh.indexedFacets >> UniformMesh) Obj.Decode.triangles 53 | ] 54 | 55 | 56 | withBoundingBox : 57 | (a -> Point3d Meters ObjCoordinates) -- a function that knows how to extract position of a vertex 58 | -> (TriangularMesh a -> ViewMesh) -- a function that knows how to create a ViewMesh 59 | -> Decoder (TriangularMesh a) -- a primitive decoder 60 | -> Decoder ( ViewMesh, BoundingBox3d Meters ObjCoordinates ) 61 | withBoundingBox getPosition createMesh = 62 | Obj.Decode.map 63 | (\triangularMesh -> 64 | ( createMesh triangularMesh 65 | , case List.map getPosition (Array.toList (TriangularMesh.vertices triangularMesh)) of 66 | first :: rest -> 67 | BoundingBox3d.hull first rest 68 | 69 | [] -> 70 | BoundingBox3d.singleton Point3d.origin 71 | ) 72 | ) 73 | 74 | 75 | type LoadState a 76 | = Empty 77 | | Loaded a 78 | | Error String 79 | 80 | 81 | type alias Model = 82 | { texture : LoadState (Texture Color) 83 | , meshWithBoundingBox : LoadState ( ViewMesh, BoundingBox3d Meters ObjCoordinates ) 84 | , hover : Bool 85 | , azimuth : Angle 86 | , elevation : Angle 87 | , zoom : Float 88 | , orbiting : Bool 89 | } 90 | 91 | 92 | type Msg 93 | = PickClicked 94 | | ResetClicked 95 | | DragEnter 96 | | DragLeave 97 | | LoadedTexture (Result WebGL.Texture.Error (Texture Color)) 98 | | LoadedMesh (Result String ( ViewMesh, BoundingBox3d Meters ObjCoordinates )) 99 | | GotFiles File (List File) 100 | | MouseDown 101 | | MouseUp 102 | | MouseMove (Quantity Float Pixels) (Quantity Float Pixels) 103 | | MouseWheel Float 104 | 105 | 106 | main : Program () Model Msg 107 | main = 108 | Browser.element 109 | { init = 110 | always 111 | ( { texture = Empty 112 | , meshWithBoundingBox = Empty 113 | , hover = False 114 | , azimuth = Angle.degrees -45 115 | , elevation = Angle.degrees 35 116 | , zoom = 0 117 | , orbiting = False 118 | } 119 | , Cmd.none 120 | ) 121 | , update = update 122 | , view = view 123 | , subscriptions = subscriptions 124 | } 125 | 126 | 127 | subscriptions : Model -> Sub Msg 128 | subscriptions model = 129 | if model.orbiting then 130 | Sub.batch 131 | [ Browser.Events.onMouseMove decodeMouseMove 132 | , Browser.Events.onMouseUp (Json.Decode.succeed MouseUp) 133 | ] 134 | 135 | else 136 | Browser.Events.onMouseDown (Json.Decode.succeed MouseDown) 137 | 138 | 139 | update : Msg -> Model -> ( Model, Cmd Msg ) 140 | update msg model = 141 | case msg of 142 | ResetClicked -> 143 | ( { model | texture = Empty, meshWithBoundingBox = Empty }, Cmd.none ) 144 | 145 | PickClicked -> 146 | ( model, File.Select.files [] GotFiles ) 147 | 148 | DragEnter -> 149 | ( { model | hover = True }, Cmd.none ) 150 | 151 | DragLeave -> 152 | ( { model | hover = False }, Cmd.none ) 153 | 154 | GotFiles file files -> 155 | let 156 | ( imageFiles, objFiles ) = 157 | List.partition 158 | (File.mime >> String.startsWith "image/") 159 | (file :: files) 160 | 161 | loadTextureCmd = 162 | case imageFiles of 163 | textureFile :: _ -> 164 | File.toUrl textureFile 165 | |> Task.andThen 166 | (Scene3d.Material.loadWith 167 | Scene3d.Material.nearestNeighborFiltering 168 | ) 169 | |> Task.attempt LoadedTexture 170 | 171 | [] -> 172 | Cmd.none 173 | 174 | loadAndDecodeMeshCmd = 175 | case objFiles of 176 | objFile :: _ -> 177 | File.toString objFile 178 | |> Task.andThen 179 | (\string -> 180 | case 181 | Obj.Decode.decodeString 182 | Length.meters 183 | meshWithBoundingBoxDecoder 184 | string 185 | of 186 | Ok m -> 187 | Task.succeed m 188 | 189 | Err err -> 190 | Task.fail err 191 | ) 192 | |> Task.attempt LoadedMesh 193 | 194 | [] -> 195 | Cmd.none 196 | in 197 | ( { model | hover = False }, Cmd.batch [ loadTextureCmd, loadAndDecodeMeshCmd ] ) 198 | 199 | LoadedMesh result -> 200 | ( { model 201 | | meshWithBoundingBox = 202 | case result of 203 | Err err -> 204 | Error err 205 | 206 | Ok m -> 207 | Loaded m 208 | } 209 | , Cmd.none 210 | ) 211 | 212 | LoadedTexture result -> 213 | ( { model 214 | | texture = 215 | case result of 216 | Err LoadError -> 217 | Error "Texture load error" 218 | 219 | Err (SizeError _ _) -> 220 | Error "Texture size error" 221 | 222 | Ok texture -> 223 | Loaded texture 224 | } 225 | , Cmd.none 226 | ) 227 | 228 | MouseDown -> 229 | ( { model | orbiting = True }, Cmd.none ) 230 | 231 | MouseUp -> 232 | ( { model | orbiting = False }, Cmd.none ) 233 | 234 | MouseMove dx dy -> 235 | if model.orbiting then 236 | let 237 | rotationRate = 238 | Quantity.per Pixels.pixel (Angle.degrees 1) 239 | in 240 | ( { model 241 | | azimuth = 242 | model.azimuth 243 | |> Quantity.minus (Quantity.at rotationRate dx) 244 | , elevation = 245 | model.elevation 246 | |> Quantity.plus (Quantity.at rotationRate dy) 247 | |> Quantity.clamp (Angle.degrees -90) (Angle.degrees 90) 248 | } 249 | , Cmd.none 250 | ) 251 | 252 | else 253 | ( model, Cmd.none ) 254 | 255 | MouseWheel deltaY -> 256 | ( { model | zoom = clamp 0 1 (model.zoom - deltaY * 0.002) }, Cmd.none ) 257 | 258 | 259 | meshView : Camera3d Meters ObjCoordinates -> LoadState (Texture Color) -> ViewMesh -> Html Msg 260 | meshView camera loadingTexture mesh = 261 | let 262 | entity = 263 | case mesh of 264 | TexturedMesh texturedMesh -> 265 | case loadingTexture of 266 | Loaded texture -> 267 | Scene3d.mesh (Scene3d.Material.texturedMatte texture) texturedMesh 268 | 269 | Error _ -> 270 | Scene3d.mesh (Scene3d.Material.matte Color.red) texturedMesh 271 | 272 | _ -> 273 | Scene3d.mesh (Scene3d.Material.matte Color.blue) texturedMesh 274 | 275 | UniformMesh uniformMesh -> 276 | Scene3d.mesh (Scene3d.Material.matte Color.blue) uniformMesh 277 | in 278 | Scene3d.sunny 279 | { upDirection = Direction3d.z 280 | , sunlightDirection = Direction3d.negativeZ 281 | , shadows = False 282 | , camera = camera 283 | , dimensions = ( Pixels.int 640, Pixels.int 640 ) 284 | , background = Scene3d.transparentBackground 285 | , clipDepth = Length.meters 0.1 286 | , entities = [ entity ] 287 | } 288 | 289 | 290 | view : Model -> Html Msg 291 | view model = 292 | centeredContents 293 | [ Html.Attributes.style "position" "absolute" 294 | , Html.Attributes.style "left" "0" 295 | , Html.Attributes.style "top" "0" 296 | , Html.Attributes.style "width" "100%" 297 | , Html.Attributes.style "height" "100%" 298 | , hijackOn "dragenter" (Json.Decode.succeed DragEnter) 299 | , hijackOn "dragover" (Json.Decode.succeed DragEnter) 300 | , hijackOn "dragleave" (Json.Decode.succeed DragLeave) 301 | , hijackOn "drop" dropDecoder 302 | , Html.Events.preventDefaultOn "wheel" 303 | (Json.Decode.map 304 | (\deltaY -> ( MouseWheel deltaY, True )) 305 | (Json.Decode.field "deltaY" Json.Decode.float) 306 | ) 307 | ] 308 | [ centeredContents 309 | [ Html.Attributes.style "border" 310 | (case ( model.hover, model.meshWithBoundingBox ) of 311 | ( True, _ ) -> 312 | "3px dashed green" 313 | 314 | ( False, Loaded _ ) -> 315 | "3px dashed rgb(52, 101, 164)" 316 | 317 | ( False, Error _ ) -> 318 | "3px dashed red" 319 | 320 | _ -> 321 | "3px dashed #ccc" 322 | ) 323 | , Html.Attributes.style "width" "640px" 324 | , Html.Attributes.style "height" "640px" 325 | , Html.Attributes.style "position" "relative" 326 | ] 327 | (case model.meshWithBoundingBox of 328 | Empty -> 329 | [ Html.button [ Html.Events.onClick PickClicked ] 330 | [ Html.text "select an OBJ file and/or an image" ] 331 | ] 332 | 333 | Error err -> 334 | [ Html.p [ Html.Attributes.style "color" "red" ] 335 | [ Html.text err ] 336 | , Html.button [ Html.Events.onClick PickClicked ] 337 | [ Html.text "try a different OBJ file" ] 338 | ] 339 | 340 | Loaded ( mesh, boundingBox ) -> 341 | let 342 | { minX, maxX, minY, maxY, minZ, maxZ } = 343 | BoundingBox3d.extrema boundingBox 344 | 345 | distance = 346 | List.map Quantity.abs [ minX, maxX, minY, maxY, minZ, maxZ ] 347 | |> List.foldl Quantity.max Quantity.zero 348 | |> Quantity.multiplyBy 2 349 | |> Quantity.multiplyBy (2 - model.zoom) 350 | 351 | camera = 352 | Camera3d.perspective 353 | { viewpoint = 354 | Viewpoint3d.orbitZ 355 | { focalPoint = BoundingBox3d.centerPoint boundingBox 356 | , azimuth = model.azimuth 357 | , elevation = model.elevation 358 | , distance = distance 359 | } 360 | , verticalFieldOfView = Angle.degrees 30 361 | } 362 | in 363 | [ meshView camera model.texture mesh 364 | , Html.button 365 | [ Html.Attributes.style "position" "absolute" 366 | , Html.Attributes.style "right" "10px" 367 | , Html.Attributes.style "top" "10px" 368 | , Html.Events.onClick ResetClicked 369 | ] 370 | [ Html.text "close" ] 371 | ] 372 | ) 373 | ] 374 | 375 | 376 | centeredContents : List (Attribute msg) -> List (Html msg) -> Html msg 377 | centeredContents attributes = 378 | Html.div 379 | ([ Html.Attributes.style "align-items" "center" 380 | , Html.Attributes.style "justify-content" "center" 381 | , Html.Attributes.style "display" "flex" 382 | , Html.Attributes.style "flex-direction" "column" 383 | ] 384 | ++ attributes 385 | ) 386 | 387 | 388 | dropDecoder : Json.Decode.Decoder Msg 389 | dropDecoder = 390 | Json.Decode.at [ "dataTransfer", "files" ] 391 | (Json.Decode.oneOrMore GotFiles File.decoder) 392 | 393 | 394 | hijackOn : String -> Json.Decode.Decoder msg -> Attribute msg 395 | hijackOn event decoder = 396 | Html.Events.preventDefaultOn event 397 | (Json.Decode.map (\val -> ( val, True )) decoder) 398 | 399 | 400 | decodeMouseMove : Json.Decode.Decoder Msg 401 | decodeMouseMove = 402 | Json.Decode.map2 MouseMove 403 | (Json.Decode.field "movementX" (Json.Decode.map Pixels.float Json.Decode.float)) 404 | (Json.Decode.field "movementY" (Json.Decode.map Pixels.float Json.Decode.float)) 405 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-utils": { 4 | "inputs": { 5 | "systems": "systems" 6 | }, 7 | "locked": { 8 | "lastModified": 1710146030, 9 | "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", 10 | "owner": "numtide", 11 | "repo": "flake-utils", 12 | "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", 13 | "type": "github" 14 | }, 15 | "original": { 16 | "owner": "numtide", 17 | "repo": "flake-utils", 18 | "type": "github" 19 | } 20 | }, 21 | "nixpkgs": { 22 | "locked": { 23 | "lastModified": 1711124224, 24 | "narHash": "sha256-l0zlN/3CiodvWDtfBOVxeTwYSRz93muVbXWSpaMjXxM=", 25 | "owner": "NixOS", 26 | "repo": "nixpkgs", 27 | "rev": "56528ee42526794d413d6f244648aaee4a7b56c0", 28 | "type": "github" 29 | }, 30 | "original": { 31 | "owner": "NixOS", 32 | "ref": "nixos-23.11", 33 | "repo": "nixpkgs", 34 | "type": "github" 35 | } 36 | }, 37 | "root": { 38 | "inputs": { 39 | "flake-utils": "flake-utils", 40 | "nixpkgs": "nixpkgs" 41 | } 42 | }, 43 | "systems": { 44 | "locked": { 45 | "lastModified": 1681028828, 46 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 47 | "owner": "nix-systems", 48 | "repo": "default", 49 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 50 | "type": "github" 51 | }, 52 | "original": { 53 | "owner": "nix-systems", 54 | "repo": "default", 55 | "type": "github" 56 | } 57 | } 58 | }, 59 | "root": "root", 60 | "version": 7 61 | } 62 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs = { 3 | nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11"; 4 | flake-utils.url = "github:numtide/flake-utils"; 5 | }; 6 | 7 | outputs = { self, nixpkgs, flake-utils }: 8 | flake-utils.lib.eachDefaultSystem (system: 9 | let pkgs = nixpkgs.legacyPackages.${system}; 10 | in 11 | { 12 | devShells.default = with pkgs; with elmPackages; mkShell { 13 | buildInputs = [ 14 | elm 15 | elm-format 16 | elm-test 17 | elm-review 18 | elm-json 19 | ]; 20 | }; 21 | }); 22 | } 23 | -------------------------------------------------------------------------------- /review/elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "application", 3 | "source-directories": [ 4 | "src" 5 | ], 6 | "elm-version": "0.19.1", 7 | "dependencies": { 8 | "direct": { 9 | "elm/core": "1.0.5", 10 | "elm/json": "1.1.3", 11 | "elm/project-metadata-utils": "1.0.1", 12 | "jfmengels/elm-review": "2.13.1", 13 | "jfmengels/elm-review-simplify": "2.1.3", 14 | "jfmengels/elm-review-unused": "1.2.0", 15 | "jfmengels/elm-review-performance": "1.0.2", 16 | "stil4m/elm-syntax": "7.3.2" 17 | }, 18 | "indirect": { 19 | "elm/html": "1.0.0", 20 | "elm/parser": "1.1.0", 21 | "elm/random": "1.0.0", 22 | "elm/time": "1.0.0", 23 | "elm/virtual-dom": "1.0.2", 24 | "elm-community/list-extra": "8.7.0", 25 | "elm-explorations/test": "2.2.0", 26 | "rtfeldman/elm-hex": "1.0.0", 27 | "stil4m/structured-writer": "1.0.3" 28 | } 29 | }, 30 | "test-dependencies": { 31 | "direct": { 32 | "elm-explorations/test": "2.2.0" 33 | }, 34 | "indirect": {} 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /review/src/ReviewConfig.elm: -------------------------------------------------------------------------------- 1 | module ReviewConfig exposing (config) 2 | 3 | {-| Do not rename the ReviewConfig module or the config function, because 4 | `elm-review` will look for these. 5 | 6 | To add packages that contain rules, add them to this review project using 7 | 8 | `elm install author/packagename` 9 | 10 | when inside the directory containing this file. 11 | 12 | -} 13 | 14 | import NoUnoptimizedRecursion 15 | import NoUnused.CustomTypeConstructorArgs 16 | import NoUnused.CustomTypeConstructors 17 | import NoUnused.Dependencies 18 | import NoUnused.Exports 19 | import NoUnused.Modules 20 | import NoUnused.Parameters 21 | import NoUnused.Patterns 22 | import NoUnused.Variables 23 | import Review.Rule exposing (Rule) 24 | import Simplify 25 | 26 | 27 | config : List Rule 28 | config = 29 | [ NoUnused.CustomTypeConstructors.rule 30 | [ { moduleName = "Point3d" 31 | , typeName = "Point3d" 32 | , index = 1 -- Position of the phantom variable in the type's arguments 33 | } 34 | ] 35 | , NoUnused.CustomTypeConstructorArgs.rule 36 | , NoUnused.Dependencies.rule 37 | , NoUnused.Exports.rule 38 | , NoUnused.Modules.rule 39 | , NoUnused.Parameters.rule 40 | , NoUnused.Patterns.rule 41 | , NoUnused.Variables.rule 42 | , Simplify.rule (Simplify.expectNaN Simplify.defaults) 43 | , NoUnoptimizedRecursion.rule (NoUnoptimizedRecursion.optOutWithComment "IGNORE TCO") 44 | ] 45 | -------------------------------------------------------------------------------- /scripts/elm-publish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euxo pipefail 3 | 4 | version=${1:-} 5 | 6 | if [ -z "$version" ]; then 7 | echo "Please set the desired version" 8 | exit 1 9 | fi 10 | 11 | if [[ -n $(git status --porcelain) ]]; then 12 | echo "Please commit all your changes" 13 | exit 1 14 | fi 15 | 16 | echo "Y" | elm bump 17 | elm_version=$(grep -m1 version elm.json | awk -F: '{ print $2 }' | sed 's/[", ]//g') 18 | 19 | if [ $version != $elm_version ]; then 20 | echo "Versions $elm_version and $version do not match!" 21 | exit 1 22 | fi 23 | 24 | git add elm.json 25 | git commit -m "Bump to $version" 26 | git push 27 | last_commit=$(git rev-parse HEAD) 28 | 29 | git rm -rf --ignore-unmatch .github examples tests benchmarks 30 | sed -i.bak "s+https://unsoundscapes.com/elm-obj-file/+https://unsoundscapes.com/elm-obj-file/$version/+g" README.md 31 | sed -i.bak "s+https://github.com/w0rm/elm-obj-file/tree/main/+https://github.com/w0rm/elm-obj-file/tree/$last_commit/+g" README.md 32 | rm README.md.bak 33 | git add README.md 34 | git commit -m "Release $version" 35 | git tag -a $version -m "Release $version" 36 | git push origin $version 37 | elm publish 38 | 39 | # restore the main branch 40 | git checkout $last_commit 41 | -------------------------------------------------------------------------------- /scripts/gh-pages.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euxo pipefail 3 | 4 | version=${1:-} 5 | 6 | if [ -z "$version" ]; then 7 | version_path="" 8 | else 9 | version_path="/$version" 10 | fi 11 | 12 | cd gh-pages 13 | 14 | if [ "$(git rev-parse --abbrev-ref HEAD)" != "gh-pages" ]; then 15 | echo "Please checkout gh-pages branch" 16 | exit 1; 17 | fi 18 | 19 | if [[ -n $(git status --porcelain) ]]; then 20 | echo "Please commit all your changes" 21 | exit 1 22 | fi 23 | 24 | cd ../examples/src 25 | for example in *.elm; do 26 | # rename CamelCase to snake-case 27 | lower=$( echo "${example%.*}" \ 28 | | sed 's/\(.\)\([A-Z]\)/\1-\2/g' \ 29 | | tr '[:upper:]' '[:lower:]' \ 30 | ) 31 | mkdir -p ../../gh-pages$version_path/examples/$lower 32 | elm make $example --optimize --output ../../gh-pages$version_path/examples/$lower/index.html 33 | done 34 | cp Pod.png Pod.obj.txt ../../gh-pages$version_path/examples/pod 35 | cp ../pod.png ../../gh-pages$version_path/examples 36 | 37 | cd ../../gh-pages 38 | git add . 39 | git commit -m "Deploying $version_path to GH Pages" 40 | git push 41 | -------------------------------------------------------------------------------- /tests/AdvancedDecoding.elm: -------------------------------------------------------------------------------- 1 | module AdvancedDecoding exposing (andThen, combine, fail, oneOf, succeed) 2 | 3 | import Expect 4 | import Length 5 | import Obj.Decode as Decode 6 | import Test exposing (Test) 7 | 8 | 9 | oneOf : Test 10 | oneOf = 11 | Test.describe "oneOf" 12 | [ Test.test "lists error messages from all failed decoders" <| 13 | \_ -> 14 | objFile 15 | |> Decode.decodeString Length.centimeters 16 | (Decode.oneOf 17 | [ Decode.object "missing-object" Decode.texturedFaces 18 | , Decode.fail "Custom error" 19 | ] 20 | ) 21 | |> Expect.equal (Err "Failed oneOf decoder: No faces found for object 'missing-object', Custom error.") 22 | , Test.test "fails when no decoders were provided" 23 | (\_ -> 24 | objFile 25 | |> Decode.decodeString Length.centimeters (Decode.oneOf []) 26 | |> Expect.equal (Err "Empty oneOf decoder") 27 | ) 28 | , Test.test "succeedes with the first succesful decoder" <| 29 | \_ -> 30 | objFile 31 | |> Decode.decodeString Length.centimeters 32 | (Decode.oneOf 33 | [ Decode.fail "error" 34 | , Decode.succeed "success 1" 35 | , Decode.succeed "success 2" 36 | ] 37 | ) 38 | |> Expect.equal (Ok "success 1") 39 | ] 40 | 41 | 42 | fail : Test 43 | fail = 44 | Test.describe "fail" 45 | [ Test.test "fails with the given error message" <| 46 | \_ -> 47 | objFile 48 | |> Decode.decodeString Length.centimeters 49 | (Decode.fail "Unexpected") 50 | |> Expect.equal (Err "Unexpected") 51 | ] 52 | 53 | 54 | succeed : Test 55 | succeed = 56 | Test.describe "succeed" 57 | [ Test.test "succeeds with the given result" <| 58 | \_ -> 59 | objFile 60 | |> Decode.decodeString Length.centimeters 61 | (Decode.succeed "Success") 62 | |> Expect.equal (Ok "Success") 63 | ] 64 | 65 | 66 | andThen : Test 67 | andThen = 68 | Test.describe "andThen" 69 | [ Test.test "works for the successful case" <| 70 | \_ -> 71 | objFile 72 | |> Decode.decodeString Length.centimeters 73 | (Decode.succeed "Success" 74 | |> Decode.andThen (\str -> Decode.succeed (str ++ " Success")) 75 | ) 76 | |> Expect.equal (Ok "Success Success") 77 | ] 78 | 79 | 80 | combine : Test 81 | combine = 82 | Test.describe "combine" 83 | [ Test.test "works for the successful case" <| 84 | \_ -> 85 | objFile 86 | |> Decode.decodeString Length.centimeters 87 | (Decode.combine 88 | [ Decode.succeed "One" 89 | , Decode.succeed "Two" 90 | , Decode.succeed "Three" 91 | ] 92 | ) 93 | |> Expect.equal (Ok [ "One", "Two", "Three" ]) 94 | ] 95 | 96 | 97 | objFile : String 98 | objFile = 99 | """o object1 100 | v -0.126193 0.126193 -0.126193 101 | v -0.126193 0.126193 0.126193 102 | v -0.126193 -0.126193 -0.126193 103 | vt 0.106151 0.902035 104 | vt 0.128224 0.902035 105 | vt 0.128224 0.913071 106 | vn 0.0000 -1.0000 0.0000 107 | vn -1.0000 0.0000 0.0000 108 | vn 0.0000 0.0000 -1.0000 109 | usemtl generic 110 | f 1/1/1 2/2/2 3/3/3 111 | """ 112 | -------------------------------------------------------------------------------- /tests/Encoding.elm: -------------------------------------------------------------------------------- 1 | module Encoding exposing 2 | ( compact 3 | , faces 4 | , multipart 5 | , options 6 | , points 7 | , polylines 8 | , texturedFaces 9 | , texturedTriangles 10 | , triangles 11 | ) 12 | 13 | import Array 14 | import Expect 15 | import Length exposing (Meters) 16 | import Obj.Encode as Encode exposing (Options, defaultOptions) 17 | import Point3d exposing (Point3d) 18 | import Polyline3d 19 | import Quantity exposing (Unitless) 20 | import Test exposing (Test) 21 | import TriangularMesh exposing (TriangularMesh) 22 | import Vector3d exposing (Vector3d) 23 | 24 | 25 | triangles : Test 26 | triangles = 27 | Test.describe "triangles" 28 | [ Test.test "correctly writes positions and indices" <| 29 | \_ -> 30 | Encode.encode Length.inMeters (Encode.triangles trianglesSquare) 31 | |> Expect.equal 32 | (String.concat 33 | [ "g\n" 34 | , "v -4.500000 4.500000 0.000000\n" 35 | , "v 4.500000 4.500000 0.000000\n" 36 | , "v 4.500000 -4.500000 0.000000\n" 37 | , "v -4.500000 -4.500000 0.000000\n" 38 | , "f 1 2 3\n" 39 | , "f 1 3 4\n" 40 | ] 41 | ) 42 | , Test.test "skips empty vertices" <| 43 | \_ -> 44 | Encode.encode Length.inMeters 45 | (Encode.triangles 46 | (TriangularMesh.indexed Array.empty [ ( 1, 2, 3 ) ]) 47 | ) 48 | |> Expect.equal "" 49 | , Test.test "skips empty indices" <| 50 | \_ -> 51 | Encode.encode Length.inMeters 52 | (Encode.triangles 53 | (TriangularMesh.indexed (Array.fromList [ Point3d.meters 1 2 3 ]) []) 54 | ) 55 | |> Expect.equal "" 56 | ] 57 | 58 | 59 | faces : Test 60 | faces = 61 | Test.describe "faces" 62 | [ Test.test "correctly writes positions, normal vectors and indices" <| 63 | \_ -> 64 | Encode.encode Length.inMeters (Encode.faces texturedFacesSquare) 65 | |> Expect.equal 66 | (String.concat 67 | [ "g\n" 68 | , "v -4.500000 4.500000 0.000000\n" 69 | , "v 4.500000 4.500000 0.000000\n" 70 | , "v 4.500000 -4.500000 0.000000\n" 71 | , "v -4.500000 -4.500000 0.000000\n" 72 | , "vn 0.000000 0.000000 1.000000\n" 73 | , "vn 0.000000 0.000000 1.000000\n" 74 | , "vn 0.000000 0.000000 1.000000\n" 75 | , "vn 0.000000 0.000000 1.000000\n" 76 | , "f 1//1 2//2 3//3\n" 77 | , "f 1//1 3//3 4//4\n" 78 | ] 79 | ) 80 | , Test.test "skips empty vertices" <| 81 | \_ -> 82 | Encode.encode Length.inMeters 83 | (Encode.faces (TriangularMesh.indexed Array.empty [ ( 1, 2, 3 ) ])) 84 | |> Expect.equal "" 85 | , Test.test "skips empty indices" <| 86 | \_ -> 87 | Encode.encode Length.inMeters 88 | (Encode.faces 89 | (TriangularMesh.indexed 90 | (Array.fromList 91 | [ { position = Point3d.meters -4.5 4.5 0 92 | , normal = Vector3d.unitless 0 0 1 93 | } 94 | ] 95 | ) 96 | [] 97 | ) 98 | ) 99 | |> Expect.equal "" 100 | ] 101 | 102 | 103 | texturedTriangles : Test 104 | texturedTriangles = 105 | Test.describe "texturedTriangles" 106 | [ Test.test "correctly writes positions, UV and indices" <| 107 | \_ -> 108 | Encode.encode Length.inMeters (Encode.texturedTriangles texturedFacesSquare) 109 | |> Expect.equal 110 | (String.concat 111 | [ "g\n" 112 | , "v -4.500000 4.500000 0.000000\n" 113 | , "v 4.500000 4.500000 0.000000\n" 114 | , "v 4.500000 -4.500000 0.000000\n" 115 | , "v -4.500000 -4.500000 0.000000\n" 116 | , "vt 0.000000 1.000000\n" 117 | , "vt 1.000000 1.000000\n" 118 | , "vt 1.000000 0.000000\n" 119 | , "vt 0.000000 0.000000\n" 120 | , "f 1/1 2/2 3/3\n" 121 | , "f 1/1 3/3 4/4\n" 122 | ] 123 | ) 124 | , Test.test "skips empty vertices" <| 125 | \_ -> 126 | Encode.encode Length.inMeters 127 | (Encode.texturedTriangles (TriangularMesh.indexed Array.empty [ ( 1, 2, 3 ) ])) 128 | |> Expect.equal "" 129 | , Test.test "skips empty indices" <| 130 | \_ -> 131 | Encode.encode Length.inMeters 132 | (Encode.texturedTriangles 133 | (TriangularMesh.indexed 134 | (Array.fromList 135 | [ { position = Point3d.meters -4.5 4.5 0 136 | , uv = ( 0, 1 ) 137 | } 138 | ] 139 | ) 140 | [] 141 | ) 142 | ) 143 | |> Expect.equal "" 144 | ] 145 | 146 | 147 | texturedFaces : Test 148 | texturedFaces = 149 | Test.describe "texturedFaces" 150 | [ Test.test "correctly writes positions, UV and indices" <| 151 | \_ -> 152 | Encode.encode Length.inCentimeters (Encode.texturedFaces texturedFacesSquare) 153 | |> Expect.equal 154 | (String.concat 155 | [ "g\n" 156 | , "v -450.000000 450.000000 0.000000\n" 157 | , "v 450.000000 450.000000 0.000000\n" 158 | , "v 450.000000 -450.000000 0.000000\n" 159 | , "v -450.000000 -450.000000 0.000000\n" 160 | , "vt 0.000000 1.000000\n" 161 | , "vt 1.000000 1.000000\n" 162 | , "vt 1.000000 0.000000\n" 163 | , "vt 0.000000 0.000000\n" 164 | , "vn 0.000000 0.000000 1.000000\n" 165 | , "vn 0.000000 0.000000 1.000000\n" 166 | , "vn 0.000000 0.000000 1.000000\n" 167 | , "vn 0.000000 0.000000 1.000000\n" 168 | , "f 1/1/1 2/2/2 3/3/3\n" 169 | , "f 1/1/1 3/3/3 4/4/4\n" 170 | ] 171 | ) 172 | , Test.test "skips empty vertices" <| 173 | \_ -> 174 | Encode.encode Length.inMeters 175 | (Encode.texturedFaces (TriangularMesh.indexed Array.empty [ ( 1, 2, 3 ) ])) 176 | |> Expect.equal "" 177 | , Test.test "skips empty indices" <| 178 | \_ -> 179 | Encode.encode Length.inMeters 180 | (Encode.texturedFaces 181 | (TriangularMesh.indexed 182 | (Array.fromList 183 | [ { position = Point3d.meters -4.5 4.5 0 184 | , uv = ( 0, 1 ) 185 | , normal = Vector3d.unitless 0 0 1 186 | } 187 | ] 188 | ) 189 | [] 190 | ) 191 | ) 192 | |> Expect.equal "" 193 | ] 194 | 195 | 196 | points : Test 197 | points = 198 | Test.describe "points" 199 | [ Test.test "correctly writes positions and indices" <| 200 | \_ -> 201 | Encode.encode Length.inMeters 202 | (Encode.points 203 | [ Point3d.meters -4.5 4.5 0 204 | , Point3d.meters 4.5 4.5 0 205 | , Point3d.meters 4.5 -4.5 0 206 | , Point3d.meters -4.5 -4.5 0 207 | ] 208 | ) 209 | |> Expect.equal 210 | (String.concat 211 | [ "g\n" 212 | , "v -4.500000 4.500000 0.000000\n" 213 | , "v 4.500000 4.500000 0.000000\n" 214 | , "v 4.500000 -4.500000 0.000000\n" 215 | , "v -4.500000 -4.500000 0.000000\n" 216 | , "p 1\n" 217 | , "p 2\n" 218 | , "p 3\n" 219 | , "p 4\n" 220 | ] 221 | ) 222 | , Test.test "skips empty points" <| 223 | \_ -> 224 | Encode.encode Length.inMeters 225 | (Encode.points []) 226 | |> Expect.equal "" 227 | ] 228 | 229 | 230 | polylines : Test 231 | polylines = 232 | Test.describe "polylines" 233 | [ Test.test "correctly writes positions and indices" <| 234 | \_ -> 235 | Encode.encode Length.inMeters 236 | (Encode.polylines 237 | [ Polyline3d.fromVertices 238 | [ Point3d.meters -4.5 4.5 0 239 | , Point3d.meters 4.5 4.5 0 240 | ] 241 | , Polyline3d.fromVertices 242 | [ Point3d.meters 4.5 -4.5 0 243 | , Point3d.meters -4.5 -4.5 0 244 | ] 245 | ] 246 | ) 247 | |> Expect.equal 248 | (String.concat 249 | [ "g\n" 250 | , "v -4.500000 4.500000 0.000000\n" 251 | , "v 4.500000 4.500000 0.000000\n" 252 | , "v 4.500000 -4.500000 0.000000\n" 253 | , "v -4.500000 -4.500000 0.000000\n" 254 | , "l 1 2\n" 255 | , "l 3 4\n" 256 | ] 257 | ) 258 | , Test.test "skips empty polylines" <| 259 | \_ -> 260 | Encode.encode Length.inMeters 261 | (Encode.polylines []) 262 | |> Expect.equal "" 263 | , Test.test "skips polylines without vertices" <| 264 | \_ -> 265 | Encode.encode Length.inMeters 266 | (Encode.polylines [ Polyline3d.fromVertices [] ]) 267 | |> Expect.equal "" 268 | ] 269 | 270 | 271 | options : Test 272 | options = 273 | Test.describe "options" 274 | [ Test.test "respects the precision option" <| 275 | \_ -> 276 | Encode.encode Length.inMeters (Encode.trianglesWith { defaultOptions | precision = 3 } precisionTriangle) 277 | |> Expect.equal 278 | (String.concat 279 | [ "g\n" 280 | , "v -6.000 6.000 0.123\n" 281 | , "v -0.123 -1.123 0.000\n" 282 | , "v 0.000 0.000 0.000\n" 283 | , "f 3 2 1\n" 284 | ] 285 | ) 286 | , Test.test "clamps the precision to 1" <| 287 | \_ -> 288 | Encode.encode Length.inMeters (Encode.trianglesWith { defaultOptions | precision = 0 } precisionTriangle) 289 | |> Expect.equal 290 | (String.concat 291 | [ "g\n" 292 | , "v -6.0 6.0 0.1\n" 293 | , "v -0.1 -1.1 0.0\n" 294 | , "v 0.0 0.0 0.0\n" 295 | , "f 3 2 1\n" 296 | ] 297 | ) 298 | , Test.test "clamps the precision to 10" <| 299 | \_ -> 300 | Encode.encode Length.inMeters (Encode.trianglesWith { defaultOptions | precision = 20 } precisionTriangle) 301 | |> Expect.equal 302 | (String.concat 303 | [ "g\n" 304 | , "v -5.9999999990 5.9999999990 0.1234567890\n" 305 | , "v -0.1234567890 -1.1234567890 0.0000000000\n" 306 | , "v 0.0000000000 0.0000000000 0.0000000001\n" 307 | , "f 3 2 1\n" 308 | ] 309 | ) 310 | , Test.test "sanitizes metadata" <| 311 | \_ -> 312 | Encode.encode Length.inMeters (Encode.pointsWith unsafeOptions [ Point3d.meters 1 2 3 ]) 313 | |> Expect.equal 314 | (String.concat 315 | [ "o ObjectName\n" 316 | , "g Group1 Group2\n" 317 | , "usemtl TestMaterial\n" 318 | , "v 1.000000 2.000000 3.000000\n" 319 | , "p 1\n" 320 | ] 321 | ) 322 | ] 323 | 324 | 325 | multipart : Test 326 | multipart = 327 | Test.describe "multipart" 328 | [ Test.test "correctly offsets indices for triangular meshes" <| 329 | \_ -> 330 | Encode.encodeMultipart Length.inMeters 331 | [ Encode.triangles trianglesSquare -- offsets positions 332 | , Encode.texturedTriangles texturedFacesSquare -- offsets positions and uvs 333 | , Encode.faces texturedFacesSquare -- offsets positions and normals 334 | , Encode.texturedFaces texturedFacesSquare 335 | ] 336 | |> Expect.equal 337 | (String.concat 338 | [ "g\n" 339 | , "v -4.500000 4.500000 0.000000\n" 340 | , "v 4.500000 4.500000 0.000000\n" 341 | , "v 4.500000 -4.500000 0.000000\n" 342 | , "v -4.500000 -4.500000 0.000000\n" 343 | , "f 1 2 3\n" 344 | , "f 1 3 4\n" 345 | , "g\n" 346 | , "v -4.500000 4.500000 0.000000\n" 347 | , "v 4.500000 4.500000 0.000000\n" 348 | , "v 4.500000 -4.500000 0.000000\n" 349 | , "v -4.500000 -4.500000 0.000000\n" 350 | , "vt 0.000000 1.000000\n" 351 | , "vt 1.000000 1.000000\n" 352 | , "vt 1.000000 0.000000\n" 353 | , "vt 0.000000 0.000000\n" 354 | 355 | -- positions + 4 356 | , "f 5/1 6/2 7/3\n" 357 | , "f 5/1 7/3 8/4\n" 358 | , "g\n" 359 | , "v -4.500000 4.500000 0.000000\n" 360 | , "v 4.500000 4.500000 0.000000\n" 361 | , "v 4.500000 -4.500000 0.000000\n" 362 | , "v -4.500000 -4.500000 0.000000\n" 363 | , "vn 0.000000 0.000000 1.000000\n" 364 | , "vn 0.000000 0.000000 1.000000\n" 365 | , "vn 0.000000 0.000000 1.000000\n" 366 | , "vn 0.000000 0.000000 1.000000\n" 367 | 368 | -- positions + 8, uvs + 4 369 | , "f 9//1 10//2 11//3\n" 370 | , "f 9//1 11//3 12//4\n" 371 | , "g\n" 372 | , "v -4.500000 4.500000 0.000000\n" 373 | , "v 4.500000 4.500000 0.000000\n" 374 | , "v 4.500000 -4.500000 0.000000\n" 375 | , "v -4.500000 -4.500000 0.000000\n" 376 | , "vt 0.000000 1.000000\n" 377 | , "vt 1.000000 1.000000\n" 378 | , "vt 1.000000 0.000000\n" 379 | , "vt 0.000000 0.000000\n" 380 | , "vn 0.000000 0.000000 1.000000\n" 381 | , "vn 0.000000 0.000000 1.000000\n" 382 | , "vn 0.000000 0.000000 1.000000\n" 383 | , "vn 0.000000 0.000000 1.000000\n" 384 | 385 | -- positions + 12, uvs + 4, normals + 4 386 | , "f 13/5/5 14/6/6 15/7/7\n" 387 | , "f 13/5/5 15/7/7 16/8/8\n" 388 | ] 389 | ) 390 | , Test.test "correctly offsets indices for lines and points" <| 391 | \_ -> 392 | Encode.encodeMultipart Length.inMeters 393 | [ Encode.polylines 394 | [ Polyline3d.fromVertices 395 | [ Point3d.meters -4.5 4.5 0 396 | , Point3d.meters 4.5 4.5 0 397 | ] 398 | , Polyline3d.fromVertices 399 | [ Point3d.meters 4.5 -4.5 0 400 | , Point3d.meters -4.5 -4.5 0 401 | ] 402 | ] 403 | , Encode.points [ Point3d.meters -4.5 4.5 0, Point3d.meters 4.5 -4.5 0 ] 404 | , Encode.points [ Point3d.meters 4.5 -4.5 0 ] 405 | ] 406 | |> Expect.equal 407 | (String.concat 408 | [ "g\n" 409 | , "v -4.500000 4.500000 0.000000\n" 410 | , "v 4.500000 4.500000 0.000000\n" 411 | , "v 4.500000 -4.500000 0.000000\n" 412 | , "v -4.500000 -4.500000 0.000000\n" 413 | , "l 1 2\n" 414 | , "l 3 4\n" 415 | , "g\n" 416 | , "v -4.500000 4.500000 0.000000\n" 417 | , "v 4.500000 -4.500000 0.000000\n" 418 | 419 | -- offset by 4 420 | , "p 5\n" 421 | , "p 6\n" 422 | , "g\n" 423 | , "v 4.500000 -4.500000 0.000000\n" 424 | 425 | -- offset by 4 + 2 = 6 426 | , "p 7\n" 427 | ] 428 | ) 429 | ] 430 | 431 | 432 | compact : Test 433 | compact = 434 | Test.describe "compact" 435 | [ Test.test "correctly reindexes triangles" <| 436 | \_ -> 437 | Encode.encodeCompact Length.inMeters 438 | [ Encode.triangles trianglesSquare ] 439 | |> Expect.equal 440 | (String.concat 441 | [ "g\n" 442 | , "v -4.500000 4.500000 0.000000\n" 443 | , "v 4.500000 4.500000 0.000000\n" 444 | , "v 4.500000 -4.500000 0.000000\n" 445 | , "v -4.500000 -4.500000 0.000000\n" 446 | , "f 1 2 3\n" 447 | , "f 1 3 4\n" 448 | ] 449 | ) 450 | , Test.test "correctly reindexes faces" <| 451 | \_ -> 452 | Encode.encodeCompact Length.inMeters 453 | [ Encode.faces suboptimalTexturedFacesSquare ] 454 | |> Expect.equal 455 | (String.concat 456 | [ "g\n" 457 | , "v -4.500000 4.500000 0.000000\n" 458 | , "v 4.500000 4.500000 0.000000\n" 459 | , "v 4.500000 -4.500000 0.000000\n" 460 | , "v -4.500000 -4.500000 0.000000\n" 461 | , "vn 0.000000 0.000000 1.000000\n" 462 | , "f 1//1 2//1 3//1\n" 463 | , "f 1//1 3//1 4//1\n" 464 | ] 465 | ) 466 | , Test.test "correctly reindexes texturedTriangles" <| 467 | \_ -> 468 | Encode.encodeCompact Length.inMeters 469 | [ Encode.texturedTriangles suboptimalTexturedFacesSquare ] 470 | |> Expect.equal 471 | (String.concat 472 | [ "g\n" 473 | , "v -4.500000 4.500000 0.000000\n" 474 | , "v 4.500000 4.500000 0.000000\n" 475 | , "v 4.500000 -4.500000 0.000000\n" 476 | , "v -4.500000 -4.500000 0.000000\n" 477 | , "vt 0.000000 1.000000\n" 478 | , "vt 1.000000 1.000000\n" 479 | , "vt 1.000000 0.000000\n" 480 | , "vt 0.000000 0.000000\n" 481 | , "f 1/1 2/2 3/3\n" 482 | , "f 1/1 3/3 4/4\n" 483 | ] 484 | ) 485 | , Test.test "correctly reindexes texturedFaces" <| 486 | \_ -> 487 | Encode.encodeCompact Length.inMeters 488 | [ Encode.texturedFaces suboptimalTexturedFacesSquare ] 489 | |> Expect.equal 490 | (String.concat 491 | [ "g\n" 492 | , "v -4.500000 4.500000 0.000000\n" 493 | , "v 4.500000 4.500000 0.000000\n" 494 | , "v 4.500000 -4.500000 0.000000\n" 495 | , "v -4.500000 -4.500000 0.000000\n" 496 | , "vt 0.000000 1.000000\n" 497 | , "vt 1.000000 1.000000\n" 498 | , "vt 1.000000 0.000000\n" 499 | , "vt 0.000000 0.000000\n" 500 | , "vn 0.000000 0.000000 1.000000\n" 501 | , "f 1/1/1 2/2/1 3/3/1\n" 502 | , "f 1/1/1 3/3/1 4/4/1\n" 503 | ] 504 | ) 505 | , Test.test "correctly reindexes lines" <| 506 | \_ -> 507 | Encode.encodeCompact Length.inMeters 508 | [ Encode.polylines 509 | [ Polyline3d.fromVertices 510 | [ Point3d.meters -4.5 4.5 0 511 | , Point3d.meters 4.5 4.5 0 512 | ] 513 | , Polyline3d.fromVertices 514 | [ Point3d.meters 4.5 -4.5 0 515 | , Point3d.meters -4.5 -4.5 0 516 | ] 517 | , Polyline3d.fromVertices 518 | [ Point3d.meters -4.5 4.5 0 519 | , Point3d.meters -4.5 -4.5 0 520 | ] 521 | ] 522 | ] 523 | |> Expect.equal 524 | (String.concat 525 | [ "g\n" 526 | , "v -4.500000 4.500000 0.000000\n" 527 | , "v 4.500000 4.500000 0.000000\n" 528 | , "v 4.500000 -4.500000 0.000000\n" 529 | , "v -4.500000 -4.500000 0.000000\n" 530 | , "l 1 2\n" 531 | , "l 3 4\n" 532 | , "l 1 4\n" 533 | ] 534 | ) 535 | , Test.test "correctly reindexes points" <| 536 | \_ -> 537 | Encode.encodeCompact Length.inMeters 538 | [ Encode.points 539 | [ Point3d.meters -4.5 4.5 0 540 | , Point3d.meters 4.5 4.5 0 541 | , Point3d.meters 4.5 -4.5 0 542 | , Point3d.meters -4.5 -4.5 0 543 | , Point3d.meters -4.5 4.5 0 544 | , Point3d.meters -4.5 -4.5 0 545 | ] 546 | ] 547 | |> Expect.equal 548 | (String.concat 549 | [ "g\n" 550 | , "v -4.500000 4.500000 0.000000\n" 551 | , "v 4.500000 4.500000 0.000000\n" 552 | , "v 4.500000 -4.500000 0.000000\n" 553 | , "v -4.500000 -4.500000 0.000000\n" 554 | , "p 1\n" 555 | , "p 2\n" 556 | , "p 3\n" 557 | , "p 4\n" 558 | , "p 1\n" 559 | , "p 4\n" 560 | ] 561 | ) 562 | , Test.test "skips empty polylines" <| 563 | \_ -> 564 | Encode.encodeCompact Length.inMeters 565 | [ Encode.polylines [] ] 566 | |> Expect.equal "" 567 | , Test.test "skips polylines without vertices" <| 568 | \_ -> 569 | Encode.encodeCompact Length.inMeters 570 | [ Encode.polylines [ Polyline3d.fromVertices [] ] ] 571 | |> Expect.equal "" 572 | , Test.test "skips empty meshes" <| 573 | \_ -> 574 | Encode.encodeCompact Length.inMeters 575 | [ Encode.triangles TriangularMesh.empty ] 576 | |> Expect.equal "" 577 | ] 578 | 579 | 580 | 581 | -- FIXTURES 582 | 583 | 584 | unsafeOptions : Options 585 | unsafeOptions = 586 | { precision = 6 587 | , object = Just "Object Name" 588 | , groups = [ "Group 1", "Group\n 2" ] 589 | , material = Just "Test Material" 590 | } 591 | 592 | 593 | trianglesSquare : TriangularMesh (Point3d Meters coords) 594 | trianglesSquare = 595 | let 596 | vertices = 597 | Array.fromList 598 | [ Point3d.meters -4.5 4.5 0 599 | , Point3d.meters 4.5 4.5 0 600 | , Point3d.meters 4.5 -4.5 0 601 | , Point3d.meters -4.5 -4.5 0 602 | ] 603 | 604 | faceIndices = 605 | [ ( 0, 1, 2 ), ( 0, 2, 3 ) ] 606 | in 607 | TriangularMesh.indexed vertices faceIndices 608 | 609 | 610 | texturedFacesSquare : TriangularMesh { position : Point3d Meters coords, normal : Vector3d Unitless coords, uv : ( Float, Float ) } 611 | texturedFacesSquare = 612 | let 613 | vertices = 614 | Array.fromList 615 | [ { position = Point3d.meters -4.5 4.5 0, uv = ( 0, 1 ), normal = Vector3d.unitless 0 0 1 } 616 | , { position = Point3d.meters 4.5 4.5 0, uv = ( 1, 1 ), normal = Vector3d.unitless 0 0 1 } 617 | , { position = Point3d.meters 4.5 -4.5 0, uv = ( 1, 0 ), normal = Vector3d.unitless 0 0 1 } 618 | , { position = Point3d.meters -4.5 -4.5 0, uv = ( 0, 0 ), normal = Vector3d.unitless 0 0 1 } 619 | ] 620 | 621 | faceIndices = 622 | [ ( 0, 1, 2 ), ( 0, 2, 3 ) ] 623 | in 624 | TriangularMesh.indexed vertices faceIndices 625 | 626 | 627 | suboptimalTexturedFacesSquare : TriangularMesh { position : Point3d Meters coords, normal : Vector3d Unitless coords, uv : ( Float, Float ) } 628 | suboptimalTexturedFacesSquare = 629 | let 630 | vertices = 631 | Array.fromList 632 | [ { position = Point3d.meters -4.5 4.5 0, uv = ( 0, 1 ), normal = Vector3d.unitless 0 0 1 } 633 | , { position = Point3d.meters 4.5 4.5 0, uv = ( 1, 1 ), normal = Vector3d.unitless 0 0 1 } 634 | , { position = Point3d.meters 4.5 -4.5 0, uv = ( 1, 0 ), normal = Vector3d.unitless 0 0 1 } 635 | , { position = Point3d.meters -4.5 4.5 0, uv = ( 0, 1 ), normal = Vector3d.unitless 0 0 1 } 636 | , { position = Point3d.meters 4.5 -4.5 0, uv = ( 1, 0 ), normal = Vector3d.unitless 0 0 1 } 637 | , { position = Point3d.meters -4.5 -4.5 0, uv = ( 0, 0 ), normal = Vector3d.unitless 0 0 1 } 638 | ] 639 | 640 | faceIndices = 641 | [ ( 0, 1, 2 ), ( 3, 4, 5 ) ] 642 | in 643 | TriangularMesh.indexed vertices faceIndices 644 | 645 | 646 | precisionTriangle : TriangularMesh (Point3d Meters coords) 647 | precisionTriangle = 648 | let 649 | vertices = 650 | Array.fromList 651 | [ Point3d.meters -5.999999999 5.999999999 0.123456789 652 | , Point3d.meters -0.123456789 -1.123456789 0 653 | , Point3d.meters (0 / 0) (1 / 0) 1.23456789e-10 654 | ] 655 | in 656 | TriangularMesh.indexed vertices [ ( 2, 1, 0 ) ] 657 | -------------------------------------------------------------------------------- /tests/MetadataAndFiltering.elm: -------------------------------------------------------------------------------- 1 | module MetadataAndFiltering exposing (defaultGroup, filter, group, groupNames, material, materialNames, object, objectNames) 2 | 3 | import Array 4 | import Expect 5 | import Length 6 | import Obj.Decode as Decode 7 | import Test exposing (Test) 8 | import TriangularMesh 9 | 10 | 11 | objectNames : Test 12 | objectNames = 13 | Test.describe "objectNames" 14 | [ Test.test "returns a list of object names from the file" <| 15 | \_ -> 16 | objFile 17 | |> Decode.decodeString Length.centimeters Decode.objectNames 18 | |> Expect.equal (Ok [ "Cube", "Lines" ]) 19 | , Test.test "returns an empty list when no objects were found in a file" <| 20 | \_ -> 21 | "" 22 | |> Decode.decodeString Length.centimeters Decode.objectNames 23 | |> Expect.equal (Ok []) 24 | ] 25 | 26 | 27 | materialNames : Test 28 | materialNames = 29 | Test.describe "materialNames" 30 | [ Test.test "returns a list of material names from the file" <| 31 | \_ -> 32 | objFile 33 | |> Decode.decodeString Length.centimeters Decode.materialNames 34 | |> Expect.equal (Ok [ "Material1", "Material2" ]) 35 | , Test.test "returns an empty list when no materials were found in a file" <| 36 | \_ -> 37 | "" 38 | |> Decode.decodeString Length.centimeters Decode.materialNames 39 | |> Expect.equal (Ok []) 40 | ] 41 | 42 | 43 | groupNames : Test 44 | groupNames = 45 | Test.describe "groupNames" 46 | [ Test.test "returns a list of group names from the file" <| 47 | \_ -> 48 | objFile 49 | |> Decode.decodeString Length.centimeters Decode.groupNames 50 | |> Expect.equal (Ok [ "Face2", "Face3", "Face4", "Face5", "Face6", "Faces", "default" ]) 51 | , Test.test "returns an empty list when no groups were found in a file" <| 52 | \_ -> 53 | "" 54 | |> Decode.decodeString Length.centimeters Decode.groupNames 55 | |> Expect.equal (Ok []) 56 | ] 57 | 58 | 59 | object : Test 60 | object = 61 | Test.describe "object" 62 | [ Test.test "decodes material names for a certain object" <| 63 | \_ -> 64 | objFile 65 | |> Decode.decodeString Length.centimeters (Decode.object "Cube" Decode.materialNames) 66 | |> Expect.equal (Ok [ "Material1" ]) 67 | , Test.test "the not found error contains object name" <| 68 | \_ -> 69 | objFile 70 | |> Decode.decodeString Length.centimeters (Decode.object "Lines" Decode.triangles) 71 | |> Expect.equal (Err "No faces found for object 'Lines'") 72 | ] 73 | 74 | 75 | material : Test 76 | material = 77 | Test.describe "material" 78 | [ Test.test "decodes object names for a certain material" <| 79 | \_ -> 80 | objFile 81 | |> Decode.decodeString Length.centimeters (Decode.material "Material2" Decode.objectNames) 82 | |> Expect.equal (Ok [ "Lines" ]) 83 | , Test.test "the not found error contains the material name" <| 84 | \_ -> 85 | objFile 86 | |> Decode.decodeString Length.centimeters (Decode.material "Material1" Decode.polylines) 87 | |> Expect.equal (Err "No lines found for material 'Material1'") 88 | ] 89 | 90 | 91 | defaultGroup : Test 92 | defaultGroup = 93 | Test.describe "defaultGroup" 94 | [ Test.test "decodes triangles in the default group" <| 95 | \_ -> 96 | objFile 97 | |> Decode.decodeString Length.centimeters (Decode.defaultGroup Decode.triangles) 98 | |> Result.map 99 | (\triangularMesh -> 100 | ( Array.length (TriangularMesh.vertices triangularMesh) 101 | , List.length (TriangularMesh.faceIndices triangularMesh) 102 | ) 103 | ) 104 | -- the default group has 4 vertices and 2 triangles 105 | |> Expect.equal (Ok ( 4, 2 )) 106 | , Test.test "the not found error contains the default group name" <| 107 | \_ -> 108 | objFile 109 | |> Decode.decodeString Length.centimeters (Decode.defaultGroup Decode.polylines) 110 | |> Expect.equal (Err "No lines found for group 'default'") 111 | ] 112 | 113 | 114 | group : Test 115 | group = 116 | Test.describe "group" 117 | [ Test.test "decodes triangles in a group" <| 118 | \_ -> 119 | objFile 120 | |> Decode.decodeString Length.centimeters (Decode.group "Faces" Decode.triangles) 121 | |> Result.map 122 | (\triangularMesh -> 123 | ( Array.length (TriangularMesh.vertices triangularMesh) 124 | , List.length (TriangularMesh.faceIndices triangularMesh) 125 | ) 126 | ) 127 | |> Expect.equal (Ok ( 8, 10 )) 128 | , Test.test "the not found error contains the group name" <| 129 | \_ -> 130 | objFile 131 | |> Decode.decodeString Length.centimeters (Decode.group "Bla" Decode.faces) 132 | |> Expect.equal (Err "No faces found for group 'Bla'") 133 | ] 134 | 135 | 136 | filter : Test 137 | filter = 138 | Test.describe "filter" 139 | [ Test.test "decodes triangles in selected groups" <| 140 | \_ -> 141 | objFile 142 | |> Decode.decodeString Length.centimeters 143 | (Decode.filter 144 | (\{ groups } -> List.member "Face2" groups || List.member "Face3" groups) 145 | Decode.triangles 146 | ) 147 | |> Result.map 148 | (\triangularMesh -> 149 | ( Array.length (TriangularMesh.vertices triangularMesh) 150 | , List.length (TriangularMesh.faceIndices triangularMesh) 151 | ) 152 | ) 153 | |> Expect.equal (Ok ( 6, 4 )) 154 | , Test.test "the not found error contains " <| 155 | \_ -> 156 | objFile 157 | |> Decode.decodeString Length.centimeters (Decode.filter (\_ -> False) Decode.faces) 158 | |> Expect.equal (Err "No faces found for ") 159 | ] 160 | 161 | 162 | objFile : String 163 | objFile = 164 | """# Blender v2.80 (sub 75) OBJ File: '' 165 | # www.blender.org 166 | mtllib untitled.mtl 167 | o Cube 168 | v 1.000000 1.000000 -1.000000 169 | v 1.000000 -1.000000 -1.000000 170 | v 1.000000 1.000000 1.000000 171 | v 1.000000 -1.000000 1.000000 172 | v -1.000000 1.000000 -1.000000 173 | v -1.000000 -1.000000 -1.000000 174 | v -1.000000 1.000000 1.000000 175 | v -1.000000 -1.000000 1.000000 176 | v -1.260743 1.051649 1.526675 177 | v -1.090024 1.399034 0.711974 178 | v -0.949402 2.107505 -0.479652 179 | vt 0.375000 0.000000 180 | vt 0.625000 0.000000 181 | vt 0.625000 0.250000 182 | vt 0.375000 0.250000 183 | vt 0.375000 0.250000 184 | vt 0.625000 0.250000 185 | vt 0.625000 0.500000 186 | vt 0.375000 0.500000 187 | vt 0.625000 0.750000 188 | vt 0.375000 0.750000 189 | vt 0.625000 0.750000 190 | vt 0.625000 1.000000 191 | vt 0.375000 1.000000 192 | vt 0.125000 0.500000 193 | vt 0.375000 0.500000 194 | vt 0.375000 0.750000 195 | vt 0.125000 0.750000 196 | vt 0.625000 0.500000 197 | vt 0.875000 0.500000 198 | vt 0.875000 0.750000 199 | vn 0.0000 1.0000 0.0000 200 | vn 0.0000 0.0000 1.0000 201 | vn -1.0000 0.0000 0.0000 202 | vn 0.0000 -1.0000 0.0000 203 | vn 1.0000 0.0000 0.0000 204 | vn 0.0000 0.0000 -1.0000 205 | usemtl Material1 206 | f 1/1/1 5/2/1 7/3/1 3/4/1 207 | g Faces Face2 208 | f 4/5/2 3/6/2 7/7/2 8/8/2 209 | g Faces Face3 210 | f 8/8/3 7/7/3 5/9/3 6/10/3 211 | g Faces Face4 212 | f 6/10/4 2/11/4 4/12/4 8/13/4 213 | g Faces Face5 214 | f 2/14/5 1/15/5 3/16/5 4/17/5 215 | g Faces Face6 216 | f 6/18/6 5/19/6 1/20/6 2/11/6 217 | o Lines 218 | usemtl Material2 219 | l 9 10 220 | l 1 2 3""" 221 | -------------------------------------------------------------------------------- /tests/Parsing.elm: -------------------------------------------------------------------------------- 1 | module Parsing exposing (parsing) 2 | 3 | import Expect 4 | import Length 5 | import Obj.Decode as Decode 6 | import Test exposing (Test) 7 | 8 | 9 | parsing : Test 10 | parsing = 11 | Test.describe "parsing" 12 | [ Test.test "passes for an empty string" <| 13 | \_ -> 14 | "" 15 | |> Decode.decodeString Length.centimeters (Decode.succeed "success") 16 | |> Expect.equal (Ok "success") 17 | , Test.test "skips comments" <| 18 | \_ -> 19 | "# this is a comment" 20 | |> Decode.decodeString Length.centimeters (Decode.succeed "success") 21 | |> Expect.equal (Ok "success") 22 | , Test.test "passes for valid format" <| 23 | \_ -> 24 | objFile defaults 25 | |> Decode.decodeString Length.centimeters (Decode.succeed "success") 26 | |> Expect.equal (Ok "success") 27 | , Test.test "fails for invalid syntax" <| 28 | \_ -> 29 | "%PDF-1.2" 30 | |> Decode.decodeString Length.centimeters (Decode.succeed "success") 31 | |> Expect.equal (Err "Line 1: Invalid OBJ syntax '%PDF-1.2'") 32 | , Test.test "fails for a missing object name" <| 33 | \_ -> 34 | objFile { defaults | objectName = "" } 35 | |> Decode.decodeString Length.centimeters (Decode.succeed "success") 36 | |> Expect.equal (Err "Line 1: No object name") 37 | , Test.test "passes for missing group names" <| 38 | \_ -> 39 | objFile { defaults | groupNames = "" } 40 | |> Decode.decodeString Length.centimeters (Decode.succeed "success") 41 | |> Expect.equal (Ok "success") 42 | , Test.test "fails for a missing material name" <| 43 | \_ -> 44 | objFile { defaults | materialName = "" } 45 | |> Decode.decodeString Length.centimeters (Decode.succeed "success") 46 | |> Expect.equal (Err "Line 12: No material name") 47 | , Test.test "passes for position with weight" <| 48 | \_ -> 49 | objFile { defaults | position = "-0.126193 0.126193 0.126193 1.000000" } 50 | |> Decode.decodeString Length.centimeters (Decode.succeed "success") 51 | |> Expect.equal (Ok "success") 52 | , Test.test "fails for invalid position format" <| 53 | \_ -> 54 | objFile { defaults | position = "-0.126193 0.126193" } 55 | |> Decode.decodeString Length.centimeters (Decode.succeed "success") 56 | |> Expect.equal (Err "Line 3: Invalid position format") 57 | , Test.test "passes for texture coordinates with depth" <| 58 | \_ -> 59 | objFile { defaults | textureCoordinates = "0.128224 0.902035 0.000000" } 60 | |> Decode.decodeString Length.centimeters (Decode.succeed "success") 61 | |> Expect.equal (Ok "success") 62 | , Test.test "passes for texture coordinates with just u" <| 63 | \_ -> 64 | objFile { defaults | textureCoordinates = "-0.126193" } 65 | |> Decode.decodeString Length.centimeters (Decode.succeed "success") 66 | |> Expect.equal (Ok "success") 67 | , Test.test "fails for invalid texture coordinates format" <| 68 | \_ -> 69 | objFile { defaults | textureCoordinates = "bla" } 70 | |> Decode.decodeString Length.centimeters (Decode.succeed "success") 71 | |> Expect.equal (Err "Line 6: Invalid texture coordinates format") 72 | , Test.test "fails for invalid normal vector format" <| 73 | \_ -> 74 | objFile { defaults | normal = "-0.126193" } 75 | |> Decode.decodeString Length.centimeters (Decode.succeed "success") 76 | |> Expect.equal (Err "Line 9: Invalid normal vector format") 77 | , Test.test "fails for invalid face format" <| 78 | \_ -> 79 | objFile { defaults | faceIndices = "invalid" } 80 | |> Decode.decodeString Length.centimeters (Decode.succeed "success") 81 | |> Expect.equal (Err "Line 14: Invalid face format") 82 | , Test.test "fails for a face with no vertices" <| 83 | \_ -> 84 | objFile { defaults | faceIndices = "" } 85 | |> Decode.decodeString Length.centimeters (Decode.succeed "success") 86 | |> Expect.equal (Err "Line 14: Face has less than three vertices") 87 | , Test.test "fails for a face with less than three vertices" <| 88 | \_ -> 89 | objFile { defaults | faceIndices = "1/1/1 2/2/2" } 90 | |> Decode.decodeString Length.centimeters (Decode.succeed "success") 91 | |> Expect.equal (Err "Line 14: Face has less than three vertices") 92 | , Test.test "fails for invalid line format" <| 93 | \_ -> 94 | objFile { defaults | lineIndices = "invalid" } 95 | |> Decode.decodeString Length.centimeters (Decode.succeed "success") 96 | |> Expect.equal (Err "Line 15: Invalid line format") 97 | , Test.test "fails for a line with no vertices" <| 98 | \_ -> 99 | objFile { defaults | lineIndices = "" } 100 | |> Decode.decodeString Length.centimeters (Decode.succeed "success") 101 | |> Expect.equal (Err "Line 15: Line has less than two vertices") 102 | , Test.test "fails for a line with less than two points" <| 103 | \_ -> 104 | objFile { defaults | lineIndices = "1" } 105 | |> Decode.decodeString Length.centimeters (Decode.succeed "success") 106 | |> Expect.equal (Err "Line 15: Line has less than two vertices") 107 | , Test.test "fails for invalid points format" <| 108 | \_ -> 109 | objFile { defaults | pointsIndices = "invalid" } 110 | |> Decode.decodeString Length.centimeters (Decode.succeed "success") 111 | |> Expect.equal (Err "Line 16: Invalid points format") 112 | , Test.test "fails for points with no vertices" <| 113 | \_ -> 114 | objFile { defaults | pointsIndices = "" } 115 | |> Decode.decodeString Length.centimeters (Decode.succeed "success") 116 | |> Expect.equal (Err "Line 16: Points element has no vertices") 117 | ] 118 | 119 | 120 | type alias Settings = 121 | { objectName : String 122 | , groupNames : String 123 | , materialName : String 124 | , position : String 125 | , textureCoordinates : String 126 | , normal : String 127 | , faceIndices : String 128 | , lineIndices : String 129 | , pointsIndices : String 130 | } 131 | 132 | 133 | defaults : Settings 134 | defaults = 135 | { objectName = "Cube" 136 | , groupNames = "Test" 137 | , materialName = "Material" 138 | , position = "-0.126193 0.126193 0.126193" 139 | , textureCoordinates = "0.128224 0.902035" 140 | , normal = "-1.0000 0.0000 0.0000" 141 | , faceIndices = "1/1/1 2/2/2 3/3/3" 142 | , lineIndices = "1/1 2/2 3/3" 143 | , pointsIndices = "1 2 3" 144 | } 145 | 146 | 147 | objFile : Settings -> String 148 | objFile { objectName, groupNames, materialName, position, textureCoordinates, normal, faceIndices, lineIndices, pointsIndices } = 149 | String.join "\n" 150 | [ "o " ++ objectName 151 | , "v -0.126193 0.126193 -0.126193" 152 | , "v " ++ position 153 | , "v -0.126193 -0.126193 -0.126193" 154 | , "vt 0.106151 0.902035" 155 | , "vt " ++ textureCoordinates 156 | , "vt 0.128224 0.913071" 157 | , "vn 0.0000 -1.0000 0.0000" 158 | , "vn " ++ normal 159 | , "vn 0.0000 0.0000 -1.0000" 160 | , "g " ++ groupNames 161 | , "usemtl " ++ materialName 162 | , "s off" 163 | , "f " ++ faceIndices 164 | , "l " ++ lineIndices 165 | , "p " ++ pointsIndices 166 | ] 167 | -------------------------------------------------------------------------------- /tests/Primitives.elm: -------------------------------------------------------------------------------- 1 | module Primitives exposing 2 | ( faces 3 | , points 4 | , polylines 5 | , texturedFaces 6 | , texturedTriangles 7 | , triangles 8 | , trianglesIn 9 | ) 10 | 11 | import Angle 12 | import Array 13 | import Axis3d 14 | import Expect 15 | import Frame3d exposing (Frame3d) 16 | import Length exposing (Meters) 17 | import Obj.Decode as Decode exposing (ObjCoordinates) 18 | import Point3d 19 | import Polyline3d 20 | import Test exposing (Test) 21 | import TriangularMesh 22 | 23 | 24 | type ZUpCoords 25 | = ZUpCoords 26 | 27 | 28 | yUpToZUpFrame : Frame3d Meters ZUpCoords { defines : ObjCoordinates } 29 | yUpToZUpFrame = 30 | Frame3d.rotateAround Axis3d.x (Angle.degrees 90) Frame3d.atOrigin 31 | 32 | 33 | trianglesIn : Test 34 | trianglesIn = 35 | Test.describe "trianglesIn" 36 | [ Test.test "transforms coordinates when decoding" <| 37 | \_ -> 38 | let 39 | yUpResult : Result String (TriangularMesh.TriangularMesh (Point3d.Point3d Meters ObjCoordinates)) 40 | yUpResult = 41 | Decode.decodeString Length.centimeters Decode.triangles objFile 42 | 43 | zUpResult : Result String (TriangularMesh.TriangularMesh (Point3d.Point3d Meters ZUpCoords)) 44 | zUpResult = 45 | Decode.decodeString Length.centimeters (Decode.trianglesIn yUpToZUpFrame) objFile 46 | in 47 | Result.map2 48 | (\yUpMesh zUpMesh -> 49 | let 50 | maybeY = 51 | TriangularMesh.vertex 0 yUpMesh 52 | |> Maybe.map (Point3d.toMeters >> .y) 53 | 54 | maybeZ = 55 | TriangularMesh.vertex 0 zUpMesh 56 | |> Maybe.map (Point3d.toMeters >> .z) 57 | in 58 | case ( maybeY, maybeZ ) of 59 | ( Just y, Just z ) -> 60 | Expect.within (Expect.Absolute 0.0001) y z 61 | 62 | _ -> 63 | Expect.fail "Missing vertices" 64 | ) 65 | yUpResult 66 | zUpResult 67 | |> Result.withDefault (Expect.fail "Failed decoding") 68 | ] 69 | 70 | 71 | triangles : Test 72 | triangles = 73 | Test.describe "triangles" 74 | [ Test.test "extracts all the vertices and indices" <| 75 | \_ -> 76 | objFile 77 | |> Decode.decodeString Length.centimeters Decode.triangles 78 | |> Result.map 79 | (\triangularMesh -> 80 | ( Array.length (TriangularMesh.vertices triangularMesh) 81 | , List.length (TriangularMesh.faceIndices triangularMesh) 82 | ) 83 | ) 84 | |> Expect.equal (Ok ( 8, 12 )) 85 | , Test.test "fails when the position index is out of range" <| 86 | \_ -> 87 | (objFile ++ "\nf 500/18/6 5/19/6 1/20/6 2/11/6") 88 | |> Decode.decodeString Length.centimeters Decode.triangles 89 | |> Expect.equal (Err "Line 54: Index out of range") 90 | , Test.test "fails when no faces were found" <| 91 | \_ -> 92 | "" 93 | |> Decode.decodeString Length.centimeters Decode.triangles 94 | |> Expect.equal (Err "No faces found") 95 | , Test.test "fails when no faces match specific filtering criteria" <| 96 | \_ -> 97 | "" 98 | |> Decode.decodeString Length.centimeters (Decode.object "Cube" (Decode.defaultGroup Decode.triangles)) 99 | |> Expect.equal (Err "No faces found for group 'default', object 'Cube'") 100 | ] 101 | 102 | 103 | faces : Test 104 | faces = 105 | Test.describe "faces" 106 | [ Test.test "extracts all the vertices and indices" <| 107 | \_ -> 108 | objFile 109 | |> Decode.decodeString Length.centimeters Decode.faces 110 | |> Result.map 111 | (\triangularMesh -> 112 | ( Array.length (TriangularMesh.vertices triangularMesh) 113 | , List.length (TriangularMesh.faceIndices triangularMesh) 114 | ) 115 | ) 116 | |> Expect.equal (Ok ( 24, 12 )) 117 | , Test.test "errors when the position index is out of range" <| 118 | \_ -> 119 | (objFile ++ "\nf 500/18/6 5/19/6 1/20/6 2/11/6") 120 | |> Decode.decodeString Length.centimeters Decode.faces 121 | |> Expect.equal (Err "Line 54: Index out of range") 122 | , Test.test "errors when the normal index is out of range" <| 123 | \_ -> 124 | (objFile ++ "\nf 6/18/500 5/19/6 1/20/6 2/11/6") 125 | |> Decode.decodeString Length.centimeters Decode.faces 126 | |> Expect.equal (Err "Line 54: Index out of range") 127 | , Test.test "errors when the normal index is missing" <| 128 | \_ -> 129 | (objFile ++ "\nf 6/18 5/19 1/20 2/11") 130 | |> Decode.decodeString Length.centimeters Decode.faces 131 | |> Expect.equal (Err "Line 54: Vertex has no normal vector") 132 | , Test.test "errors when no faces were found" <| 133 | \_ -> 134 | "" 135 | |> Decode.decodeString Length.centimeters Decode.faces 136 | |> Expect.equal (Err "No faces found") 137 | , Test.test "errors when no faces were found matching specific filtering criteria" <| 138 | \_ -> 139 | "" 140 | |> Decode.decodeString Length.centimeters (Decode.object "Cube" (Decode.defaultGroup Decode.faces)) 141 | |> Expect.equal (Err "No faces found for group 'default', object 'Cube'") 142 | ] 143 | 144 | 145 | texturedTriangles : Test 146 | texturedTriangles = 147 | Test.describe "texturedTriangles" 148 | [ Test.test "extracts all the vertices and indices" <| 149 | \_ -> 150 | objFile 151 | |> Decode.decodeString Length.centimeters Decode.texturedTriangles 152 | |> Result.map 153 | (\triangularMesh -> 154 | ( Array.length (TriangularMesh.vertices triangularMesh) 155 | , List.length (TriangularMesh.faceIndices triangularMesh) 156 | ) 157 | ) 158 | |> Expect.equal (Ok ( 20, 12 )) 159 | , Test.test "errors when the position index is out of range" <| 160 | \_ -> 161 | (objFile ++ "\nf 500/18/6 5/19/6 1/20/6 2/11/6") 162 | |> Decode.decodeString Length.centimeters Decode.texturedTriangles 163 | |> Expect.equal (Err "Line 54: Index out of range") 164 | , Test.test "errors when the uv index is out of range" <| 165 | \_ -> 166 | (objFile ++ "\nf 6/500/6 5/19/6 1/20/6 2/11/6") 167 | |> Decode.decodeString Length.centimeters Decode.texturedTriangles 168 | |> Expect.equal (Err "Line 54: Index out of range") 169 | , Test.test "errors when the uv index is missing" <| 170 | \_ -> 171 | (objFile ++ "\nf 6//6 5//6 1//6 2//6") 172 | |> Decode.decodeString Length.centimeters Decode.texturedTriangles 173 | |> Expect.equal (Err "Line 54: Vertex has no texture coordinates") 174 | , Test.test "errors when no faces were found" <| 175 | \_ -> 176 | "" 177 | |> Decode.decodeString Length.centimeters Decode.texturedTriangles 178 | |> Expect.equal (Err "No faces found") 179 | , Test.test "errors when no faces were found matching specific filtering criteria" <| 180 | \_ -> 181 | "" 182 | |> Decode.decodeString Length.centimeters (Decode.object "Cube" (Decode.defaultGroup Decode.texturedTriangles)) 183 | |> Expect.equal (Err "No faces found for group 'default', object 'Cube'") 184 | ] 185 | 186 | 187 | texturedFaces : Test 188 | texturedFaces = 189 | Test.describe "texturedFaces" 190 | [ Test.test "extracts all the vertices and indices" <| 191 | \_ -> 192 | objFile 193 | |> Decode.decodeString Length.centimeters Decode.texturedFaces 194 | |> Result.map 195 | (\triangularMesh -> 196 | ( Array.length (TriangularMesh.vertices triangularMesh) 197 | , List.length (TriangularMesh.faceIndices triangularMesh) 198 | ) 199 | ) 200 | |> Expect.equal (Ok ( 24, 12 )) 201 | , Test.test "errors when the position index is out of range" <| 202 | \_ -> 203 | (objFile ++ "\nf 500/18/6 5/19/6 1/20/6 2/11/6") 204 | |> Decode.decodeString Length.centimeters Decode.texturedFaces 205 | |> Expect.equal (Err "Line 54: Index out of range") 206 | , Test.test "errors when the normal index is out of range" <| 207 | \_ -> 208 | (objFile ++ "\nf 6/18/500 5/19/6 1/20/6 2/11/6") 209 | |> Decode.decodeString Length.centimeters Decode.texturedFaces 210 | |> Expect.equal (Err "Line 54: Index out of range") 211 | , Test.test "errors when the normal index is missing" <| 212 | \_ -> 213 | (objFile ++ "\nf 6/18 5/19 1/20 2/11") 214 | |> Decode.decodeString Length.centimeters Decode.texturedFaces 215 | |> Expect.equal (Err "Line 54: Vertex missing normal vector and/or texture coordinates") 216 | , Test.test "errors when the uv index is out of range" <| 217 | \_ -> 218 | (objFile ++ "\nf 6/500/6 5/19/6 1/20/6 2/11/6") 219 | |> Decode.decodeString Length.centimeters Decode.texturedFaces 220 | |> Expect.equal (Err "Line 54: Index out of range") 221 | , Test.test "errors when the uv index is missing" <| 222 | \_ -> 223 | (objFile ++ "\nf 6//6 5//6 1//6 2//6") 224 | |> Decode.decodeString Length.centimeters Decode.texturedFaces 225 | |> Expect.equal (Err "Line 54: Vertex missing normal vector and/or texture coordinates") 226 | , Test.test "errors when no faces were found" <| 227 | \_ -> 228 | "" 229 | |> Decode.decodeString Length.centimeters Decode.texturedFaces 230 | |> Expect.equal (Err "No faces found") 231 | , Test.test "errors when no faces were found matching specific filtering criteria" <| 232 | \_ -> 233 | "" 234 | |> Decode.decodeString Length.centimeters (Decode.object "Cube" (Decode.defaultGroup Decode.texturedFaces)) 235 | |> Expect.equal (Err "No faces found for group 'default', object 'Cube'") 236 | ] 237 | 238 | 239 | polylines : Test 240 | polylines = 241 | Test.describe "polylines" 242 | [ Test.test "extracts all points" <| 243 | \_ -> 244 | objFile 245 | |> Decode.decodeString Length.centimeters Decode.polylines 246 | |> Expect.equal 247 | (Ok 248 | [ Polyline3d.fromVertices 249 | [ Point3d.centimeters -1.260743 1.051649 1.526675 250 | , Point3d.centimeters -1.090024 1.399034 0.711974 251 | ] 252 | , Polyline3d.fromVertices 253 | [ Point3d.centimeters 1.0 1.0 -1.0 254 | , Point3d.centimeters 1.0 -1.0 -1.0 255 | , Point3d.centimeters 1.0 1.0 1.0 256 | ] 257 | ] 258 | ) 259 | , Test.test "errors when the position index is out of range" <| 260 | \_ -> 261 | (objFile ++ "\nl 500 5 1 2") 262 | |> Decode.decodeString Length.centimeters Decode.polylines 263 | |> Expect.equal (Err "Line 54: Index out of range") 264 | , Test.test "errors when no lines were found" <| 265 | \_ -> 266 | "" 267 | |> Decode.decodeString Length.centimeters Decode.polylines 268 | |> Expect.equal (Err "No lines found") 269 | , Test.test "errors when no lines were found matching specific filtering criteria" <| 270 | \_ -> 271 | "" 272 | |> Decode.decodeString Length.centimeters (Decode.object "Cube" (Decode.defaultGroup Decode.polylines)) 273 | |> Expect.equal (Err "No lines found for group 'default', object 'Cube'") 274 | ] 275 | 276 | 277 | points : Test 278 | points = 279 | Test.describe "points" 280 | [ Test.test "extracts all points" <| 281 | \_ -> 282 | objFile 283 | |> Decode.decodeString Length.centimeters Decode.points 284 | |> Expect.equal 285 | (Ok 286 | [ Point3d.centimeters -1.260743 1.051649 1.526675 287 | , Point3d.centimeters -1.090024 1.399034 0.711974 288 | , Point3d.centimeters 1.0 1.0 -1.0 289 | ] 290 | ) 291 | , Test.test "errors when the position index is out of range" <| 292 | \_ -> 293 | (objFile ++ "\np 500 5 1 2") 294 | |> Decode.decodeString Length.centimeters Decode.points 295 | |> Expect.equal (Err "Line 54: Index out of range") 296 | , Test.test "errors when no points were found" <| 297 | \_ -> 298 | "" 299 | |> Decode.decodeString Length.centimeters Decode.points 300 | |> Expect.equal (Err "No points found") 301 | , Test.test "errors when no points were found matching specific filtering criteria" <| 302 | \_ -> 303 | "" 304 | |> Decode.decodeString Length.centimeters (Decode.object "Cube" (Decode.defaultGroup Decode.points)) 305 | |> Expect.equal (Err "No points found for group 'default', object 'Cube'") 306 | ] 307 | 308 | 309 | objFile : String 310 | objFile = 311 | """# Blender v2.80 (sub 75) OBJ File: '' 312 | # www.blender.org 313 | mtllib untitled.mtl 314 | o Cube 315 | v 1.000000 1.000000 -1.000000 316 | v 1.000000 -1.000000 -1.000000 317 | v 1.000000 1.000000 1.000000 318 | v 1.000000 -1.000000 1.000000 319 | v -1.000000 1.000000 -1.000000 320 | v -1.000000 -1.000000 -1.000000 321 | v -1.000000 1.000000 1.000000 322 | v -1.000000 -1.000000 1.000000 323 | v -1.260743 1.051649 1.526675 324 | v -1.090024 1.399034 0.711974 325 | v -0.949402 2.107505 -0.479652 326 | vt 0.375000 0.000000 327 | vt 0.625000 0.000000 328 | vt 0.625000 0.250000 329 | vt 0.375000 0.250000 330 | vt 0.375000 0.250000 331 | vt 0.625000 0.250000 332 | vt 0.625000 0.500000 333 | vt 0.375000 0.500000 334 | vt 0.625000 0.750000 335 | vt 0.375000 0.750000 336 | vt 0.625000 0.750000 337 | vt 0.625000 1.000000 338 | vt 0.375000 1.000000 339 | vt 0.125000 0.500000 340 | vt 0.375000 0.500000 341 | vt 0.375000 0.750000 342 | vt 0.125000 0.750000 343 | vt 0.625000 0.500000 344 | vt 0.875000 0.500000 345 | vt 0.875000 0.750000 346 | vn 0.0000 1.0000 0.0000 347 | vn 0.0000 0.0000 1.0000 348 | vn -1.0000 0.0000 0.0000 349 | vn 0.0000 -1.0000 0.0000 350 | vn 1.0000 0.0000 0.0000 351 | vn 0.0000 0.0000 -1.0000 352 | usemtl Material 353 | s off 354 | f 1/1/1 5/2/1 7/3/1 3/4/1 355 | f 4/5/2 3/6/2 7/7/2 8/8/2 356 | f 8/8/3 7/7/3 5/9/3 6/10/3 357 | f 6/10/4 2/11/4 4/12/4 8/13/4 358 | f 2/14/5 1/15/5 3/16/5 4/17/5 359 | f 6/18/6 5/19/6 1/20/6 2/11/6 360 | l 9 10 361 | l 1 2 3 362 | p 9 10 363 | p 1""" 364 | --------------------------------------------------------------------------------