repos/TA159

Notas, resueltos y trabajos practicos de la materia Sistemas Gráficos
Commits Files Refs Submodules README LICENSE
commit 9e04bee9fe659eb7aea361271d8dfa815b1a5319
parent 7613c22cbfe66784723c801f82a618891c701691
Author: Martin Kloeckner <mjkloeckner@gmail.com>
Date:   Thu, 14 Aug 2025 14:10:50 -0300

added `ej/parametric_surface`

Diffstat:
Aej/parametric_surface/LICENSE | 21+++++++++++++++++++++
Aej/parametric_surface/index.html | 26++++++++++++++++++++++++++
Aej/parametric_surface/package-lock.json | 929+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aej/parametric_surface/package.json | 19+++++++++++++++++++
Aej/parametric_surface/src/assets/durmientes.jpg | 0
Aej/parametric_surface/src/assets/elevation_map_wider_river.png | 0
Aej/parametric_surface/src/assets/madera.jpg | 0
Aej/parametric_surface/src/assets/pared_de_ladrillo.jpg | 0
Aej/parametric_surface/src/assets/pasto.jpg | 0
Aej/parametric_surface/src/assets/roca.jpg | 0
Aej/parametric_surface/src/assets/sky_day_void.jpg | 0
Aej/parametric_surface/src/assets/sky_night.jpg | 0
Aej/parametric_surface/src/assets/tierra.jpg | 0
Aej/parametric_surface/src/assets/tierra_seca.jpg | 0
Aej/parametric_surface/src/assets/tree_forbidden_zone_map_wider_path.png | 0
Aej/parametric_surface/src/main.js | 464+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aej/parametric_surface/vite.config.mjs | 21+++++++++++++++++++++
17 files changed, 1480 insertions(+), 0 deletions(-)
diff --git a/ej/parametric_surface/LICENSE b/ej/parametric_surface/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2024 Martin J. Klöckner
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/ej/parametric_surface/index.html b/ej/parametric_surface/index.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html lang="en">
+    <head>
+        <meta charset="utf-8" />
+        <title>Trabajo Practico Sistemas Graficos | Martin Klöckner</title>
+        <link rel="icon" href="data:,"> <!-- Do not request favicon -->
+        <style>
+            html, body, #mainContainer {
+                padding: 0;
+                margin: 0;
+                height: 100%;
+                font-family: Baskerville, "Times New Roman", serif;
+                pointer-events: auto;
+                display: block;
+            }
+            canvas {
+                pointer-events: auto;
+                display: block;
+            }
+        </style>
+    </head>
+    <body>
+        <div id="mainContainer"></div>
+        <script type="module" src="src/main.js"></script>
+    </body>
+</html>
diff --git a/ej/parametric_surface/package-lock.json b/ej/parametric_surface/package-lock.json
@@ -0,0 +1,929 @@
+{
+  "name": "Trabajo Práctico",
+  "version": "1.0.0",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "Trabajo Práctico",
+      "version": "1.0.0",
+      "license": "MIT",
+      "dependencies": {
+        "dat.gui": "^0.7.9",
+        "three": "^0.162.0"
+      },
+      "devDependencies": {
+        "vite": "^5.1.4"
+      }
+    },
+    "node_modules/@esbuild/aix-ppc64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
+      "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "aix"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/android-arm": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
+      "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/android-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
+      "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/android-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
+      "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/darwin-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
+      "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/darwin-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
+      "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/freebsd-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
+      "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/freebsd-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
+      "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-arm": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
+      "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
+      "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-ia32": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
+      "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-loong64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
+      "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-mips64el": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
+      "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
+      "cpu": [
+        "mips64el"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-ppc64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
+      "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-riscv64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
+      "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-s390x": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
+      "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
+      "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/netbsd-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
+      "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/openbsd-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
+      "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/sunos-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
+      "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "sunos"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
+      "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-ia32": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
+      "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
+      "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@rollup/rollup-android-arm-eabi": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.2.tgz",
+      "integrity": "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@rollup/rollup-android-arm64": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.2.tgz",
+      "integrity": "sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@rollup/rollup-darwin-arm64": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.2.tgz",
+      "integrity": "sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@rollup/rollup-darwin-x64": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.2.tgz",
+      "integrity": "sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@rollup/rollup-freebsd-arm64": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.2.tgz",
+      "integrity": "sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ]
+    },
+    "node_modules/@rollup/rollup-freebsd-x64": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.2.tgz",
+      "integrity": "sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.2.tgz",
+      "integrity": "sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.2.tgz",
+      "integrity": "sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm64-gnu": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.2.tgz",
+      "integrity": "sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm64-musl": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.2.tgz",
+      "integrity": "sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-loongarch64-gnu": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.2.tgz",
+      "integrity": "sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.2.tgz",
+      "integrity": "sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.2.tgz",
+      "integrity": "sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-riscv64-musl": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.2.tgz",
+      "integrity": "sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-s390x-gnu": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.2.tgz",
+      "integrity": "sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-x64-gnu": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.2.tgz",
+      "integrity": "sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-x64-musl": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.2.tgz",
+      "integrity": "sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-arm64-msvc": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.2.tgz",
+      "integrity": "sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-ia32-msvc": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.2.tgz",
+      "integrity": "sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-x64-msvc": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.2.tgz",
+      "integrity": "sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@types/estree": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+      "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/dat.gui": {
+      "version": "0.7.9",
+      "resolved": "https://registry.npmjs.org/dat.gui/-/dat.gui-0.7.9.tgz",
+      "integrity": "sha512-sCNc1OHobc+Erc1HqiswYgHdVNpSJUlk/Hz8vzOCsER7rl+oF/4+v8GXFUyCgtXpoCX6+bnmg07DedLvBLwYKQ==",
+      "license": "Apache-2.0"
+    },
+    "node_modules/esbuild": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
+      "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "optionalDependencies": {
+        "@esbuild/aix-ppc64": "0.21.5",
+        "@esbuild/android-arm": "0.21.5",
+        "@esbuild/android-arm64": "0.21.5",
+        "@esbuild/android-x64": "0.21.5",
+        "@esbuild/darwin-arm64": "0.21.5",
+        "@esbuild/darwin-x64": "0.21.5",
+        "@esbuild/freebsd-arm64": "0.21.5",
+        "@esbuild/freebsd-x64": "0.21.5",
+        "@esbuild/linux-arm": "0.21.5",
+        "@esbuild/linux-arm64": "0.21.5",
+        "@esbuild/linux-ia32": "0.21.5",
+        "@esbuild/linux-loong64": "0.21.5",
+        "@esbuild/linux-mips64el": "0.21.5",
+        "@esbuild/linux-ppc64": "0.21.5",
+        "@esbuild/linux-riscv64": "0.21.5",
+        "@esbuild/linux-s390x": "0.21.5",
+        "@esbuild/linux-x64": "0.21.5",
+        "@esbuild/netbsd-x64": "0.21.5",
+        "@esbuild/openbsd-x64": "0.21.5",
+        "@esbuild/sunos-x64": "0.21.5",
+        "@esbuild/win32-arm64": "0.21.5",
+        "@esbuild/win32-ia32": "0.21.5",
+        "@esbuild/win32-x64": "0.21.5"
+      }
+    },
+    "node_modules/fsevents": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+      "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      }
+    },
+    "node_modules/nanoid": {
+      "version": "3.3.11",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+      "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "bin": {
+        "nanoid": "bin/nanoid.cjs"
+      },
+      "engines": {
+        "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+      }
+    },
+    "node_modules/picocolors": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+      "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/postcss": {
+      "version": "8.5.6",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
+      "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/postcss"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "nanoid": "^3.3.11",
+        "picocolors": "^1.1.1",
+        "source-map-js": "^1.2.1"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14"
+      }
+    },
+    "node_modules/rollup": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.2.tgz",
+      "integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/estree": "1.0.8"
+      },
+      "bin": {
+        "rollup": "dist/bin/rollup"
+      },
+      "engines": {
+        "node": ">=18.0.0",
+        "npm": ">=8.0.0"
+      },
+      "optionalDependencies": {
+        "@rollup/rollup-android-arm-eabi": "4.46.2",
+        "@rollup/rollup-android-arm64": "4.46.2",
+        "@rollup/rollup-darwin-arm64": "4.46.2",
+        "@rollup/rollup-darwin-x64": "4.46.2",
+        "@rollup/rollup-freebsd-arm64": "4.46.2",
+        "@rollup/rollup-freebsd-x64": "4.46.2",
+        "@rollup/rollup-linux-arm-gnueabihf": "4.46.2",
+        "@rollup/rollup-linux-arm-musleabihf": "4.46.2",
+        "@rollup/rollup-linux-arm64-gnu": "4.46.2",
+        "@rollup/rollup-linux-arm64-musl": "4.46.2",
+        "@rollup/rollup-linux-loongarch64-gnu": "4.46.2",
+        "@rollup/rollup-linux-ppc64-gnu": "4.46.2",
+        "@rollup/rollup-linux-riscv64-gnu": "4.46.2",
+        "@rollup/rollup-linux-riscv64-musl": "4.46.2",
+        "@rollup/rollup-linux-s390x-gnu": "4.46.2",
+        "@rollup/rollup-linux-x64-gnu": "4.46.2",
+        "@rollup/rollup-linux-x64-musl": "4.46.2",
+        "@rollup/rollup-win32-arm64-msvc": "4.46.2",
+        "@rollup/rollup-win32-ia32-msvc": "4.46.2",
+        "@rollup/rollup-win32-x64-msvc": "4.46.2",
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/source-map-js": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+      "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/three": {
+      "version": "0.162.0",
+      "resolved": "https://registry.npmjs.org/three/-/three-0.162.0.tgz",
+      "integrity": "sha512-xfCYj4RnlozReCmUd+XQzj6/5OjDNHBy5nT6rVwrOKGENAvpXe2z1jL+DZYaMu4/9pNsjH/4Os/VvS9IrH7IOQ==",
+      "license": "MIT"
+    },
+    "node_modules/vite": {
+      "version": "5.4.19",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz",
+      "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "esbuild": "^0.21.3",
+        "postcss": "^8.4.43",
+        "rollup": "^4.20.0"
+      },
+      "bin": {
+        "vite": "bin/vite.js"
+      },
+      "engines": {
+        "node": "^18.0.0 || >=20.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/vitejs/vite?sponsor=1"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.3"
+      },
+      "peerDependencies": {
+        "@types/node": "^18.0.0 || >=20.0.0",
+        "less": "*",
+        "lightningcss": "^1.21.0",
+        "sass": "*",
+        "sass-embedded": "*",
+        "stylus": "*",
+        "sugarss": "*",
+        "terser": "^5.4.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        },
+        "less": {
+          "optional": true
+        },
+        "lightningcss": {
+          "optional": true
+        },
+        "sass": {
+          "optional": true
+        },
+        "sass-embedded": {
+          "optional": true
+        },
+        "stylus": {
+          "optional": true
+        },
+        "sugarss": {
+          "optional": true
+        },
+        "terser": {
+          "optional": true
+        }
+      }
+    }
+  }
+}
diff --git a/ej/parametric_surface/package.json b/ej/parametric_surface/package.json
@@ -0,0 +1,19 @@
+{
+  "name": "Trabajo Práctico",
+  "version": "1.0.0",
+  "description": "",
+  "main": "index.js",
+  "scripts": {
+    "build": "vite build",
+    "preview": "vite preview"
+  },
+  "author": "",
+  "license": "MIT",
+  "dependencies": {
+    "dat.gui": "^0.7.9",
+    "three": "^0.162.0"
+  },
+  "devDependencies": {
+    "vite": "^5.1.4"
+  }
+}
diff --git a/ej/parametric_surface/src/assets/durmientes.jpg b/ej/parametric_surface/src/assets/durmientes.jpg
Binary files differ.
diff --git a/ej/parametric_surface/src/assets/elevation_map_wider_river.png b/ej/parametric_surface/src/assets/elevation_map_wider_river.png
Binary files differ.
diff --git a/ej/parametric_surface/src/assets/madera.jpg b/ej/parametric_surface/src/assets/madera.jpg
Binary files differ.
diff --git a/ej/parametric_surface/src/assets/pared_de_ladrillo.jpg b/ej/parametric_surface/src/assets/pared_de_ladrillo.jpg
Binary files differ.
diff --git a/ej/parametric_surface/src/assets/pasto.jpg b/ej/parametric_surface/src/assets/pasto.jpg
Binary files differ.
diff --git a/ej/parametric_surface/src/assets/roca.jpg b/ej/parametric_surface/src/assets/roca.jpg
Binary files differ.
diff --git a/ej/parametric_surface/src/assets/sky_day_void.jpg b/ej/parametric_surface/src/assets/sky_day_void.jpg
Binary files differ.
diff --git a/ej/parametric_surface/src/assets/sky_night.jpg b/ej/parametric_surface/src/assets/sky_night.jpg
Binary files differ.
diff --git a/ej/parametric_surface/src/assets/tierra.jpg b/ej/parametric_surface/src/assets/tierra.jpg
Binary files differ.
diff --git a/ej/parametric_surface/src/assets/tierra_seca.jpg b/ej/parametric_surface/src/assets/tierra_seca.jpg
Binary files differ.
diff --git a/ej/parametric_surface/src/assets/tree_forbidden_zone_map_wider_path.png b/ej/parametric_surface/src/assets/tree_forbidden_zone_map_wider_path.png
Binary files differ.
diff --git a/ej/parametric_surface/src/main.js b/ej/parametric_surface/src/main.js
@@ -0,0 +1,464 @@
+import * as THREE from 'three';
+import * as dat from 'dat.gui';
+import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
+
+import { ParametricGeometry } from 'three/addons/geometries/ParametricGeometry.js';
+import { ParametricGeometries } from 'three/examples/jsm/geometries/ParametricGeometries.js';
+import { mergeGeometries } from 'three/addons/utils/BufferGeometryUtils.js';
+
+let scene, camera, renderer, container, terrainMaterial, instancedTrees;
+let spherePath;
+let railsPath;
+let railsFoundationShape;
+
+let controls;
+let shapeBase, shapeNarrow;
+
+import tierraUrl     from '/src/assets/tierra.jpg'
+import rocaUrl       from '/src/assets/roca.jpg'
+import pastoUrl      from '/src/assets/pasto.jpg'
+import durmientesUrl from '/src/assets/durmientes.jpg'
+
+const textures = {
+    tierra:     { url: tierraUrl,     object: null },
+    roca:       { url: rocaUrl,       object: null },
+    pasto:      { url: pastoUrl,      object: null },
+    durmientes: { url: durmientesUrl, object: null },
+};
+
+function onResize() {
+    camera.aspect = container.offsetWidth / container.offsetHeight;
+    const rotMatrix = new THREE.Matrix4();
+    renderer.setSize(container.offsetWidth, container.offsetHeight);
+}
+
+function setupThreeJs() {
+    scene = new THREE.Scene();
+    container = document.getElementById('mainContainer');
+
+    renderer = new THREE.WebGLRenderer();
+    renderer.setClearColor(0x606060);
+    container.appendChild(renderer.domElement);
+
+    camera = new THREE.PerspectiveCamera(
+        35, window.innerWidth / window.innerHeight, 0.1, 1000);
+
+    camera.position.set(10, 20, 10);
+    camera.lookAt(0, 0, 0);
+
+    controls = new OrbitControls(camera, renderer.domElement);
+    controls.update();
+
+    const ambientLight = new THREE.AmbientLight(0xffffff);
+    scene.add(ambientLight);
+
+    const hemisphereLight = new THREE.HemisphereLight(0xffffff, 0x000000, 0.25);
+    scene.add(hemisphereLight);
+
+    const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
+    directionalLight.position.set(100, 100, 100);
+    scene.add(directionalLight);
+
+    const gridHelper = new THREE.GridHelper(50, 50);
+    scene.add(gridHelper);
+
+    const axesHelper = new THREE.AxesHelper(1);
+    scene.add(axesHelper);
+
+    window.addEventListener('resize', onResize);
+    onResize();
+}
+
+function onTextureLoaded(key, texture) {
+    texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
+    textures[key].object = texture;
+    console.log('Texture `' + key + '` loaded');
+}
+
+function loadTextures(callback) {
+    const loadingManager = new THREE.LoadingManager();
+
+    loadingManager.onLoad = () => {
+        console.log('All textures loaded');
+        callback();
+    };
+
+    for (const key in textures) {
+        console.log("Loading textures");
+        const loader = new THREE.TextureLoader(loadingManager);
+        const texture = textures[key];
+        texture.object = loader.load(
+            texture.url,
+            onTextureLoaded.bind(this, key),
+            null,
+            (error) => {
+                console.error(error);
+            }
+        );
+    }
+}
+
+function parametricRailsFoundationFunction(u, v, target) {
+    const rotMatrix = new THREE.Matrix4();
+    const translationMatrix = new THREE.Matrix4();
+    const levelMatrix = new THREE.Matrix4();
+
+    let railsPathPos = railsPath.getPointAt(v);
+    let railsFoundationShapePos = railsFoundationShape.getPointAt(u);
+    // TODO: make `railsFoundationShape` smaller and remove this multiplication
+    railsFoundationShapePos.multiplyScalar(0.5);
+
+    let tangente = new THREE.Vector3();
+    let binormal = new THREE.Vector3();
+    let normal = new THREE.Vector3();
+
+    tangente = railsPath.getTangent(v);
+
+    tangente.normalize();
+    binormal = new THREE.Vector3(0, 1, 0);
+    normal.crossVectors(tangente, binormal);
+
+    translationMatrix.makeTranslation(railsPathPos);
+
+    rotMatrix.identity();
+    levelMatrix.identity();
+
+    levelMatrix.makeTranslation(railsPathPos);
+    rotMatrix.makeBasis(normal, tangente, binormal);
+    levelMatrix.multiply(rotMatrix);
+    railsFoundationShapePos.applyMatrix4(levelMatrix);
+    
+    const x = railsFoundationShapePos.x;
+    const y = railsFoundationShapePos.y;
+    const z = railsFoundationShapePos.z;
+    target.set(x, y, z);
+}
+
+
+export function buildRailsFoundation() {
+    railsFoundationShape = new THREE.CatmullRomCurve3([
+        new THREE.Vector3( -2.00, 0.00, 0.00),
+        new THREE.Vector3( -1.00, 0.00, 0.50),
+        new THREE.Vector3(  0.00, 0.00, 0.55),
+        new THREE.Vector3(  1.00, 0.00, 0.50),
+        new THREE.Vector3(  2.00, 0.00, 0.00),
+    ], false);
+
+    // show rails foundation shape
+    const points = railsFoundationShape.getPoints(50);
+    const geometry = new THREE.BufferGeometry().setFromPoints(points);
+    const lineMaterial = new THREE.LineBasicMaterial({ color: 0xff0000 });
+    const curveObject = new THREE.Line(geometry, lineMaterial);
+    scene.add(curveObject);
+
+    const pGeometry = new ParametricGeometry(
+        parametricRailsFoundationFunction, 4, 50); // paso de discretizacion u y v
+    
+    textures.durmientes.object.wrapS = THREE.RepeatWrapping;
+    textures.durmientes.object.wrapT = THREE.RepeatWrapping;
+    textures.durmientes.object.repeat.set(1, 60);
+    textures.durmientes.object.anisotropy = 16;
+
+    // load into `map` the example texture
+    const map = new THREE.TextureLoader().load('https://threejs.org/examples/textures/uv_grid_opengl.jpg');
+    map.wrapS = map.wrapT = THREE.RepeatWrapping;
+    map.repeat.set(1, 30);
+    map.anisotropy = 16;
+    // map.rotation = Math.PI/2;
+
+    const pMaterial = new THREE.MeshPhongMaterial({
+        side: THREE.DoubleSide,
+        transparent: false,
+        opacity: 1.0,
+        shininess: 10,
+        // map: textures.durmientes.object
+        map: map
+    });
+    const pMesh = new THREE.Mesh(pGeometry, pMaterial);
+    pMesh.receiveShadow = true;
+    pMesh.castShadow = true;
+    scene.add(pMesh);
+}
+
+// `position` es de tipo `THREE.Vector3` y representa la translacion de la
+// forma del rail con respecto al origen del sist. de coordenadas de modelado
+function getParametricRailsFunction(radius, position) {
+    return function parametricRails(u, v, target) {
+        const rotMatrix = new THREE.Matrix4();
+        const translationMatrix = new THREE.Matrix4();
+        const levelMatrix = new THREE.Matrix4();
+
+        let railsShape = new THREE.Vector3();
+
+        let railsPathPos = railsPath.getPointAt(v);
+
+        // railsShapePos es un cilindro
+        let railsShapePos = new THREE.Vector3(
+            Math.cos(u*6.28) + position.x,
+            position.y,
+            Math.sin(u*6.28) + position.z);
+
+        railsShapePos.multiplyScalar(0.1*railsRadius);
+
+        let tangente = new THREE.Vector3();
+        let binormal = new THREE.Vector3();
+        let normal = new THREE.Vector3();
+
+        // https://threejs.org/docs/index.html?q=curve#api/en/extras/core/Curve.getTangent
+        tangente = railsPath.getTangentAt(v);
+        binormal = new THREE.Vector3(0, 1, 0);
+        normal.crossVectors(tangente, binormal);
+
+        translationMatrix.makeTranslation(railsPathPos);
+
+        rotMatrix.identity();
+        levelMatrix.identity();
+
+        levelMatrix.makeTranslation(railsPathPos);
+        rotMatrix.makeBasis(normal, tangente, binormal);
+
+        levelMatrix.multiply(rotMatrix);
+        railsShapePos.applyMatrix4(levelMatrix);
+        
+        const x = railsShapePos.x;
+        const y = railsShapePos.y;
+        const z = railsShapePos.z;
+        target.set(x, y, z);
+    }
+}
+
+const railsRadius = 0.35;
+function buildRails() {
+    let railsGeometries = [];
+
+    const leftRailGeometryFunction  = getParametricRailsFunction(railsRadius,
+        new THREE.Vector3( 6, 0, railsRadius+8));
+
+    const rightRailGeometryFunction = getParametricRailsFunction(railsRadius,
+        new THREE.Vector3(-6, 0, railsRadius+8));
+
+    const leftRailGeometry  = new ParametricGeometry(leftRailGeometryFunction, 100, 500);
+    const rightRailGeometry = new ParametricGeometry(rightRailGeometryFunction, 100, 500);
+
+    railsGeometries.push(leftRailGeometry);
+    railsGeometries.push(rightRailGeometry);
+
+    const railsMaterial = new THREE.MeshPhongMaterial({
+        side: THREE.DoubleSide,
+        transparent: false,
+        opacity: 1.0,
+        shininess: 10,
+        color: 0xFFFFFF
+    });
+
+    const railsGeometry = mergeGeometries(railsGeometries);
+    const rails = new THREE.Mesh(railsGeometry, railsMaterial);
+    rails.castShadow = true;
+    scene.add(rails);
+}
+
+function buildRailsPath() {
+    railsPath = new THREE.CatmullRomCurve3([
+        new THREE.Vector3(-10, 0,  10),
+        new THREE.Vector3( 10, 0,  10),
+        new THREE.Vector3( 10, 0, -10),
+        new THREE.Vector3(-10, 0, -10),
+    ], true, 'catmullrom', 1.0);
+
+    // muestra la curva utilizada para el camino de `rails`
+    const railsPathPoints = railsPath.getPoints(50);
+    const railsPathGeometry = new THREE.BufferGeometry().setFromPoints(railsPathPoints);
+    const railsPathMaterial = new THREE.LineBasicMaterial({ color: 0xff0000 });
+    const railsPathMesh = new THREE.Line(railsPathGeometry, railsPathMaterial);
+    scene.add(railsPathMesh);
+}
+
+function shapeExample() {
+    const arcRadius = 1;
+
+    shapeBase = new THREE.Shape()
+        .moveTo(-1, -1.5)
+        .lineTo(1, -1.5)
+        .lineTo(1, -1)
+        .arc(0, arcRadius, arcRadius, -Math.PI/2, Math.PI/2, false) 
+        .lineTo(1, 1.5)
+        .lineTo(-1, 1.5)
+        .lineTo(-1, 1)
+        .arc(0, -arcRadius, arcRadius, Math.PI/2, -Math.PI/2, false) 
+        .lineTo(-1, -1.5);
+
+    shapeNarrow = new THREE.Shape()
+        .moveTo(-1.0, -1.0)
+        .lineTo(-1.0, -0.5)
+        .lineTo( 1.0, -0.5)
+        .lineTo( 1.0, -1.0)
+        .arc(0, arcRadius, arcRadius, -Math.PI/2, Math.PI/2, false) 
+        .lineTo( 1.0, 0.5)
+        .lineTo(-1.0, 0.5)
+        .lineTo(-1.0, 1.0)
+        .arc(0, -arcRadius, arcRadius, Math.PI/2, -Math.PI/2, false);
+
+    const pointsBase = shapeBase.getPoints();
+    const geometryBase = new THREE.BufferGeometry().setFromPoints(pointsBase);
+    const materialBase = new THREE.LineBasicMaterial({ color: 0xff0000 });
+    const lineBase = new THREE.Line(geometryBase, materialBase);
+    // lineBase.rotation.x = Math.PI / 2; // de XY a XZ
+
+    const pointsNarrow = shapeNarrow.getPoints();
+    const geometryNarrow = new THREE.BufferGeometry().setFromPoints(pointsNarrow);
+    const materialNarrow = new THREE.LineBasicMaterial({ color: 0x0000FF });
+    const lineNarrow = new THREE.Line(geometryNarrow, materialNarrow);
+    // lineNarrow.rotation.x = Math.PI / 2; // de XY a XZ
+
+    // scene.add(lineBase);
+    // scene.add(lineNarrow);
+}
+
+function shapeExtrude(shape, color) {
+    const extrudeSettings = {
+        steps: 10,
+        depth: 5,
+        bevelEnabled: false,
+    };
+
+    const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings );
+    const material = new THREE.MeshPhongMaterial( { color: color } );
+    const mesh = new THREE.Mesh( geometry, material ) ;
+    scene.add(mesh);
+    mesh.rotation.x =-Math.PI/2;
+}
+
+/*
+if (v <= 0.2 || v >= 0.8) {
+    point = pointNarrow;
+} else if (v <= 0.3) {
+    const interpolationFactor = (v - 0.3)/0.1;
+
+    point.x = THREE.MathUtils.lerp(pointNarrow.x, pointBase.x, interpolationFactor);
+    point.y = THREE.MathUtils.lerp(pointNarrow.y, pointBase.y, interpolationFactor);
+} else if (v >= 0.7 && v <= 0.8) {
+    const interpolationFactor = (v - 0.7)/0.1;
+
+    point.x = THREE.MathUtils.lerp(pointBase.x, pointNarrow.x, interpolationFactor);
+    point.y = THREE.MathUtils.lerp(pointBase.y, pointNarrow.y, interpolationFactor);
+} else {
+    point = pointBase;
+}
+*/
+function parametricFunction(u, v, target) {
+    let point = new THREE.Vector2(0, 0);
+    const pointBase = shapeBase.getPointAt(u);
+    const pointNarrow = shapeNarrow.getPointAt(u);
+    // const pointNarrow = shapeBase.getPointAt(u);
+
+    // if (v <= 0.40) {
+    //     point = pointNarrow;
+    // } else if (v >= 0.60) {
+    //     point = pointBase;
+    // } else { // v en [0.45 - 0.55]
+    //     const interpolationFactor = (v - 0.40)/0.2;
+    //     console.log(interpolationFactor);
+    //     point.lerpVectors(pointNarrow, pointBase, interpolationFactor);
+    // }
+    
+    point = new THREE.Vector3(pointBase.x, pointBase.y, 10*v);
+    // point.applyAxisAngle(new THREE.Vector3(0, 0, 1), 2*Math.PI*v);
+
+    const rotMatrix = new THREE.Matrix4();
+    rotMatrix.makeRotationAxis(new THREE.Vector3(0, 0, 1), Math.PI*v);
+    // rotMatrix.makeBasis(normal, tangente, binormal);
+    
+    point.applyMatrix4(rotMatrix);
+    target.set(point.x, point.y, point.z);
+}
+
+function generateTunnelGeometry(
+    tunnelHeight = 20, tunnelWidth = 14,
+    tunnelWallThickness = 0.5, tunnelLen = 26) {
+
+    const path = new THREE.Path();
+    path.moveTo(-tunnelWidth/2, 0);
+    path.lineTo(-tunnelWidth/2, tunnelHeight*1/3);
+    path.moveTo(-tunnelWidth/2, tunnelHeight*1/3);
+    path.quadraticCurveTo(0, tunnelHeight, tunnelWidth/2, tunnelHeight*1/3);
+    path.moveTo(tunnelWidth/2, 0);
+    path.lineTo(tunnelWidth/2, 0);
+
+
+    // cerramos la curva con otra de la misma forma con una diferencia de
+    // `tunnelWallThickness`
+    path.lineTo(tunnelWidth/2-tunnelWallThickness, 0);
+    path.moveTo(tunnelWidth/2-tunnelWallThickness, 0);
+
+    path.lineTo(tunnelWidth/2-tunnelWallThickness, tunnelHeight*1/3);
+    path.moveTo(tunnelWidth/2-tunnelWallThickness, tunnelHeight*1/3);
+
+    path.quadraticCurveTo(
+        0, tunnelHeight-(tunnelWallThickness*2),
+        -tunnelWidth/2+tunnelWallThickness, tunnelHeight*1/3);
+
+    path.lineTo(-tunnelWidth/2+tunnelWallThickness, 0);
+    path.moveTo(-tunnelWidth/2+tunnelWallThickness, 0);
+
+    path.lineTo(-tunnelWidth/2, 0);
+    path.moveTo(-tunnelWidth/2, 0);
+
+    const pathPoints = path.getPoints(50);
+    const pathGeometry = new THREE.BufferGeometry().setFromPoints(pathPoints);
+    const pathMaterial = new THREE.LineBasicMaterial({ color: 0xff0000 });
+    const pathObject = new THREE.Line(pathGeometry, pathMaterial);
+    scene.add(pathObject);
+    return;
+
+    // const points = path.getPoints();
+    // const shape = new THREE.Shape(points);
+    const extrudeSettings = {
+        curveSegments: 24,
+        steps: 50,
+        depth: tunnelLen,
+    };
+
+    const geometry = new THREE.ExtrudeGeometry(path, extrudeSettings);
+
+    // el `1` en `x` es porque por algun motivo queda descentrado con respecto
+    // a la vía del tren
+    geometry.translate(1, 0, -tunnelLen/2);
+    return geometry;
+}
+
+function generateTunnel() {
+    const geometry = generateTunnelGeometry();
+    const material = new THREE.MeshPhongMaterial({ color: 0xFFFF00, side: THREE.DoubleSide });
+    const mesh = new THREE.Mesh( geometry, material );
+    scene.add(mesh);
+}
+
+function shapeSweep(shape) {
+    const geometry = new ParametricGeometry(parametricFunction, 100, 100);
+    const material = new THREE.MeshPhongMaterial({ color: 0xFFFF00, side: THREE.DoubleSide });
+    const mesh = new THREE.Mesh( geometry, material );
+    mesh.rotation.x = -Math.PI/2;
+    scene.add(mesh);
+}
+
+function mainLoop() {
+    requestAnimationFrame(mainLoop);
+    renderer.render(scene, camera);
+}
+
+function main() {
+    shapeExample();
+    // shapeExtrude(shapeBase, 0xCF4040);
+    // shapeExtrude(shapeNarrow, 0x5050FF);
+    shapeSweep();
+    // generateTunnel();
+
+    // buildRailsPath();
+    // buildRailsFoundation();
+    // buildRails();
+
+    mainLoop();
+}
+
+setupThreeJs();
+loadTextures(main);
diff --git a/ej/parametric_surface/vite.config.mjs b/ej/parametric_surface/vite.config.mjs
@@ -0,0 +1,21 @@
+import { resolve } from 'path'
+import { defineConfig } from 'vite'
+
+export default defineConfig({
+    build: {
+        rollupOptions: {
+            input: {
+                main:      resolve(__dirname, 'index.html'),
+                bridge:    resolve(__dirname, 'bridge.html'),
+                rails:     resolve(__dirname, 'rails.html'),
+                scene:     resolve(__dirname, 'scene.html'),
+                terrain:   resolve(__dirname, 'terrain.html'),
+                trackMap: resolve(__dirname, 'track-map.html'),
+                train:     resolve(__dirname, 'train.html'),
+                trees:     resolve(__dirname, 'trees.html'),
+                tunnel:    resolve(__dirname, 'tunnel.html')
+            },
+        },
+    },
+    base: "./",
+})