|
<!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/[email protected]/build/three.module.js'; |
|
import { OrbitControls } from 'https://cdn.jsdelivr.net/npm/[email protected]/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> |
|
|