Update app.py
Browse files
app.py
CHANGED
|
@@ -532,7 +532,133 @@ def create_interface():
|
|
| 532 |
# Three.jsを使用した3Dマップの表示
|
| 533 |
three_map = gr.HTML(
|
| 534 |
value="""
|
| 535 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 536 |
""",
|
| 537 |
label="3D町マップ"
|
| 538 |
)
|
|
@@ -576,7 +702,6 @@ def create_interface():
|
|
| 576 |
updatePositions({json.dumps(state)});
|
| 577 |
}}
|
| 578 |
</script>
|
| 579 |
-
<iframe src="./static/index.html" width="100%" height="600px" frameborder="0"></iframe>
|
| 580 |
"""
|
| 581 |
|
| 582 |
# ボタンイベントの接続
|
|
@@ -662,8 +787,5 @@ if __name__ == "__main__":
|
|
| 662 |
server_name="0.0.0.0",
|
| 663 |
server_port=7860,
|
| 664 |
share=True,
|
| 665 |
-
show_error=True
|
| 666 |
-
root_path="",
|
| 667 |
-
favicon_path=None,
|
| 668 |
-
allowed_paths=["static"]
|
| 669 |
)
|
|
|
|
| 532 |
# Three.jsを使用した3Dマップの表示
|
| 533 |
three_map = gr.HTML(
|
| 534 |
value="""
|
| 535 |
+
<div id="three-container" style="width: 100%; height: 600px;"></div>
|
| 536 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
| 537 |
+
<script>
|
| 538 |
+
// Three.jsを使用した3Dマップの実装
|
| 539 |
+
let scene, camera, renderer, buildings = {}, residents = {};
|
| 540 |
+
|
| 541 |
+
function init() {
|
| 542 |
+
// シーンの作成
|
| 543 |
+
scene = new THREE.Scene();
|
| 544 |
+
scene.background = new THREE.Color(0x87CEEB); // 空色の背景
|
| 545 |
+
|
| 546 |
+
// カメラの設定
|
| 547 |
+
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
|
| 548 |
+
camera.position.set(50, 50, 50);
|
| 549 |
+
camera.lookAt(0, 0, 0);
|
| 550 |
+
|
| 551 |
+
// レンダラーの設定
|
| 552 |
+
renderer = new THREE.WebGLRenderer({ antialias: true });
|
| 553 |
+
renderer.setSize(window.innerWidth, window.innerHeight);
|
| 554 |
+
document.getElementById('three-container').appendChild(renderer.domElement);
|
| 555 |
+
|
| 556 |
+
// 光源の追加
|
| 557 |
+
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
|
| 558 |
+
scene.add(ambientLight);
|
| 559 |
+
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
|
| 560 |
+
directionalLight.position.set(50, 50, 50);
|
| 561 |
+
scene.add(directionalLight);
|
| 562 |
+
|
| 563 |
+
// 地面の作成
|
| 564 |
+
const groundGeometry = new THREE.PlaneGeometry(100, 100);
|
| 565 |
+
const groundMaterial = new THREE.MeshStandardMaterial({
|
| 566 |
+
color: 0x90EE90,
|
| 567 |
+
side: THREE.DoubleSide
|
| 568 |
+
});
|
| 569 |
+
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
|
| 570 |
+
ground.rotation.x = -Math.PI / 2;
|
| 571 |
+
scene.add(ground);
|
| 572 |
+
|
| 573 |
+
// アニメーションループの開始
|
| 574 |
+
animate();
|
| 575 |
+
|
| 576 |
+
// ウィンドウリサイズのハンドリング
|
| 577 |
+
window.addEventListener('resize', onWindowResize, false);
|
| 578 |
+
}
|
| 579 |
+
|
| 580 |
+
function createBuilding(name, position, color = 0x808080) {
|
| 581 |
+
const geometry = new THREE.BoxGeometry(10, 10, 10);
|
| 582 |
+
const material = new THREE.MeshStandardMaterial({ color: color });
|
| 583 |
+
const building = new THREE.Mesh(geometry, material);
|
| 584 |
+
building.position.set(position.x, 5, position.z);
|
| 585 |
+
scene.add(building);
|
| 586 |
+
buildings[name] = building;
|
| 587 |
+
|
| 588 |
+
// 建物名のラベル
|
| 589 |
+
const canvas = document.createElement('canvas');
|
| 590 |
+
const context = canvas.getContext('2d');
|
| 591 |
+
canvas.width = 256;
|
| 592 |
+
canvas.height = 64;
|
| 593 |
+
context.fillStyle = '#ffffff';
|
| 594 |
+
context.font = '24px Arial';
|
| 595 |
+
context.fillText(name, 10, 32);
|
| 596 |
+
const texture = new THREE.CanvasTexture(canvas);
|
| 597 |
+
const labelMaterial = new THREE.SpriteMaterial({ map: texture });
|
| 598 |
+
const label = new THREE.Sprite(labelMaterial);
|
| 599 |
+
label.position.set(position.x, 15, position.z);
|
| 600 |
+
scene.add(label);
|
| 601 |
+
}
|
| 602 |
+
|
| 603 |
+
function createResident(name, position, color = 0xff0000) {
|
| 604 |
+
const geometry = new THREE.SphereGeometry(1, 32, 32);
|
| 605 |
+
const material = new THREE.MeshStandardMaterial({ color: color });
|
| 606 |
+
const resident = new THREE.Mesh(geometry, material);
|
| 607 |
+
resident.position.set(position.x, 1, position.z);
|
| 608 |
+
scene.add(resident);
|
| 609 |
+
residents[name] = resident;
|
| 610 |
+
|
| 611 |
+
// 住民名のラベル
|
| 612 |
+
const canvas = document.createElement('canvas');
|
| 613 |
+
const context = canvas.getContext('2d');
|
| 614 |
+
canvas.width = 256;
|
| 615 |
+
canvas.height = 64;
|
| 616 |
+
context.fillStyle = '#ffffff';
|
| 617 |
+
context.font = '24px Arial';
|
| 618 |
+
context.fillText(name, 10, 32);
|
| 619 |
+
const texture = new THREE.CanvasTexture(canvas);
|
| 620 |
+
const labelMaterial = new THREE.SpriteMaterial({ map: texture });
|
| 621 |
+
const label = new THREE.Sprite(labelMaterial);
|
| 622 |
+
label.position.set(position.x, 3, position.z);
|
| 623 |
+
scene.add(label);
|
| 624 |
+
}
|
| 625 |
+
|
| 626 |
+
function updatePositions(state) {
|
| 627 |
+
// 建物の位置を更新
|
| 628 |
+
state.locations.forEach(location => {
|
| 629 |
+
if (!buildings[location.name]) {
|
| 630 |
+
createBuilding(location.name, location.position);
|
| 631 |
+
}
|
| 632 |
+
});
|
| 633 |
+
|
| 634 |
+
// 住民の位置を更新
|
| 635 |
+
state.residents.forEach(resident => {
|
| 636 |
+
if (!residents[resident.name]) {
|
| 637 |
+
createResident(resident.name, resident.position);
|
| 638 |
+
} else {
|
| 639 |
+
residents[resident.name].position.set(
|
| 640 |
+
resident.position.x,
|
| 641 |
+
1,
|
| 642 |
+
resident.position.z
|
| 643 |
+
);
|
| 644 |
+
}
|
| 645 |
+
});
|
| 646 |
+
}
|
| 647 |
+
|
| 648 |
+
function onWindowResize() {
|
| 649 |
+
camera.aspect = window.innerWidth / window.innerHeight;
|
| 650 |
+
camera.updateProjectionMatrix();
|
| 651 |
+
renderer.setSize(window.innerWidth, window.innerHeight);
|
| 652 |
+
}
|
| 653 |
+
|
| 654 |
+
function animate() {
|
| 655 |
+
requestAnimationFrame(animate);
|
| 656 |
+
renderer.render(scene, camera);
|
| 657 |
+
}
|
| 658 |
+
|
| 659 |
+
// 初期化
|
| 660 |
+
init();
|
| 661 |
+
</script>
|
| 662 |
""",
|
| 663 |
label="3D町マップ"
|
| 664 |
)
|
|
|
|
| 702 |
updatePositions({json.dumps(state)});
|
| 703 |
}}
|
| 704 |
</script>
|
|
|
|
| 705 |
"""
|
| 706 |
|
| 707 |
# ボタンイベントの接続
|
|
|
|
| 787 |
server_name="0.0.0.0",
|
| 788 |
server_port=7860,
|
| 789 |
share=True,
|
| 790 |
+
show_error=True
|
|
|
|
|
|
|
|
|
|
| 791 |
)
|