| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8" /> |
| <title>3D Apartment Visualization</title> |
| |
| <meta |
| http-equiv="Content-Security-Policy" |
| content="default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline';" |
| /> |
| <style> |
| body { |
| margin: 0; |
| overflow: hidden; |
| background-color: #202020; |
| } |
| canvas { |
| display: block; |
| } |
| </style> |
| </head> |
| <body> |
| <script type="module"> |
| |
| import * as THREE from 'https://cdn.jsdelivr.net/npm/three@0.149.0/build/three.module.js'; |
| import { OrbitControls } from 'https://cdn.jsdelivr.net/npm/three@0.149.0/examples/jsm/controls/OrbitControls.js'; |
| |
| |
| const scene = new THREE.Scene(); |
| scene.background = new THREE.Color(0x202020); |
| |
| |
| const camera = new THREE.PerspectiveCamera( |
| 75, |
| window.innerWidth / window.innerHeight, |
| 0.1, |
| 1000 |
| ); |
| camera.position.set(100, 100, 300); |
| |
| |
| const renderer = new THREE.WebGLRenderer({ antialias: true }); |
| renderer.setSize(window.innerWidth, window.innerHeight); |
| document.body.appendChild(renderer.domElement); |
| |
| |
| const controls = new OrbitControls(camera, renderer.domElement); |
| controls.enableDamping = true; |
| controls.dampingFactor = 0.05; |
| |
| |
| const apartments = [ |
| { rank: 1, pricePerSqm: 90788, block: 'D' }, |
| { rank: 2, pricePerSqm: 95584, block: 'D' }, |
| { rank: 3, pricePerSqm: 96545, block: 'C' }, |
| { rank: 4, pricePerSqm: 100171, block: 'C' }, |
| { rank: 5, pricePerSqm: 101833, block: 'C' }, |
| { rank: 6, pricePerSqm: 102200, block: 'B' }, |
| { rank: 7, pricePerSqm: 106238, block: 'D' }, |
| { rank: 8, pricePerSqm: 106327, block: 'B' }, |
| { rank: 9, pricePerSqm: 107423, block: 'B' }, |
| { rank: 10, pricePerSqm: 109136, block: 'A' } |
| ]; |
| |
| |
| const apartmentGroup = new THREE.Group(); |
| scene.add(apartmentGroup); |
| |
| |
| const blockColors = { |
| 'A': 0xff0000, |
| 'B': 0x0000ff, |
| 'C': 0x00ff00, |
| 'D': 0xffff00 |
| }; |
| |
| |
| apartments.forEach(apartment => { |
| |
| const x = apartment.rank * 30; |
| const y = -(apartment.pricePerSqm - 90000) / 50; |
| let z; |
| switch (apartment.block) { |
| case 'A': |
| z = 0; |
| break; |
| case 'B': |
| z = 100; |
| break; |
| case 'C': |
| z = 200; |
| break; |
| case 'D': |
| z = 300; |
| break; |
| default: |
| z = 0; |
| } |
| |
| |
| const geometry = new THREE.SphereGeometry(5, 16, 16); |
| const material = new THREE.MeshPhongMaterial({ |
| color: blockColors[apartment.block] || 0xffffff |
| }); |
| const sphere = new THREE.Mesh(geometry, material); |
| sphere.position.set(x, y, z); |
| apartmentGroup.add(sphere); |
| }); |
| |
| |
| const ambientLight = new THREE.AmbientLight(0xffffff, 0.5); |
| scene.add(ambientLight); |
| const directionalLight = new THREE.DirectionalLight(0xffffff, 1); |
| directionalLight.position.set(0, 1, 1); |
| scene.add(directionalLight); |
| |
| |
| window.addEventListener('resize', () => { |
| camera.aspect = window.innerWidth / window.innerHeight; |
| camera.updateProjectionMatrix(); |
| renderer.setSize(window.innerWidth, window.innerHeight); |
| }); |
| |
| |
| function animate() { |
| requestAnimationFrame(animate); |
| controls.update(); |
| renderer.render(scene, camera); |
| } |
| animate(); |
| </script> |
| </body> |
| </html> |
|
|