HANSOL commited on
Commit ยท
2be4f60
1
Parent(s): d7747e3
v4.3: Multi-Page structure + 5 new landforms (uvala, tower_karst, karren, transverse_dune, star_dune)
Browse files- app.py +98 -10
- app/Home.py +102 -0
- app/main.py +184 -141
- app/pages/1_๐_Gallery.py +272 -0
- engine/ideal_landforms.py +671 -141
- pages/1_๐_Gallery.py +272 -0
- terrain_processes.html +474 -0
- terrain_processes_3d.html +0 -0
- terrain_processes_stages.html +0 -0
app.py
CHANGED
|
@@ -1,14 +1,102 @@
|
|
| 1 |
-
|
| 2 |
-
|
|
|
|
|
|
|
|
|
|
| 3 |
|
| 4 |
-
|
| 5 |
-
|
|
|
|
|
|
|
|
|
|
| 6 |
|
| 7 |
-
#
|
| 8 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
|
| 10 |
-
|
| 11 |
-
|
| 12 |
|
| 13 |
-
|
| 14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
๐ Geo-Lab AI - ํ
|
| 3 |
+
HuggingFace Spaces Entry Point (Multi-Page Streamlit)
|
| 4 |
+
"""
|
| 5 |
+
import streamlit as st
|
| 6 |
|
| 7 |
+
st.set_page_config(
|
| 8 |
+
page_title="๐ Geo-Lab AI",
|
| 9 |
+
page_icon="๐",
|
| 10 |
+
layout="wide"
|
| 11 |
+
)
|
| 12 |
|
| 13 |
+
# ========== ์ต์๋จ: ์ ์์ ์ ๋ณด ==========
|
| 14 |
+
st.markdown("""
|
| 15 |
+
<div style='background: linear-gradient(90deg, #1565C0, #42A5F5); padding: 12px 20px; border-radius: 10px; margin-bottom: 15px;'>
|
| 16 |
+
<div style='display: flex; justify-content: space-between; align-items: center; color: white;'>
|
| 17 |
+
<span style='font-size: 1.1rem;'>๐ <b>Geo-Lab AI</b> - ์ด์์ ์งํ ์๋ฎฌ๋ ์ดํฐ</span>
|
| 18 |
+
<span style='font-size: 0.85rem;'>์ ์: 2025 ํ๋ฐฑ๊ณ ๋ฑํ๊ต ๊นํ์T</span>
|
| 19 |
+
</div>
|
| 20 |
+
</div>
|
| 21 |
+
""", unsafe_allow_html=True)
|
| 22 |
|
| 23 |
+
st.title("๐ Geo-Lab AI")
|
| 24 |
+
st.subheader("_๊ต์ฌ๋ฅผ ์ํ ์งํ ํ์ฑ๊ณผ์ ์๊ฐํ ๋๊ตฌ_")
|
| 25 |
|
| 26 |
+
st.markdown("---")
|
| 27 |
+
|
| 28 |
+
# ========== ๊ธฐ๋ฅ ์๊ฐ ==========
|
| 29 |
+
col1, col2, col3 = st.columns(3)
|
| 30 |
+
|
| 31 |
+
with col1:
|
| 32 |
+
st.markdown("""
|
| 33 |
+
### ๐ ์ด์์ ์งํ ๊ฐค๋ฌ๋ฆฌ
|
| 34 |
+
- 31์ข
+ ๊ต๊ณผ์์ ์งํ ๋ชจ๋ธ
|
| 35 |
+
- 7๊ฐ ์นดํ
๊ณ ๋ฆฌ ๋ถ๋ฅ
|
| 36 |
+
- 2D/3D ์๊ฐํ
|
| 37 |
+
|
| 38 |
+
**๐ ์ผ์ชฝ ์ฌ์ด๋๋ฐ์์ ํ์ด์ง ์ ํ**
|
| 39 |
+
""")
|
| 40 |
+
|
| 41 |
+
with col2:
|
| 42 |
+
st.markdown("""
|
| 43 |
+
### ๐ฌ ํ์ฑ ๊ณผ์ ์ ๋๋ฉ์ด์
|
| 44 |
+
- 0% โ 100% ์ฌ๋ผ์ด๋
|
| 45 |
+
- ์ค์๊ฐ ์งํ ๋ณํ ๊ด์ฐฐ
|
| 46 |
+
- ๋ฌผ๋ฆฌ ๊ธฐ๋ฐ ์๋ฎฌ๋ ์ด์
|
| 47 |
+
""")
|
| 48 |
+
|
| 49 |
+
with col3:
|
| 50 |
+
st.markdown("""
|
| 51 |
+
### ๐ ์งํ ์๋๋ฆฌ์ค
|
| 52 |
+
- ๋ค์ค ์ด๋ก ๋ชจ๋ธ ๋น๊ต
|
| 53 |
+
- ํ๋ผ๋ฏธํฐ ์กฐ์
|
| 54 |
+
- ๊ณผํ์ ์๋ฎฌ๋ ์ด์
|
| 55 |
+
""")
|
| 56 |
+
|
| 57 |
+
st.markdown("---")
|
| 58 |
+
|
| 59 |
+
# ========== ์ฌ์ฉ๋ฒ ==========
|
| 60 |
+
st.info("""
|
| 61 |
+
### ๐ก ์ฌ์ฉ๋ฒ
|
| 62 |
+
|
| 63 |
+
1. **์ผ์ชฝ ์ฌ์ด๋๋ฐ**์์ ์ํ๋ ํ์ด์ง ์ ํ
|
| 64 |
+
2. **๐ ์ด์์ ์งํ ๊ฐค๋ฌ๋ฆฌ** - ๊ต๊ณผ์์ ์งํ ํ์ธ
|
| 65 |
+
3. **๐ ์งํ ์๋๋ฆฌ์ค** - ์์ธ ์๋ฎฌ๋ ์ด์
์คํ
|
| 66 |
+
|
| 67 |
+
> โ ๏ธ **๊ฐ ํ์ด์ง๋ ๋
๋ฆฝ์ ์ผ๋ก ๋ก๋๋ฉ๋๋ค** - ํ์ด์ง ์ด๋ ์ ์ด์ 3D๊ฐ ํด์ ๋์ด ์์ ์ ์ผ๋ก ์๋ํฉ๋๋ค.
|
| 68 |
+
""")
|
| 69 |
+
|
| 70 |
+
# ========== ์ง์ ์งํ ๋ชฉ๋ก ==========
|
| 71 |
+
with st.expander("๐ ์ง์ ์งํ ๋ชฉ๋ก (36์ข
)", expanded=False):
|
| 72 |
+
st.markdown("""
|
| 73 |
+
| ์นดํ
๊ณ ๋ฆฌ | ์งํ |
|
| 74 |
+
|----------|------|
|
| 75 |
+
| ๐ ํ์ฒ | ์ ์์ง, ์์ ๊ณก๋ฅ, ๊ฐ์
๊ณก๋ฅ, V์๊ณก, ๋ง์ํ์ฒ, ํญํฌ |
|
| 76 |
+
| ๐บ ์ผ๊ฐ์ฃผ | ์ผ๋ฐ, ์กฐ์กฑ์, ํธ์, ์ฒจ๋์ |
|
| 77 |
+
| โ๏ธ ๋นํ | U์๊ณก, ๊ถ๊ณก, ํธ๋ฅธ, ํผ์ค๋ฅด๋, ๋๋ผ๋ฆฐ, ๋นํด์ |
|
| 78 |
+
| ๐ ํ์ฐ | ์์ํ์ฐ, ์ฑ์ธตํ์ฐ, ์นผ๋ฐ๋ผ, ํ๊ตฌํธ, ์ฉ์๋์ง |
|
| 79 |
+
| ๐ฆ ์นด๋ฅด์คํธ | ๋๋ฆฌ๋ค, **์ฐ๋ฐ๋ผ, ํ์นด๋ฅด์คํธ, ์นด๋ ** |
|
| 80 |
+
| ๐๏ธ ๊ฑด์กฐ | ๋ฐ๋ฅดํ, **ํก์ฌ๊ตฌ, ์ฑ์ฌ๊ตฌ**, ๋ฉ์ฌ/๋ทฐํธ |
|
| 81 |
+
| ๐๏ธ ํด์ | ํด์์ ๋ฒฝ, ์ฌ์ทจ+์ํธ, ์ก๊ณ์ฌ์ฃผ, ๋ฆฌ์์คํด์, ํด์์์น, ํด์์ฌ๊ตฌ |
|
| 82 |
+
""")
|
| 83 |
+
|
| 84 |
+
# ========== ์
๋ฐ์ดํธ ๋ด์ญ ==========
|
| 85 |
+
with st.expander("๐ ์
๋ฐ์ดํธ ๋ด์ญ", expanded=False):
|
| 86 |
+
st.markdown("""
|
| 87 |
+
**v4.3 (2025-12-14)** ๐
|
| 88 |
+
- ์ ์งํ ์ถ๊ฐ: ์ฐ๋ฐ๋ผ, ํ์นด๋ฅด์คํธ, ์นด๋ , ํก์ฌ๊ตฌ, ์ฑ์ฌ๊ตฌ
|
| 89 |
+
- ๋ฆฌ์์ค ํด์, ํด์์์น ๊ฐ์
|
| 90 |
+
- ํ์ฑ๊ณผ์ ์ ๋๋ฉ์ด์
๊ฐ์ (ํญํฌ ๋๋ถ์นจ์, ํผ์ค๋ฅด๋ ๋นํโ๋ฌผ)
|
| 91 |
+
|
| 92 |
+
**v4.2 (2025-12-14)**
|
| 93 |
+
- Multi-Page ๊ตฌ์กฐ๋ก ๋ณ๊ฒฝ (์์ ์ฑ ํฅ์)
|
| 94 |
+
- WebGL ์ปจํ
์คํธ ๊ด๋ฆฌ ๊ฐ์
|
| 95 |
+
|
| 96 |
+
**v4.1 (2025-12-14)**
|
| 97 |
+
- ์ด์์ ์งํ ๊ฐค๋ฌ๋ฆฌ 31์ข
์ถ๊ฐ
|
| 98 |
+
- ํ์ฑ๊ณผ์ ์ ๋๋ฉ์ด์
๊ธฐ๋ฅ
|
| 99 |
+
""")
|
| 100 |
+
|
| 101 |
+
st.markdown("---")
|
| 102 |
+
st.caption("ยฉ 2025 ํ๋ฐฑ๊ณ ๋ฑํ๊ต ๊นํ์T | Geo-Lab AI")
|
app/Home.py
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
๐ Geo-Lab AI - ํ
|
| 3 |
+
Multi-page Streamlit ์ฑ์ ๋ฉ์ธ ํ์ด์ง
|
| 4 |
+
"""
|
| 5 |
+
import streamlit as st
|
| 6 |
+
|
| 7 |
+
st.set_page_config(
|
| 8 |
+
page_title="๐ Geo-Lab AI",
|
| 9 |
+
page_icon="๐",
|
| 10 |
+
layout="wide"
|
| 11 |
+
)
|
| 12 |
+
|
| 13 |
+
# ========== ์ต์๋จ: ์ ์์ ์ ๋ณด ==========
|
| 14 |
+
st.markdown("""
|
| 15 |
+
<div style='background: linear-gradient(90deg, #1565C0, #42A5F5); padding: 12px 20px; border-radius: 10px; margin-bottom: 15px;'>
|
| 16 |
+
<div style='display: flex; justify-content: space-between; align-items: center; color: white;'>
|
| 17 |
+
<span style='font-size: 1.1rem;'>๐ <b>Geo-Lab AI</b> - ์ด์์ ์งํ ์๋ฎฌ๋ ์ดํฐ</span>
|
| 18 |
+
<span style='font-size: 0.85rem;'>์ ์: 2025 ํ๋ฐฑ๊ณ ๋ฑํ๊ต ๊นํ์T</span>
|
| 19 |
+
</div>
|
| 20 |
+
</div>
|
| 21 |
+
""", unsafe_allow_html=True)
|
| 22 |
+
|
| 23 |
+
st.title("๐ Geo-Lab AI")
|
| 24 |
+
st.subheader("_๊ต์ฌ๋ฅผ ์ํ ์งํ ํ์ฑ๊ณผ์ ์๊ฐํ ๋๊ตฌ_")
|
| 25 |
+
|
| 26 |
+
st.markdown("---")
|
| 27 |
+
|
| 28 |
+
# ========== ๊ธฐ๋ฅ ์๊ฐ ==========
|
| 29 |
+
col1, col2, col3 = st.columns(3)
|
| 30 |
+
|
| 31 |
+
with col1:
|
| 32 |
+
st.markdown("""
|
| 33 |
+
### ๐ ์ด์์ ์งํ ๊ฐค๋ฌ๋ฆฌ
|
| 34 |
+
- 31์ข
๊ต๊ณผ์์ ์งํ ๋ชจ๋ธ
|
| 35 |
+
- 7๊ฐ ์นดํ
๊ณ ๋ฆฌ ๋ถ๋ฅ
|
| 36 |
+
- 2D/3D ์๊ฐํ
|
| 37 |
+
|
| 38 |
+
**๐ ์ผ์ชฝ ์ฌ์ด๋๋ฐ์์ ํ์ด์ง ์ ํ**
|
| 39 |
+
""")
|
| 40 |
+
|
| 41 |
+
with col2:
|
| 42 |
+
st.markdown("""
|
| 43 |
+
### ๐ฌ ํ์ฑ ๊ณผ์ ์ ๋๋ฉ์ด์
|
| 44 |
+
- 0% โ 100% ์ฌ๋ผ์ด๋
|
| 45 |
+
- ์ค์๊ฐ ์งํ ๋ณํ ๊ด์ฐฐ
|
| 46 |
+
- ๋ฌผ๋ฆฌ ๊ธฐ๋ฐ ์๋ฎฌ๋ ์ด์
|
| 47 |
+
""")
|
| 48 |
+
|
| 49 |
+
with col3:
|
| 50 |
+
st.markdown("""
|
| 51 |
+
### ๐ ์งํ ์๋๋ฆฌ์ค
|
| 52 |
+
- ๋ค์ค ์ด๋ก ๋ชจ๋ธ ๋น๊ต
|
| 53 |
+
- ํ๋ผ๋ฏธํฐ ์กฐ์
|
| 54 |
+
- ๊ณผํ์ ์๋ฎฌ๋ ์ด์
|
| 55 |
+
""")
|
| 56 |
+
|
| 57 |
+
st.markdown("---")
|
| 58 |
+
|
| 59 |
+
# ========== ์ฌ์ฉ๋ฒ ==========
|
| 60 |
+
st.info("""
|
| 61 |
+
### ๐ก ์ฌ์ฉ๋ฒ
|
| 62 |
+
|
| 63 |
+
1. **์ผ์ชฝ ์ฌ์ด๋๋ฐ**์์ ์ํ๋ ํ์ด์ง ์ ํ
|
| 64 |
+
2. **๐ ์ด์์ ์งํ ๊ฐค๋ฌ๋ฆฌ** - ๊ต๊ณผ์์ ์งํ ํ์ธ
|
| 65 |
+
3. **๐ ์งํ ์๋๋ฆฌ์ค** - ์์ธ ์๋ฎฌ๋ ์ด์
์คํ
|
| 66 |
+
|
| 67 |
+
> โ ๏ธ **๊ฐ ํ์ด์ง๋ ๋
๋ฆฝ์ ์ผ๋ก ๋ก๋๋ฉ๋๋ค** - ํ์ด์ง ์ด๋ ์ ์ด์ 3D๊ฐ ํด์ ๋์ด ์์ ์ ์ผ๋ก ์๋ํฉ๋๋ค.
|
| 68 |
+
""")
|
| 69 |
+
|
| 70 |
+
# ========== ์ง์ ์งํ ๋ชฉ๋ก ==========
|
| 71 |
+
with st.expander("๐ ์ง์ ์งํ ๋ชฉ๋ก (31์ข
)", expanded=False):
|
| 72 |
+
st.markdown("""
|
| 73 |
+
| ์นดํ
๊ณ ๋ฆฌ | ์งํ |
|
| 74 |
+
|----------|------|
|
| 75 |
+
| ๐ ํ์ฒ | ์ ์์ง, ์์ ๊ณก๋ฅ, ๊ฐ์
๊ณก๋ฅ, V์๊ณก, ๋ง์ํ์ฒ, ํญํฌ |
|
| 76 |
+
| ๐บ ์ผ๊ฐ์ฃผ | ์ผ๋ฐ, ์กฐ์กฑ์, ํธ์, ์ฒจ๋์ |
|
| 77 |
+
| โ๏ธ ๋นํ | U์๊ณก, ๊ถ๊ณก, ํธ๋ฅธ, ํผ์ค๋ฅด๋, ๋๋ผ๋ฆฐ, ๋นํด์ |
|
| 78 |
+
| ๐ ํ์ฐ | ์์ํ์ฐ, ์ฑ์ธตํ์ฐ, ์นผ๋ฐ๋ผ, ํ๊ตฌํธ, ์ฉ์๋์ง |
|
| 79 |
+
| ๐ฆ ์นด๋ฅด์คํธ | ๋๋ฆฌ๋ค |
|
| 80 |
+
| ๐๏ธ ๊ฑด์กฐ | ๋ฐ๋ฅดํ ์ฌ๊ตฌ, ๋ฉ์ฌ/๋ทฐํธ |
|
| 81 |
+
| ๐๏ธ ํด์ | ํด์์ ๋ฒฝ, ์ฌ์ทจ+์ํธ, ์ก๊ณ์ฌ์ฃผ, ๋ฆฌ์์คํด์, ํด์์์น, ํด์์ฌ๊ตฌ |
|
| 82 |
+
""")
|
| 83 |
+
|
| 84 |
+
# ========== ์
๋ฐ์ดํธ ๋ด์ญ ==========
|
| 85 |
+
with st.expander("๐ ์
๋ฐ์ดํธ ๋ด์ญ", expanded=False):
|
| 86 |
+
st.markdown("""
|
| 87 |
+
**v4.2 (2025-12-14)** ๐
|
| 88 |
+
- Multi-Page ๊ตฌ์กฐ๋ก ๋ณ๊ฒฝ (์์ ์ฑ ํฅ์)
|
| 89 |
+
- WebGL ์ปจํ
์คํธ ๊ด๋ฆฌ ๊ฐ์
|
| 90 |
+
|
| 91 |
+
**v4.1 (2025-12-14)**
|
| 92 |
+
- ์ด์์ ์งํ ๊ฐค๋ฌ๋ฆฌ 31์ข
์ถ๊ฐ
|
| 93 |
+
- ํ์ฑ๊ณผ์ ์ ๋๋ฉ์ด์
๊ธฐ๋ฅ
|
| 94 |
+
- 7๊ฐ ์นดํ
๊ณ ๋ฆฌ ๋ถ๋ฅ
|
| 95 |
+
|
| 96 |
+
**v4.0**
|
| 97 |
+
- Project Genesis ํตํฉ ๋ฌผ๋ฆฌ ์์ง
|
| 98 |
+
- ์งํ ์๋๋ฆฌ์ค ํญ
|
| 99 |
+
""")
|
| 100 |
+
|
| 101 |
+
st.markdown("---")
|
| 102 |
+
st.caption("ยฉ 2025 ํ๋ฐฑ๊ณ ๋ฑํ๊ต ๊นํ์T | Geo-Lab AI")
|
app/main.py
CHANGED
|
@@ -66,27 +66,6 @@ st.markdown("""
|
|
| 66 |
""", unsafe_allow_html=True)
|
| 67 |
|
| 68 |
|
| 69 |
-
# ============ ์ด๋ฏธ์ง ์์ ๋ก๋ ํฌํผ ============
|
| 70 |
-
def safe_image(path, caption="", use_column_width=True):
|
| 71 |
-
"""์ด๋ฏธ์ง ํ์ผ์ด ์์ด๋ ์๋ฌ ์์ด ์ฒ๋ฆฌ"""
|
| 72 |
-
if os.path.exists(path):
|
| 73 |
-
st.image(path, caption=caption, use_column_width=use_column_width)
|
| 74 |
-
else:
|
| 75 |
-
st.info(f"๐ท {caption} (์ด๋ฏธ์ง ๋ฏธํฌํจ)")
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
# ============ ์๋ฒ์ฌ์ด๋ 3D ๋ ๋๋ง (WebGL ์์ด) ============
|
| 79 |
-
def render_3d_as_image(fig_plotly, width=800, height=600):
|
| 80 |
-
"""Plotly 3D figure๋ฅผ ์๋ฒ์ฌ์ด๋์์ PNG ์ด๋ฏธ์ง๋ก ๋ ๋๋ง (WebGL ๋ถํ์)"""
|
| 81 |
-
import io
|
| 82 |
-
try:
|
| 83 |
-
img_bytes = fig_plotly.to_image(format="png", width=width, height=height, engine="kaleido")
|
| 84 |
-
return img_bytes
|
| 85 |
-
except Exception as e:
|
| 86 |
-
st.error(f"3D ๋ ๋๋ง ์ค๋ฅ: {e}")
|
| 87 |
-
return None
|
| 88 |
-
|
| 89 |
-
|
| 90 |
# ============ ์ด๋ก ์ ์ ============
|
| 91 |
|
| 92 |
V_VALLEY_THEORIES = {
|
|
@@ -2547,8 +2526,14 @@ def render_terrain_3d(elevation, title, add_water=True, water_level=0, view_elev
|
|
| 2547 |
return fig
|
| 2548 |
|
| 2549 |
|
| 2550 |
-
def render_terrain_plotly(elevation, title, add_water=True, water_level=0, texture_path=None, force_camera=True, water_depth_grid=None, sediment_grid=None):
|
| 2551 |
-
"""Plotly ์ธํฐ๋ํฐ๋ธ 3D Surface - ์ฌ์ค์ ํ
์ค์ฒ(Biome) ๋๋ ์์ฑ ์ด๋ฏธ์ง ์ ์ฉ
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2552 |
h, w = elevation.shape
|
| 2553 |
x = np.arange(w)
|
| 2554 |
y = np.arange(h)
|
|
@@ -2581,27 +2566,70 @@ def render_terrain_plotly(elevation, title, add_water=True, water_level=0, textu
|
|
| 2581 |
biome[is_deposit] = 0
|
| 2582 |
|
| 2583 |
# ์์ (๊ฒฝ์ฌ๊ฐ ๊ธํ ๊ณณ) - ์ ๋ฒฝ
|
| 2584 |
-
|
| 2585 |
-
|
| 2586 |
-
|
| 2587 |
-
|
| 2588 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2589 |
|
| 2590 |
# ์กฐ๊ธ ๋ ์์ฐ์ค๋ฝ๊ฒ: ๋
ธ์ด์ฆ ์ถ๊ฐ (๊ฒฝ๊ณ๋ฉด ๋ธ๋ ๋ฉ ํจ๊ณผ ํ๋ด)
|
| 2591 |
noise = np.random.normal(0, 0.2, elevation.shape)
|
| 2592 |
biome_noisy = np.clip(biome + noise, 0, 3).round(2)
|
| 2593 |
|
| 2594 |
# ์ปค์คํ
์ปฌ๋ฌ์ค์ผ์ผ (Discrete)
|
| 2595 |
-
# 0: Soil/Sand (Yellowish), 1: Grass (Green), 2: Rock (Gray), 3: Snow
|
| 2596 |
-
|
| 2597 |
-
|
| 2598 |
-
|
| 2599 |
-
|
| 2600 |
-
|
| 2601 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2602 |
|
| 2603 |
# ์งํ ๋
ธ์ด์ฆ (Fractal Roughness) - ์๊ฐ์ ๋ํ
์ผ ์ถ๊ฐ
|
| 2604 |
-
visual_z = (elevation + np.random.normal(0, 0.2, elevation.shape)).round(2)
|
| 2605 |
|
| 2606 |
# ํ
์ค์ฒ ๋ก์ง (์ด๋ฏธ์ง ๋งคํ)
|
| 2607 |
final_surface_color = biome_noisy
|
|
@@ -2611,7 +2639,7 @@ def render_terrain_plotly(elevation, title, add_water=True, water_level=0, textu
|
|
| 2611 |
final_colorbar = dict(
|
| 2612 |
title=dict(text="์งํ ์ํ", font=dict(color='white')),
|
| 2613 |
tickvals=[0.37, 1.12, 1.87, 2.62],
|
| 2614 |
-
ticktext=
|
| 2615 |
tickfont=dict(color='white')
|
| 2616 |
)
|
| 2617 |
|
|
@@ -3131,41 +3159,44 @@ def main():
|
|
| 3131 |
elevation = np.zeros((gallery_grid_size, gallery_grid_size))
|
| 3132 |
|
| 3133 |
with col_view:
|
| 3134 |
-
# 2D
|
| 3135 |
-
|
|
|
|
| 3136 |
|
| 3137 |
-
|
| 3138 |
-
|
| 3139 |
-
|
| 3140 |
-
|
| 3141 |
-
|
| 3142 |
-
|
| 3143 |
-
|
| 3144 |
-
|
| 3145 |
-
|
| 3146 |
-
|
| 3147 |
-
|
| 3148 |
-
|
| 3149 |
-
|
| 3150 |
-
|
| 3151 |
-
plt.close(fig_2d)
|
| 3152 |
-
else:
|
| 3153 |
-
# 3D ์๋ฒ์ฌ์ด๋ ๋ ๋๋ง (WebGL ์ฌ์ฉ ์ ํจ!)
|
| 3154 |
-
with st.spinner("3D ๋ ๋๋ง ์ค..."):
|
| 3155 |
-
fig_3d = render_terrain_plotly(
|
| 3156 |
-
elevation,
|
| 3157 |
-
f"{selected_landform} - 3D",
|
| 3158 |
-
add_water=(landform_key in ["delta", "meander", "coastal_cliff", "fjord", "ria_coast", "spit_lagoon"]),
|
| 3159 |
-
water_level=0 if landform_key in ["delta", "coastal_cliff"] else -999,
|
| 3160 |
-
force_camera=True
|
| 3161 |
-
)
|
| 3162 |
-
img_bytes = render_3d_as_image(fig_3d, width=800, height=600)
|
| 3163 |
-
if img_bytes:
|
| 3164 |
-
st.image(img_bytes, caption=f"{selected_landform} - 3D (์๋ฒ ๋ ๋๋ง)", use_column_width=True)
|
| 3165 |
-
else:
|
| 3166 |
-
st.error("3D ๋ ๋๋ง์ ์คํจํ์ต๋๋ค.")
|
| 3167 |
|
| 3168 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3169 |
|
| 3170 |
# Educational Description
|
| 3171 |
descriptions = {
|
|
@@ -3226,42 +3257,54 @@ def main():
|
|
| 3226 |
anim_func = ANIMATED_LANDFORM_GENERATORS[landform_key]
|
| 3227 |
stage_elev = anim_func(gallery_grid_size, stage_value)
|
| 3228 |
|
| 3229 |
-
#
|
| 3230 |
-
|
|
|
|
| 3231 |
|
| 3232 |
-
|
| 3233 |
-
|
| 3234 |
-
|
| 3235 |
-
|
| 3236 |
-
|
| 3237 |
-
|
| 3238 |
-
|
| 3239 |
-
|
| 3240 |
-
|
| 3241 |
-
ax_2d.axis('off')
|
| 3242 |
-
plt.colorbar(im, ax=ax_2d, shrink=0.6, label='๊ณ ๋ (m)')
|
| 3243 |
-
st.pyplot(fig_2d)
|
| 3244 |
-
plt.close(fig_2d)
|
| 3245 |
-
else:
|
| 3246 |
-
# 3D ์๋ฒ์ฌ์ด๋ ๋ ๋๋ง (WebGL ์ฌ์ฉ ์ ํจ!)
|
| 3247 |
-
with st.spinner("3D ๋ ๋๋ง ์ค..."):
|
| 3248 |
-
stage_water = np.maximum(0, -stage_elev + 1.0)
|
| 3249 |
-
stage_water[stage_elev > 2] = 0
|
| 3250 |
-
fig_3d = render_terrain_plotly(
|
| 3251 |
-
stage_elev,
|
| 3252 |
-
f"{selected_landform} - {int(stage_value*100)}%",
|
| 3253 |
-
add_water=True,
|
| 3254 |
-
water_depth_grid=stage_water,
|
| 3255 |
-
water_level=-999,
|
| 3256 |
-
force_camera=True
|
| 3257 |
-
)
|
| 3258 |
-
img_bytes = render_3d_as_image(fig_3d, width=800, height=600)
|
| 3259 |
-
if img_bytes:
|
| 3260 |
-
st.image(img_bytes, caption=f"{selected_landform} - {int(stage_value*100)}% (3D)", use_column_width=True)
|
| 3261 |
-
else:
|
| 3262 |
-
st.error("3D ๋ ๋๋ง์ ์คํจํ์ต๋๋ค.")
|
| 3263 |
|
| 3264 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3265 |
|
| 3266 |
# 3. Scenarios Sub-tabs
|
| 3267 |
with t_scenarios:
|
|
@@ -3337,7 +3380,7 @@ def main():
|
|
| 3337 |
fig_step = render_terrain_plotly(r_step['elevation'],
|
| 3338 |
f"V์๊ณก ({t:,}๋
)",
|
| 3339 |
add_water=True, water_level=r_step['elevation'].min() + 3,
|
| 3340 |
-
texture_path="
|
| 3341 |
plot_container.plotly_chart(fig_step, use_container_width=True, key="v_plot_shared")
|
| 3342 |
anim_prog.progress(min(1.0, t / v_time))
|
| 3343 |
time.sleep(0.1)
|
|
@@ -3358,12 +3401,12 @@ def main():
|
|
| 3358 |
result['elevation'],
|
| 3359 |
f"V์๊ณก | ๊น์ด: {result['depth']:.0f}m | {v_time:,}๋
",
|
| 3360 |
add_water=True, water_level=result['elevation'].min() + 3,
|
| 3361 |
-
texture_path="
|
| 3362 |
water_depth_grid=result.get('water_depth')
|
| 3363 |
)
|
| 3364 |
plot_container.plotly_chart(plotly_fig, use_container_width=True, key="v_plot_shared")
|
| 3365 |
else:
|
| 3366 |
-
|
| 3367 |
caption="V์๊ณก - Google Earth ์คํ์ผ (AI ์์ฑ)",
|
| 3368 |
use_column_width=True)
|
| 3369 |
|
|
@@ -3425,7 +3468,7 @@ def main():
|
|
| 3425 |
r_step['elevation'],
|
| 3426 |
f"์์ ๊ณก๋ฅ ({t:,}๋
)",
|
| 3427 |
water_depth_grid=r_step['water_depth'],
|
| 3428 |
-
texture_path="
|
| 3429 |
)
|
| 3430 |
anim_chart.plotly_chart(fig_step, use_container_width=True, key=f"m_anim_{t}")
|
| 3431 |
|
|
@@ -3440,11 +3483,11 @@ def main():
|
|
| 3440 |
result['elevation'],
|
| 3441 |
f"์์ ๊ณก๋ฅ - {MEANDER_THEORIES[m_theory].get('description', '')[:20]}...",
|
| 3442 |
water_depth_grid=result['water_depth'],
|
| 3443 |
-
texture_path="
|
| 3444 |
)
|
| 3445 |
st.plotly_chart(fig, use_container_width=True, key="m_plot")
|
| 3446 |
else:
|
| 3447 |
-
|
| 3448 |
caption="๊ณก๋ฅ ํ์ฒ - Google Earth ์คํ์ผ (AI ์์ฑ)",
|
| 3449 |
use_column_width=True)
|
| 3450 |
|
|
@@ -3515,7 +3558,7 @@ def main():
|
|
| 3515 |
fig_step = render_terrain_plotly(r_step['elevation'],
|
| 3516 |
f"{r_step['delta_type']} ({t:,}๋
)",
|
| 3517 |
add_water=True, water_level=0,
|
| 3518 |
-
texture_path="
|
| 3519 |
plot_container.plotly_chart(fig_step, use_container_width=True, key="d_plot_shared")
|
| 3520 |
anim_prog.progress(min(1.0, t / d_time))
|
| 3521 |
# time.sleep(0.1)
|
|
@@ -3538,12 +3581,12 @@ def main():
|
|
| 3538 |
result['elevation'],
|
| 3539 |
f"{result['delta_type']} | ๋ฉด์ : {result['area']:.2f} kmยฒ | {d_time:,}๋
",
|
| 3540 |
add_water=True, water_level=0,
|
| 3541 |
-
texture_path="
|
| 3542 |
water_depth_grid=result.get('water_depth')
|
| 3543 |
)
|
| 3544 |
plot_container.plotly_chart(plotly_fig, use_container_width=True, key="d_plot_shared")
|
| 3545 |
else:
|
| 3546 |
-
|
| 3547 |
caption="์กฐ์กฑ์ ์ผ๊ฐ์ฃผ - Google Earth ์คํ์ผ (AI ์์ฑ)",
|
| 3548 |
use_column_width=True)
|
| 3549 |
|
|
@@ -3615,7 +3658,7 @@ def main():
|
|
| 3615 |
plt.close()
|
| 3616 |
else:
|
| 3617 |
st.caption("๐ฑ๏ธ **๋ง์ฐ์ค ๋๋๊ทธ๋ก ํ์ , ์คํฌ๋กค๋ก ์ค**")
|
| 3618 |
-
plotly_fig = render_terrain_plotly(result['elevation'], f"์ ์์ง | ๋ฉด์ : {result['area']:.2f}kmยฒ | {af_time:,}๋
", add_water=False, texture_path="
|
| 3619 |
plot_container.plotly_chart(plotly_fig, use_container_width=True, key="af_plot_shared")
|
| 3620 |
|
| 3621 |
# ํ์๋จ๊ตฌ
|
|
@@ -3769,7 +3812,7 @@ def main():
|
|
| 3769 |
plotly_fig = render_terrain_plotly(result['elevation'], f"{result['type']} | ๊น์ด: {result['depth']:.0f}m | {em_time:,}๋
", add_water=True, water_level=result['elevation'].min()+2, water_depth_grid=result.get('water_depth'))
|
| 3770 |
plot_container.plotly_chart(plotly_fig, use_container_width=True, key="em_plot_shared")
|
| 3771 |
else:
|
| 3772 |
-
|
| 3773 |
|
| 3774 |
# ๋ง์ํ์ฒ
|
| 3775 |
with river_sub[7]:
|
|
@@ -3801,10 +3844,10 @@ def main():
|
|
| 3801 |
|
| 3802 |
v_mode = st.radio("๋ณด๊ธฐ ๋ชจ๋", ["๐ฎ ์ธํฐ๋ํฐ๋ธ 3D", "๐ฐ๏ธ ์ฐธ๊ณ ์ฌ์ง"], horizontal=True, key="bs_v")
|
| 3803 |
if "3D" in v_mode:
|
| 3804 |
-
fig = render_terrain_plotly(result['elevation'], f"๋ง์ํ์ฒ ({bs_time}๋
)", add_water=True, water_level=result['elevation'].min()+0.5, texture_path="
|
| 3805 |
plot_container.plotly_chart(fig, use_container_width=True, key="bs_plot_shared")
|
| 3806 |
else:
|
| 3807 |
-
|
| 3808 |
|
| 3809 |
# ํญํฌ
|
| 3810 |
with river_sub[8]:
|
|
@@ -3837,7 +3880,7 @@ def main():
|
|
| 3837 |
fig = render_terrain_plotly(result['elevation'], f"ํญํฌ ({wf_time}๋
)", add_water=True, water_level=90, water_depth_grid=result.get('water_depth'))
|
| 3838 |
plot_container.plotly_chart(fig, use_container_width=True, key="wf_plot_shared")
|
| 3839 |
else:
|
| 3840 |
-
|
| 3841 |
|
| 3842 |
# ๋ฒ๋์ ์์ธ
|
| 3843 |
with river_sub[9]:
|
|
@@ -3870,7 +3913,7 @@ def main():
|
|
| 3870 |
fig = render_terrain_plotly(result['elevation'], f"๋ฒ๋์ ์์ธ ({lv_time}๋
)", add_water=True, water_level=42, water_depth_grid=result.get('water_depth'))
|
| 3871 |
plot_container.plotly_chart(fig, use_container_width=True, key="lv_plot_shared")
|
| 3872 |
else:
|
| 3873 |
-
|
| 3874 |
|
| 3875 |
# ===== ํด์ ์งํ =====
|
| 3876 |
with tab_coast:
|
|
@@ -4009,9 +4052,9 @@ def main():
|
|
| 4009 |
plot_container.plotly_chart(plotly_fig, use_container_width=True, key="co_plot_shared")
|
| 4010 |
else:
|
| 4011 |
if theory_key == "cliff_retreat":
|
| 4012 |
-
|
| 4013 |
elif theory_key in ["tombolo", "spit"]:
|
| 4014 |
-
|
| 4015 |
else:
|
| 4016 |
st.info("์ด ์งํ์ ๋ํ ์ฐธ๊ณ ์ฌ์ง์ด ์์ง ์์ต๋๋ค.")
|
| 4017 |
|
|
@@ -4057,7 +4100,7 @@ def main():
|
|
| 4057 |
f = render_terrain_plotly(result['elevation'], f"๋๋ฆฌ๋ค | {ka_time:,}๋
", add_water=False)
|
| 4058 |
plot_container.plotly_chart(f, use_container_width=True, key="ka_plot_shared")
|
| 4059 |
else:
|
| 4060 |
-
|
| 4061 |
|
| 4062 |
# ํ ์นด๋ฅด์คํธ
|
| 4063 |
with ka_subs[1]:
|
|
@@ -4090,10 +4133,10 @@ def main():
|
|
| 4090 |
plot_container.plotly_chart(f, use_container_width=True, key="tk_plot_shared")
|
| 4091 |
elif "3D" in v_mode:
|
| 4092 |
st.caption("๐ฑ๏ธ **๋ง์ฐ์ค ๋๋๊ทธ๋ก ํ์ /์ค**")
|
| 4093 |
-
f = render_terrain_plotly(result['elevation'], f"ํ ์นด๋ฅด์คํธ | {tk_time:,}๋
", add_water=False, texture_path="
|
| 4094 |
plot_container.plotly_chart(f, use_container_width=True, key="tk_plot_shared")
|
| 4095 |
else:
|
| 4096 |
-
|
| 4097 |
|
| 4098 |
# ์ํ๋๊ตด
|
| 4099 |
with ka_subs[2]:
|
|
@@ -4129,7 +4172,7 @@ def main():
|
|
| 4129 |
f = render_terrain_plotly(result['elevation'], f"์ํ๋๊ตด | {cv_time:,}๋
", add_water=False)
|
| 4130 |
plot_container.plotly_chart(f, use_container_width=True, key="cv_plot_shared")
|
| 4131 |
else:
|
| 4132 |
-
|
| 4133 |
|
| 4134 |
# ===== ํ์ฐ =====
|
| 4135 |
with tab_volcano:
|
|
@@ -4161,19 +4204,19 @@ def main():
|
|
| 4161 |
for _ in range(n_reps):
|
| 4162 |
for t in range(0, vo_time+1, max(1, vo_time//20)):
|
| 4163 |
r = simulate_volcanic(VOLCANIC_THEORIES[vo_theory]['key'], t, params, grid_size=grid_size)
|
| 4164 |
-
f = render_terrain_plotly(r['elevation'], f"{r['type']} ({t:,}๋
)", add_water=False, texture_path="
|
| 4165 |
plot_container.plotly_chart(f, use_container_width=True, key="vo_plot_shared")
|
| 4166 |
time.sleep(0.1)
|
| 4167 |
v_mode = st.radio("๋ณด๊ธฐ ๋ชจ๋", ["๐ฎ ์ธํฐ๋ํฐ๋ธ 3D", "๐ฐ๏ธ ์ฐธ๊ณ ์ฌ์ง"], horizontal=True, key="vo_v")
|
| 4168 |
if "3D" in v_mode:
|
| 4169 |
-
f = render_terrain_plotly(result['elevation'], f"{result['type']} ({vo_time:,}๋
)", add_water=False, texture_path="
|
| 4170 |
plot_container.plotly_chart(f, use_container_width=True, key="vo_plot_shared")
|
| 4171 |
else:
|
| 4172 |
# ํ์ฐ ์ ํ์ ๋ฐ๋ผ ๋ค๋ฅธ ์ด๋ฏธ์ง
|
| 4173 |
if "shield" in VOLCANIC_THEORIES[vo_theory]['key']:
|
| 4174 |
-
|
| 4175 |
else:
|
| 4176 |
-
|
| 4177 |
|
| 4178 |
# ์ฉ์ ๋์ง
|
| 4179 |
with vo_subs[1]:
|
|
@@ -4209,7 +4252,7 @@ def main():
|
|
| 4209 |
f = render_terrain_plotly(result['elevation'], f"์ฉ์๋์ง | {lp_time:,}๋
", add_water=False)
|
| 4210 |
plot_container.plotly_chart(f, use_container_width=True, key="lp_plot_shared")
|
| 4211 |
else:
|
| 4212 |
-
|
| 4213 |
|
| 4214 |
# ์ฃผ์์ ๋ฆฌ
|
| 4215 |
with vo_subs[2]:
|
|
@@ -4245,7 +4288,7 @@ def main():
|
|
| 4245 |
f = render_terrain_plotly(result['elevation'], f"์ฃผ์์ ๋ฆฌ | {cj_time:,}๋
", add_water=True, water_level=80)
|
| 4246 |
plot_container.plotly_chart(f, use_container_width=True, key="cj_plot_shared")
|
| 4247 |
else:
|
| 4248 |
-
|
| 4249 |
|
| 4250 |
# ===== ๋นํ =====
|
| 4251 |
with tab_glacial:
|
|
@@ -4275,18 +4318,18 @@ def main():
|
|
| 4275 |
for _ in range(n_reps):
|
| 4276 |
for t in range(0, gl_time+1, max(1, gl_time//20)):
|
| 4277 |
r = simulate_glacial(key, t, {'ice_thickness': gl_ice}, grid_size=grid_size)
|
| 4278 |
-
tex_path = "
|
| 4279 |
f = render_terrain_plotly(r['elevation'], f"{gl_type} ({t:,}๋
)", add_water=(key=="fjord"), water_level=100 if key=="fjord" else 0, texture_path=tex_path, force_camera=False)
|
| 4280 |
plot_container.plotly_chart(f, use_container_width=True, key="gl_plot_shared")
|
| 4281 |
time.sleep(0.1)
|
| 4282 |
|
| 4283 |
-
tex_path = "
|
| 4284 |
f = render_terrain_plotly(result['elevation'], f"{gl_type} ({gl_time:,}๋
)", add_water=(key=="fjord"), water_level=100 if key=="fjord" else 0, texture_path=tex_path)
|
| 4285 |
v_mode = st.radio("๋ณด๊ธฐ ๋ชจ๋", ["๐ฎ ์ธํฐ๋ํฐ๋ธ 3D", "๐ฐ๏ธ ์ฐธ๊ณ ์ฌ์ง"], horizontal=True, key="gl_v")
|
| 4286 |
if "3D" in v_mode:
|
| 4287 |
plot_container.plotly_chart(f, use_container_width=True, key="gl_plot_shared")
|
| 4288 |
else:
|
| 4289 |
-
|
| 4290 |
|
| 4291 |
# ๊ถ๊ณก
|
| 4292 |
with gl_subs[1]:
|
|
@@ -4319,10 +4362,10 @@ def main():
|
|
| 4319 |
plot_container.plotly_chart(f, use_container_width=True, key="cq_plot_shared")
|
| 4320 |
elif "3D" in v_mode:
|
| 4321 |
st.caption("๐ฑ๏ธ **๋ง์ฐ์ค ๋๋๊ทธ๋ก ํ์ /์ค**")
|
| 4322 |
-
f = render_terrain_plotly(result['elevation'], f"๊ถ๊ณก | {cq_time:,}๋
", add_water=False, texture_path="
|
| 4323 |
plot_container.plotly_chart(f, use_container_width=True, key="cq_plot_shared")
|
| 4324 |
else:
|
| 4325 |
-
|
| 4326 |
|
| 4327 |
# ๋ชจ๋ ์ธ
|
| 4328 |
with gl_subs[2]:
|
|
@@ -4358,7 +4401,7 @@ def main():
|
|
| 4358 |
f = render_terrain_plotly(result['elevation'], f"๋ชจ๋ ์ธ | {mo_time:,}๋
", add_water=False)
|
| 4359 |
plot_container.plotly_chart(f, use_container_width=True, key="mo_plot_shared")
|
| 4360 |
else:
|
| 4361 |
-
|
| 4362 |
|
| 4363 |
# ===== ๊ฑด์กฐ =====
|
| 4364 |
with tab_arid:
|
|
@@ -4419,7 +4462,7 @@ def main():
|
|
| 4419 |
# ๋ฐ๋ฅดํ ์ฌ๊ตฌ์ธ ๊ฒฝ์ฐ ํ
์ค์ฒ ์ ์ฉ
|
| 4420 |
tex_path = None
|
| 4421 |
if ARID_THEORIES[ar_theory]['key'] == "barchan":
|
| 4422 |
-
tex_path = "
|
| 4423 |
|
| 4424 |
plotly_fig = render_terrain_plotly(result['elevation'],
|
| 4425 |
f"{result['type']} | {ar_time:,}๋
",
|
|
@@ -4430,9 +4473,9 @@ def main():
|
|
| 4430 |
# ์ด๋ก ํค์ ๋ฐ๋ผ ์ด๋ฏธ์ง ๋ถ๊ธฐ
|
| 4431 |
tk = ARID_THEORIES[ar_theory]['key']
|
| 4432 |
if tk == "barchan":
|
| 4433 |
-
|
| 4434 |
elif tk == "mesa":
|
| 4435 |
-
|
| 4436 |
else:
|
| 4437 |
st.info("์ค๋น ์ค์
๋๋ค.")
|
| 4438 |
|
|
@@ -4584,7 +4627,7 @@ for i in range(steps):
|
|
| 4584 |
st.session_state['script_grid'] = WorldGrid(100, 100, 10.0)
|
| 4585 |
st.experimental_rerun()
|
| 4586 |
else:
|
| 4587 |
-
|
| 4588 |
|
| 4589 |
# ===== Project Genesis (Unified Engine) =====
|
| 4590 |
with tab_genesis:
|
|
|
|
| 66 |
""", unsafe_allow_html=True)
|
| 67 |
|
| 68 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
# ============ ์ด๋ก ์ ์ ============
|
| 70 |
|
| 71 |
V_VALLEY_THEORIES = {
|
|
|
|
| 2526 |
return fig
|
| 2527 |
|
| 2528 |
|
| 2529 |
+
def render_terrain_plotly(elevation, title, add_water=True, water_level=0, texture_path=None, force_camera=True, water_depth_grid=None, sediment_grid=None, landform_type=None):
|
| 2530 |
+
"""Plotly ์ธํฐ๋ํฐ๋ธ 3D Surface - ์ฌ์ค์ ํ
์ค์ฒ(Biome) ๋๋ ์์ฑ ์ด๋ฏธ์ง ์ ์ฉ
|
| 2531 |
+
|
| 2532 |
+
Args:
|
| 2533 |
+
landform_type: 'river', 'coastal', 'glacial', 'volcanic', 'karst', 'arid' ์ค ํ๋.
|
| 2534 |
+
None์ด๋ฉด ๊ธฐ์กด ๋ก์ง ์ฌ์ฉ.
|
| 2535 |
+
'glacial'๋ง ๋ง๋
์ค ํ์, ๋๋จธ์ง๋ ๋ฌผ/ํ/์์๋ง
|
| 2536 |
+
"""
|
| 2537 |
h, w = elevation.shape
|
| 2538 |
x = np.arange(w)
|
| 2539 |
y = np.arange(h)
|
|
|
|
| 2566 |
biome[is_deposit] = 0
|
| 2567 |
|
| 2568 |
# ์์ (๊ฒฝ์ฌ๊ฐ ๊ธํ ๊ณณ) - ์ ๋ฒฝ
|
| 2569 |
+
biome[slope > 1.2] = 2
|
| 2570 |
+
|
| 2571 |
+
# ์งํ ์ ํ๋ณ ์ฒ๋ฆฌ
|
| 2572 |
+
if landform_type == 'glacial':
|
| 2573 |
+
# ๋นํ ์งํ: ์ ์ฒด์ ์ผ๋ก ๋นํ/๋ ํ์ (๋์ ๊ณณ + U์๊ณก ๋ฐ๋ฅ๋)
|
| 2574 |
+
biome[elevation > 50] = 3 # ๋นํ ์งํ์ ์ ์ฒด์ ๋/๋นํ
|
| 2575 |
+
biome[slope > 1.5] = 2 # ๊ฐํ๋ฅธ ์ ๋ฒฝ๋ง ์์
|
| 2576 |
+
elif landform_type in ['river', 'coastal']:
|
| 2577 |
+
# ํ์ฒ/ํด์: ๋ฌผ ์์ญ ๋ช
์์ ํ์ (biome=0์ ๋ฌผ์์ผ๋ก)
|
| 2578 |
+
if water_depth_grid is not None:
|
| 2579 |
+
is_water = water_depth_grid > 0.5
|
| 2580 |
+
biome[is_water] = 0 # ๋ฌผ ์์ญ
|
| 2581 |
+
# ์์ ๊ณ ๋ = ๋ฐ๋ค/ํธ์
|
| 2582 |
+
biome[elevation < 0] = 0
|
| 2583 |
+
elif landform_type == 'arid':
|
| 2584 |
+
# ๊ฑด์กฐ: ์ ์ฒด ๋ชจ๋์
|
| 2585 |
+
biome[slope < 0.8] = 0 # ํํํ ๊ณณ์ ๋ชจ๋
|
| 2586 |
+
# else: ๊ธฐ๋ณธ (ํ์ฐ, ์นด๋ฅด์คํธ) - ๋ง๋
์ค ์์, ํ/์์๋ง
|
| 2587 |
|
| 2588 |
# ์กฐ๊ธ ๋ ์์ฐ์ค๋ฝ๊ฒ: ๋
ธ์ด์ฆ ์ถ๊ฐ (๊ฒฝ๊ณ๋ฉด ๋ธ๋ ๋ฉ ํจ๊ณผ ํ๋ด)
|
| 2589 |
noise = np.random.normal(0, 0.2, elevation.shape)
|
| 2590 |
biome_noisy = np.clip(biome + noise, 0, 3).round(2)
|
| 2591 |
|
| 2592 |
# ์ปค์คํ
์ปฌ๋ฌ์ค์ผ์ผ (Discrete)
|
| 2593 |
+
# 0: Soil/Sand (Yellowish), 1: Grass (Green), 2: Rock (Gray), 3: Snow/Water
|
| 2594 |
+
if landform_type == 'glacial':
|
| 2595 |
+
# ๋นํ ์งํ: ๋/๋นํ ํ์
|
| 2596 |
+
realistic_colorscale = [
|
| 2597 |
+
[0.0, '#E6C288'], [0.25, '#E6C288'], # Sand/Soil
|
| 2598 |
+
[0.25, '#556B2F'], [0.5, '#556B2F'], # Grass
|
| 2599 |
+
[0.5, '#808080'], [0.75, '#808080'], # Rock
|
| 2600 |
+
[0.75, '#E0FFFF'], [1.0, '#FFFFFF'] # Ice/Snow (๋ฐ์ ์ฒญ๋ฐฑ์)
|
| 2601 |
+
]
|
| 2602 |
+
colorbar_labels = ["ํด์ (ๅ)", "์์(่)", "์์(ๅฒฉ)", "๋นํ(ๆฐท)"]
|
| 2603 |
+
elif landform_type in ['river', 'coastal']:
|
| 2604 |
+
# ํ์ฒ/ํด์: ๋ฌผ ํ์ (ํ๋์)
|
| 2605 |
+
realistic_colorscale = [
|
| 2606 |
+
[0.0, '#4682B4'], [0.25, '#4682B4'], # Water (Steel Blue)
|
| 2607 |
+
[0.25, '#556B2F'], [0.5, '#556B2F'], # Grass
|
| 2608 |
+
[0.5, '#808080'], [0.75, '#808080'], # Rock
|
| 2609 |
+
[0.75, '#D2B48C'], [1.0, '#D2B48C'] # Sand (๋ฐ์ ๊ฐ์)
|
| 2610 |
+
]
|
| 2611 |
+
colorbar_labels = ["์์ญ(ๆฐด)", "์์(่)", "์์(ๅฒฉ)", "์ฌ์ง(็ )"]
|
| 2612 |
+
elif landform_type == 'arid':
|
| 2613 |
+
# ๊ฑด์กฐ ์งํ: ์ฌ๋ง ํ์ (๊ฐ์/์ฃผํฉ)
|
| 2614 |
+
realistic_colorscale = [
|
| 2615 |
+
[0.0, '#EDC9AF'], [0.25, '#EDC9AF'], # Desert Sand
|
| 2616 |
+
[0.25, '#CD853F'], [0.5, '#CD853F'], # Brown
|
| 2617 |
+
[0.5, '#808080'], [0.75, '#808080'], # Rock
|
| 2618 |
+
[0.75, '#DAA520'], [1.0, '#DAA520'] # Gold Sand
|
| 2619 |
+
]
|
| 2620 |
+
colorbar_labels = ["์ฌ๋ง(็ )", "์์ง(ๅท)", "์์(ๅฒฉ)", "๋ชจ๋(ๆฒ)"]
|
| 2621 |
+
else:
|
| 2622 |
+
# ๊ธฐ๋ณธ (ํ์ฐ, ์นด๋ฅด์คํธ ๋ฑ)
|
| 2623 |
+
realistic_colorscale = [
|
| 2624 |
+
[0.0, '#E6C288'], [0.25, '#E6C288'],
|
| 2625 |
+
[0.25, '#556B2F'], [0.5, '#556B2F'],
|
| 2626 |
+
[0.5, '#808080'], [0.75, '#808080'],
|
| 2627 |
+
[0.75, '#A0522D'], [1.0, '#A0522D'] # ๊ฐ์ (๋ง๋
์ค ์ ๊ฑฐ)
|
| 2628 |
+
]
|
| 2629 |
+
colorbar_labels = ["ํด์ (ๅ)", "์์(่)", "์์(ๅฒฉ)", "ํํ (ๅ)"]
|
| 2630 |
|
| 2631 |
# ์งํ ๋
ธ์ด์ฆ (Fractal Roughness) - ์๊ฐ์ ๋ํ
์ผ ์ถ๊ฐ
|
| 2632 |
+
visual_z = (elevation + np.random.normal(0, 0.2, elevation.shape)).round(2)
|
| 2633 |
|
| 2634 |
# ํ
์ค์ฒ ๋ก์ง (์ด๋ฏธ์ง ๋งคํ)
|
| 2635 |
final_surface_color = biome_noisy
|
|
|
|
| 2639 |
final_colorbar = dict(
|
| 2640 |
title=dict(text="์งํ ์ํ", font=dict(color='white')),
|
| 2641 |
tickvals=[0.37, 1.12, 1.87, 2.62],
|
| 2642 |
+
ticktext=colorbar_labels,
|
| 2643 |
tickfont=dict(color='white')
|
| 2644 |
)
|
| 2645 |
|
|
|
|
| 3159 |
elevation = np.zeros((gallery_grid_size, gallery_grid_size))
|
| 3160 |
|
| 3161 |
with col_view:
|
| 3162 |
+
# ๊ธฐ๋ณธ: 2D ํ๋ฉด๋ (matplotlib) - WebGL ์ปจํ
์คํธ ์ฌ์ฉ ์ ํจ
|
| 3163 |
+
import matplotlib.pyplot as plt
|
| 3164 |
+
import matplotlib.colors as mcolors
|
| 3165 |
|
| 3166 |
+
fig_2d, ax = plt.subplots(figsize=(8, 8))
|
| 3167 |
+
|
| 3168 |
+
# ์งํ ์์ ๋งต
|
| 3169 |
+
cmap = plt.cm.terrain
|
| 3170 |
+
|
| 3171 |
+
# ๋ฌผ์ด ์๋ ์งํ์ ํ๋์ ์ค๋ฒ๋ ์ด
|
| 3172 |
+
water_mask = elevation < 0
|
| 3173 |
+
|
| 3174 |
+
im = ax.imshow(elevation, cmap=cmap, origin='upper')
|
| 3175 |
+
|
| 3176 |
+
# ๋ฌผ ์์ญ ํ์
|
| 3177 |
+
if water_mask.any():
|
| 3178 |
+
water_overlay = np.ma.masked_where(~water_mask, np.ones_like(elevation))
|
| 3179 |
+
ax.imshow(water_overlay, cmap='Blues', alpha=0.6, origin='upper')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3180 |
|
| 3181 |
+
ax.set_title(f"{selected_landform}", fontsize=14)
|
| 3182 |
+
ax.axis('off')
|
| 3183 |
+
|
| 3184 |
+
# ์ปฌ๋ฌ๋ฐ
|
| 3185 |
+
cbar = plt.colorbar(im, ax=ax, shrink=0.6, label='๊ณ ๋ (m)')
|
| 3186 |
+
|
| 3187 |
+
st.pyplot(fig_2d)
|
| 3188 |
+
plt.close(fig_2d)
|
| 3189 |
+
|
| 3190 |
+
# 3D ๋ณด๊ธฐ (๋ฒํผ ํด๋ฆญ ์์๋ง)
|
| 3191 |
+
if st.button("๐ฒ 3D ๋ทฐ ๋ณด๊ธฐ", key="show_3d_view"):
|
| 3192 |
+
fig_3d = render_terrain_plotly(
|
| 3193 |
+
elevation,
|
| 3194 |
+
f"{selected_landform} - 3D",
|
| 3195 |
+
add_water=(landform_key in ["delta", "meander", "coastal_cliff", "fjord", "ria_coast", "spit_lagoon"]),
|
| 3196 |
+
water_level=0 if landform_key in ["delta", "coastal_cliff"] else -999,
|
| 3197 |
+
force_camera=True
|
| 3198 |
+
)
|
| 3199 |
+
st.plotly_chart(fig_3d, use_container_width=True)
|
| 3200 |
|
| 3201 |
# Educational Description
|
| 3202 |
descriptions = {
|
|
|
|
| 3257 |
anim_func = ANIMATED_LANDFORM_GENERATORS[landform_key]
|
| 3258 |
stage_elev = anim_func(gallery_grid_size, stage_value)
|
| 3259 |
|
| 3260 |
+
# ๋ฌผ ์์ฑ
|
| 3261 |
+
stage_water = np.maximum(0, -stage_elev + 1.0)
|
| 3262 |
+
stage_water[stage_elev > 2] = 0
|
| 3263 |
|
| 3264 |
+
# ํน์ ์งํ ๋ฌผ ์ฒ๋ฆฌ
|
| 3265 |
+
if landform_key == "alluvial_fan":
|
| 3266 |
+
apex_y = int(gallery_grid_size * 0.15)
|
| 3267 |
+
center = gallery_grid_size // 2
|
| 3268 |
+
for r in range(apex_y + 5):
|
| 3269 |
+
for dc in range(-2, 3):
|
| 3270 |
+
c = center + dc
|
| 3271 |
+
if 0 <= c < gallery_grid_size:
|
| 3272 |
+
stage_water[r, c] = 3.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3273 |
|
| 3274 |
+
# ๋จ์ผ 3D ๋ ๋๋ง (WebGL ์ปจํ
์คํธ ์ ์ฝ)
|
| 3275 |
+
fig_stage = render_terrain_plotly(
|
| 3276 |
+
stage_elev,
|
| 3277 |
+
f"{selected_landform} - {int(stage_value*100)}%",
|
| 3278 |
+
add_water=True,
|
| 3279 |
+
water_depth_grid=stage_water,
|
| 3280 |
+
water_level=-999,
|
| 3281 |
+
force_camera=True
|
| 3282 |
+
)
|
| 3283 |
+
st.plotly_chart(fig_stage, use_container_width=True, key="stage_view")
|
| 3284 |
+
|
| 3285 |
+
# ์๋ ์ฌ์ ๋ฒํผ
|
| 3286 |
+
if st.button("โถ๏ธ ์๋ ์ฌ์ (0%โ100%)", key="auto_play"):
|
| 3287 |
+
stage_container = st.empty()
|
| 3288 |
+
prog = st.progress(0)
|
| 3289 |
+
|
| 3290 |
+
for i in range(11):
|
| 3291 |
+
s = i / 10.0
|
| 3292 |
+
elev = anim_func(gallery_grid_size, s)
|
| 3293 |
+
water = np.maximum(0, -elev + 1.0)
|
| 3294 |
+
water[elev > 2] = 0
|
| 3295 |
+
|
| 3296 |
+
fig = render_terrain_plotly(
|
| 3297 |
+
elev, f"{selected_landform} - {int(s*100)}%",
|
| 3298 |
+
add_water=True, water_depth_grid=water,
|
| 3299 |
+
water_level=-999, force_camera=False
|
| 3300 |
+
)
|
| 3301 |
+
stage_container.plotly_chart(fig, use_container_width=True)
|
| 3302 |
+
prog.progress(s)
|
| 3303 |
+
|
| 3304 |
+
import time
|
| 3305 |
+
time.sleep(0.4)
|
| 3306 |
+
|
| 3307 |
+
st.success("โ
์๋ฃ!")
|
| 3308 |
|
| 3309 |
# 3. Scenarios Sub-tabs
|
| 3310 |
with t_scenarios:
|
|
|
|
| 3380 |
fig_step = render_terrain_plotly(r_step['elevation'],
|
| 3381 |
f"V์๊ณก ({t:,}๋
)",
|
| 3382 |
add_water=True, water_level=r_step['elevation'].min() + 3,
|
| 3383 |
+
texture_path="assets/reference/v_valley_texture.png", force_camera=False, water_depth_grid=r_step.get('water_depth'))
|
| 3384 |
plot_container.plotly_chart(fig_step, use_container_width=True, key="v_plot_shared")
|
| 3385 |
anim_prog.progress(min(1.0, t / v_time))
|
| 3386 |
time.sleep(0.1)
|
|
|
|
| 3401 |
result['elevation'],
|
| 3402 |
f"V์๊ณก | ๊น์ด: {result['depth']:.0f}m | {v_time:,}๋
",
|
| 3403 |
add_water=True, water_level=result['elevation'].min() + 3,
|
| 3404 |
+
texture_path="assets/reference/v_valley_texture.png",
|
| 3405 |
water_depth_grid=result.get('water_depth')
|
| 3406 |
)
|
| 3407 |
plot_container.plotly_chart(plotly_fig, use_container_width=True, key="v_plot_shared")
|
| 3408 |
else:
|
| 3409 |
+
st.image("assets/reference/v_valley_satellite_1765437288622.png",
|
| 3410 |
caption="V์๊ณก - Google Earth ์คํ์ผ (AI ์์ฑ)",
|
| 3411 |
use_column_width=True)
|
| 3412 |
|
|
|
|
| 3468 |
r_step['elevation'],
|
| 3469 |
f"์์ ๊ณก๋ฅ ({t:,}๋
)",
|
| 3470 |
water_depth_grid=r_step['water_depth'],
|
| 3471 |
+
texture_path="assets/reference/meander_texture.png"
|
| 3472 |
)
|
| 3473 |
anim_chart.plotly_chart(fig_step, use_container_width=True, key=f"m_anim_{t}")
|
| 3474 |
|
|
|
|
| 3483 |
result['elevation'],
|
| 3484 |
f"์์ ๊ณก๋ฅ - {MEANDER_THEORIES[m_theory].get('description', '')[:20]}...",
|
| 3485 |
water_depth_grid=result['water_depth'],
|
| 3486 |
+
texture_path="assets/reference/meander_texture.png"
|
| 3487 |
)
|
| 3488 |
st.plotly_chart(fig, use_container_width=True, key="m_plot")
|
| 3489 |
else:
|
| 3490 |
+
st.image("assets/reference/meander_satellite_1765437309640.png",
|
| 3491 |
caption="๊ณก๋ฅ ํ์ฒ - Google Earth ์คํ์ผ (AI ์์ฑ)",
|
| 3492 |
use_column_width=True)
|
| 3493 |
|
|
|
|
| 3558 |
fig_step = render_terrain_plotly(r_step['elevation'],
|
| 3559 |
f"{r_step['delta_type']} ({t:,}๋
)",
|
| 3560 |
add_water=True, water_level=0,
|
| 3561 |
+
texture_path="assets/reference/delta_texture.png", force_camera=False)
|
| 3562 |
plot_container.plotly_chart(fig_step, use_container_width=True, key="d_plot_shared")
|
| 3563 |
anim_prog.progress(min(1.0, t / d_time))
|
| 3564 |
# time.sleep(0.1)
|
|
|
|
| 3581 |
result['elevation'],
|
| 3582 |
f"{result['delta_type']} | ๋ฉด์ : {result['area']:.2f} kmยฒ | {d_time:,}๋
",
|
| 3583 |
add_water=True, water_level=0,
|
| 3584 |
+
texture_path="assets/reference/delta_texture.png",
|
| 3585 |
water_depth_grid=result.get('water_depth')
|
| 3586 |
)
|
| 3587 |
plot_container.plotly_chart(plotly_fig, use_container_width=True, key="d_plot_shared")
|
| 3588 |
else:
|
| 3589 |
+
st.image("assets/reference/delta_satellite_1765437326499.png",
|
| 3590 |
caption="์กฐ์กฑ์ ์ผ๊ฐ์ฃผ - Google Earth ์คํ์ผ (AI ์์ฑ)",
|
| 3591 |
use_column_width=True)
|
| 3592 |
|
|
|
|
| 3658 |
plt.close()
|
| 3659 |
else:
|
| 3660 |
st.caption("๐ฑ๏ธ **๋ง์ฐ์ค ๋๋๊ทธ๋ก ํ์ , ์คํฌ๋กค๋ก ์ค**")
|
| 3661 |
+
plotly_fig = render_terrain_plotly(result['elevation'], f"์ ์์ง | ๋ฉด์ : {result['area']:.2f}kmยฒ | {af_time:,}๋
", add_water=False, texture_path="assets/reference/alluvial_fan_texture.png", water_depth_grid=result.get('water_depth'))
|
| 3662 |
plot_container.plotly_chart(plotly_fig, use_container_width=True, key="af_plot_shared")
|
| 3663 |
|
| 3664 |
# ํ์๋จ๊ตฌ
|
|
|
|
| 3812 |
plotly_fig = render_terrain_plotly(result['elevation'], f"{result['type']} | ๊น์ด: {result['depth']:.0f}m | {em_time:,}๋
", add_water=True, water_level=result['elevation'].min()+2, water_depth_grid=result.get('water_depth'))
|
| 3813 |
plot_container.plotly_chart(plotly_fig, use_container_width=True, key="em_plot_shared")
|
| 3814 |
else:
|
| 3815 |
+
st.image("assets/reference/entrenched_meander_ref_1765496053723.png", caption="๊ฐ์
๊ณก๋ฅ (Entrenched Meander) - AI ์์ฑ", use_column_width=True)
|
| 3816 |
|
| 3817 |
# ๋ง์ํ์ฒ
|
| 3818 |
with river_sub[7]:
|
|
|
|
| 3844 |
|
| 3845 |
v_mode = st.radio("๋ณด๊ธฐ ๋ชจ๋", ["๐ฎ ์ธํฐ๋ํฐ๋ธ 3D", "๐ฐ๏ธ ์ฐธ๊ณ ์ฌ์ง"], horizontal=True, key="bs_v")
|
| 3846 |
if "3D" in v_mode:
|
| 3847 |
+
fig = render_terrain_plotly(result['elevation'], f"๋ง์ํ์ฒ ({bs_time}๋
)", add_water=True, water_level=result['elevation'].min()+0.5, texture_path="assets/reference/braided_river_texture.png", water_depth_grid=result.get('water_depth'))
|
| 3848 |
plot_container.plotly_chart(fig, use_container_width=True, key="bs_plot_shared")
|
| 3849 |
else:
|
| 3850 |
+
st.image("assets/reference/braided_river_1765410638302.png", caption="๋ง์ ํ์ฒ (AI ์์ฑ)", use_column_width=True)
|
| 3851 |
|
| 3852 |
# ํญํฌ
|
| 3853 |
with river_sub[8]:
|
|
|
|
| 3880 |
fig = render_terrain_plotly(result['elevation'], f"ํญํฌ ({wf_time}๋
)", add_water=True, water_level=90, water_depth_grid=result.get('water_depth'))
|
| 3881 |
plot_container.plotly_chart(fig, use_container_width=True, key="wf_plot_shared")
|
| 3882 |
else:
|
| 3883 |
+
st.image("assets/reference/waterfall_gorge_formation_1765410495876.png", caption="ํญํฌ ๋ฐ ํ๊ณก (AI ์์ฑ)", use_column_width=True)
|
| 3884 |
|
| 3885 |
# ๋ฒ๋์ ์์ธ
|
| 3886 |
with river_sub[9]:
|
|
|
|
| 3913 |
fig = render_terrain_plotly(result['elevation'], f"๋ฒ๋์ ์์ธ ({lv_time}๋
)", add_water=True, water_level=42, water_depth_grid=result.get('water_depth'))
|
| 3914 |
plot_container.plotly_chart(fig, use_container_width=True, key="lv_plot_shared")
|
| 3915 |
else:
|
| 3916 |
+
st.image("assets/reference/floodplain_landforms_1765436731483.png", caption="๋ฒ๋์ - ์์ฐ์ ๋ฐฉ๊ณผ ๋ฐฐํ์ต์ง (AI ์์ฑ)", use_column_width=True)
|
| 3917 |
|
| 3918 |
# ===== ํด์ ์งํ =====
|
| 3919 |
with tab_coast:
|
|
|
|
| 4052 |
plot_container.plotly_chart(plotly_fig, use_container_width=True, key="co_plot_shared")
|
| 4053 |
else:
|
| 4054 |
if theory_key == "cliff_retreat":
|
| 4055 |
+
st.image("assets/reference/sea_stack_arch_ref_1765495979396.png", caption="์์คํ & ํด์์์น - AI ์์ฑ", use_column_width=True)
|
| 4056 |
elif theory_key in ["tombolo", "spit"]:
|
| 4057 |
+
st.image("assets/reference/tombolo_sandbar_ref_1765495999194.png", caption="์ก๊ณ๋ & ์ฌ์ทจ - AI ์์ฑ", use_column_width=True)
|
| 4058 |
else:
|
| 4059 |
st.info("์ด ์งํ์ ๋ํ ์ฐธ๊ณ ์ฌ์ง์ด ์์ง ์์ต๋๋ค.")
|
| 4060 |
|
|
|
|
| 4100 |
f = render_terrain_plotly(result['elevation'], f"๋๋ฆฌ๋ค | {ka_time:,}๋
", add_water=False)
|
| 4101 |
plot_container.plotly_chart(f, use_container_width=True, key="ka_plot_shared")
|
| 4102 |
else:
|
| 4103 |
+
st.image("assets/reference/doline_sinkhole_1765436375545.png", caption="๋๋ฆฌ๋ค (AI ์์ฑ)", use_column_width=True)
|
| 4104 |
|
| 4105 |
# ํ ์นด๋ฅด์คํธ
|
| 4106 |
with ka_subs[1]:
|
|
|
|
| 4133 |
plot_container.plotly_chart(f, use_container_width=True, key="tk_plot_shared")
|
| 4134 |
elif "3D" in v_mode:
|
| 4135 |
st.caption("๐ฑ๏ธ **๋ง์ฐ์ค ๋๋๊ทธ๋ก ํ์ /์ค**")
|
| 4136 |
+
f = render_terrain_plotly(result['elevation'], f"ํ ์นด๋ฅด์คํธ | {tk_time:,}๋
", add_water=False, texture_path="assets/reference/tower_karst_texture.png")
|
| 4137 |
plot_container.plotly_chart(f, use_container_width=True, key="tk_plot_shared")
|
| 4138 |
else:
|
| 4139 |
+
st.image("assets/reference/tower_karst_ref.png", caption="ํ ์นด๋ฅด์คํธ (Guilin) - AI ์์ฑ", use_column_width=True)
|
| 4140 |
|
| 4141 |
# ์ํ๋๊ตด
|
| 4142 |
with ka_subs[2]:
|
|
|
|
| 4172 |
f = render_terrain_plotly(result['elevation'], f"์ํ๋๊ตด | {cv_time:,}๋
", add_water=False)
|
| 4173 |
plot_container.plotly_chart(f, use_container_width=True, key="cv_plot_shared")
|
| 4174 |
else:
|
| 4175 |
+
st.image("assets/reference/cave_ref.png", caption="์ํ๋๊ตด ๋ด๋ถ - AI ์์ฑ", use_column_width=True)
|
| 4176 |
|
| 4177 |
# ===== ํ์ฐ =====
|
| 4178 |
with tab_volcano:
|
|
|
|
| 4204 |
for _ in range(n_reps):
|
| 4205 |
for t in range(0, vo_time+1, max(1, vo_time//20)):
|
| 4206 |
r = simulate_volcanic(VOLCANIC_THEORIES[vo_theory]['key'], t, params, grid_size=grid_size)
|
| 4207 |
+
f = render_terrain_plotly(r['elevation'], f"{r['type']} ({t:,}๋
)", add_water=False, texture_path="assets/reference/volcano_texture.png", force_camera=False)
|
| 4208 |
plot_container.plotly_chart(f, use_container_width=True, key="vo_plot_shared")
|
| 4209 |
time.sleep(0.1)
|
| 4210 |
v_mode = st.radio("๋ณด๊ธฐ ๋ชจ๋", ["๐ฎ ์ธํฐ๋ํฐ๋ธ 3D", "๐ฐ๏ธ ์ฐธ๊ณ ์ฌ์ง"], horizontal=True, key="vo_v")
|
| 4211 |
if "3D" in v_mode:
|
| 4212 |
+
f = render_terrain_plotly(result['elevation'], f"{result['type']} ({vo_time:,}๋
)", add_water=False, texture_path="assets/reference/volcano_texture.png")
|
| 4213 |
plot_container.plotly_chart(f, use_container_width=True, key="vo_plot_shared")
|
| 4214 |
else:
|
| 4215 |
# ํ์ฐ ์ ํ์ ๋ฐ๋ผ ๋ค๋ฅธ ์ด๋ฏธ์ง
|
| 4216 |
if "shield" in VOLCANIC_THEORIES[vo_theory]['key']:
|
| 4217 |
+
st.image("assets/reference/shield_vs_stratovolcano_1765436448576.png", caption="์์ ํ์ฐ (AI ์์ฑ)", use_column_width=True)
|
| 4218 |
else:
|
| 4219 |
+
st.image("assets/reference/caldera_formation_1765436466778.png", caption="์นผ๋ฐ๋ผ (AI ์์ฑ)", use_column_width=True)
|
| 4220 |
|
| 4221 |
# ์ฉ์ ๋์ง
|
| 4222 |
with vo_subs[1]:
|
|
|
|
| 4252 |
f = render_terrain_plotly(result['elevation'], f"์ฉ์๋์ง | {lp_time:,}๋
", add_water=False)
|
| 4253 |
plot_container.plotly_chart(f, use_container_width=True, key="lp_plot_shared")
|
| 4254 |
else:
|
| 4255 |
+
st.image("assets/reference/lava_plateau_ref.png", caption="์ฉ์๋์ง (Iceland) - AI ์์ฑ", use_column_width=True)
|
| 4256 |
|
| 4257 |
# ์ฃผ์์ ๋ฆฌ
|
| 4258 |
with vo_subs[2]:
|
|
|
|
| 4288 |
f = render_terrain_plotly(result['elevation'], f"์ฃผ์์ ๋ฆฌ | {cj_time:,}๋
", add_water=True, water_level=80)
|
| 4289 |
plot_container.plotly_chart(f, use_container_width=True, key="cj_plot_shared")
|
| 4290 |
else:
|
| 4291 |
+
st.image("assets/reference/columnar_ref.png", caption="์ฃผ์์ ๋ฆฌ (Basalt Columns) - AI ์์ฑ", use_column_width=True)
|
| 4292 |
|
| 4293 |
# ===== ๋นํ =====
|
| 4294 |
with tab_glacial:
|
|
|
|
| 4318 |
for _ in range(n_reps):
|
| 4319 |
for t in range(0, gl_time+1, max(1, gl_time//20)):
|
| 4320 |
r = simulate_glacial(key, t, {'ice_thickness': gl_ice}, grid_size=grid_size)
|
| 4321 |
+
tex_path = "assets/reference/fjord_texture.png" if key == "fjord" else None
|
| 4322 |
f = render_terrain_plotly(r['elevation'], f"{gl_type} ({t:,}๋
)", add_water=(key=="fjord"), water_level=100 if key=="fjord" else 0, texture_path=tex_path, force_camera=False)
|
| 4323 |
plot_container.plotly_chart(f, use_container_width=True, key="gl_plot_shared")
|
| 4324 |
time.sleep(0.1)
|
| 4325 |
|
| 4326 |
+
tex_path = "assets/reference/fjord_texture.png" if key == "fjord" else None
|
| 4327 |
f = render_terrain_plotly(result['elevation'], f"{gl_type} ({gl_time:,}๋
)", add_water=(key=="fjord"), water_level=100 if key=="fjord" else 0, texture_path=tex_path)
|
| 4328 |
v_mode = st.radio("๋ณด๊ธฐ ๋ชจ๋", ["๐ฎ ์ธํฐ๋ํฐ๋ธ 3D", "๐ฐ๏ธ ์ฐธ๊ณ ์ฌ์ง"], horizontal=True, key="gl_v")
|
| 4329 |
if "3D" in v_mode:
|
| 4330 |
plot_container.plotly_chart(f, use_container_width=True, key="gl_plot_shared")
|
| 4331 |
else:
|
| 4332 |
+
st.image("assets/reference/fjord_valley_ref_1765495963491.png", caption="ํผ์ค๋ฅด (Fjord) - AI ์์ฑ", use_column_width=True)
|
| 4333 |
|
| 4334 |
# ๊ถ๊ณก
|
| 4335 |
with gl_subs[1]:
|
|
|
|
| 4362 |
plot_container.plotly_chart(f, use_container_width=True, key="cq_plot_shared")
|
| 4363 |
elif "3D" in v_mode:
|
| 4364 |
st.caption("๐ฑ๏ธ **๋ง์ฐ์ค ๋๋๊ทธ๋ก ํ์ /์ค**")
|
| 4365 |
+
f = render_terrain_plotly(result['elevation'], f"๊ถ๊ณก | {cq_time:,}๋
", add_water=False, texture_path="assets/reference/cirque_texture.png")
|
| 4366 |
plot_container.plotly_chart(f, use_container_width=True, key="cq_plot_shared")
|
| 4367 |
else:
|
| 4368 |
+
st.image("assets/reference/cirque_ref.png", caption="๊ถ๊ณก (Glacial Cirque) - AI ์์ฑ", use_column_width=True)
|
| 4369 |
|
| 4370 |
# ๋ชจ๋ ์ธ
|
| 4371 |
with gl_subs[2]:
|
|
|
|
| 4401 |
f = render_terrain_plotly(result['elevation'], f"๋ชจ๋ ์ธ | {mo_time:,}๋
", add_water=False)
|
| 4402 |
plot_container.plotly_chart(f, use_container_width=True, key="mo_plot_shared")
|
| 4403 |
else:
|
| 4404 |
+
st.image("assets/reference/moraine_ref.png", caption="๋ชจ๋ ์ธ (Moraine) - AI ์์ฑ", use_column_width=True)
|
| 4405 |
|
| 4406 |
# ===== ๊ฑด์กฐ =====
|
| 4407 |
with tab_arid:
|
|
|
|
| 4462 |
# ๋ฐ๋ฅดํ ์ฌ๊ตฌ์ธ ๊ฒฝ์ฐ ํ
์ค์ฒ ์ ์ฉ
|
| 4463 |
tex_path = None
|
| 4464 |
if ARID_THEORIES[ar_theory]['key'] == "barchan":
|
| 4465 |
+
tex_path = "assets/reference/barchan_dune_texture_topdown_1765496401371.png"
|
| 4466 |
|
| 4467 |
plotly_fig = render_terrain_plotly(result['elevation'],
|
| 4468 |
f"{result['type']} | {ar_time:,}๋
",
|
|
|
|
| 4473 |
# ์ด๋ก ํค์ ๋ฐ๋ผ ์ด๋ฏธ์ง ๋ถ๊ธฐ
|
| 4474 |
tk = ARID_THEORIES[ar_theory]['key']
|
| 4475 |
if tk == "barchan":
|
| 4476 |
+
st.image("assets/reference/barchan_dune_ref_1765496023768.png", caption="๋ฐ๋ฅดํ ์ฌ๊ตฌ - AI ์์ฑ", use_column_width=True)
|
| 4477 |
elif tk == "mesa":
|
| 4478 |
+
st.image("assets/reference/mesa_butte_ref_1765496038880.png", caption="๋ฉ์ฌ & ๋ทฐํธ - AI ์์ฑ", use_column_width=True)
|
| 4479 |
else:
|
| 4480 |
st.info("์ค๋น ์ค์
๋๋ค.")
|
| 4481 |
|
|
|
|
| 4627 |
st.session_state['script_grid'] = WorldGrid(100, 100, 10.0)
|
| 4628 |
st.experimental_rerun()
|
| 4629 |
else:
|
| 4630 |
+
st.image("assets/reference/peneplain_erosion_cycle_1765436750353.png", caption="ํ์ผ - ์คํ์ํ ๊ณผ์ (AI ์์ฑ)", use_column_width=True)
|
| 4631 |
|
| 4632 |
# ===== Project Genesis (Unified Engine) =====
|
| 4633 |
with tab_genesis:
|
app/pages/1_๐_Gallery.py
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
๐ ์ด์์ ์งํ ๊ฐค๋ฌ๋ฆฌ
|
| 3 |
+
31์ข
์ ๊ต๊ณผ์์ ์งํ์ ์๊ฐํํฉ๋๋ค.
|
| 4 |
+
"""
|
| 5 |
+
import streamlit as st
|
| 6 |
+
import numpy as np
|
| 7 |
+
import matplotlib.pyplot as plt
|
| 8 |
+
import sys
|
| 9 |
+
import os
|
| 10 |
+
|
| 11 |
+
# ์์ ๋๋ ํ ๋ฆฌ๋ฅผ ๊ฒฝ๋ก์ ์ถ๊ฐ
|
| 12 |
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
| 13 |
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
| 14 |
+
|
| 15 |
+
from engine.ideal_landforms import IDEAL_LANDFORM_GENERATORS, ANIMATED_LANDFORM_GENERATORS
|
| 16 |
+
from app.main import render_terrain_plotly
|
| 17 |
+
|
| 18 |
+
st.header("๐ ์ด์์ ์งํ ๊ฐค๋ฌ๋ฆฌ")
|
| 19 |
+
st.markdown("_๊ต๊ณผ์์ ์ธ ์งํ ํํ๋ฅผ ๊ธฐํํ์ ๋ชจ๋ธ๋ก ์๊ฐํํฉ๋๋ค._")
|
| 20 |
+
|
| 21 |
+
# ๊ฐ์กฐ ๋ฉ์์ง
|
| 22 |
+
st.info("๐ก **Tip:** ์งํ ์ ํ ํ **์๋๋ก ์คํฌ๋กค**ํ๋ฉด **๐ฌ ํ์ฑ ๊ณผ์ ์ ๋๋ฉ์ด์
**์ ํ์ธํ ์ ์์ต๋๋ค!")
|
| 23 |
+
|
| 24 |
+
# ์นดํ
๊ณ ๋ฆฌ๋ณ ์งํ
|
| 25 |
+
st.sidebar.subheader("๐๏ธ ์งํ ์นดํ
๊ณ ๋ฆฌ")
|
| 26 |
+
category = st.sidebar.radio("์นดํ
๊ณ ๋ฆฌ ์ ํ", [
|
| 27 |
+
"๐ ํ์ฒ ์งํ",
|
| 28 |
+
"๐บ ์ผ๊ฐ์ฃผ ์ ํ",
|
| 29 |
+
"โ๏ธ ๋นํ ์งํ",
|
| 30 |
+
"๐ ํ์ฐ ์งํ",
|
| 31 |
+
"๐ฆ ์นด๋ฅด์คํธ ์งํ",
|
| 32 |
+
"๐๏ธ ๊ฑด์กฐ ์งํ",
|
| 33 |
+
"๐๏ธ ํด์ ์งํ"
|
| 34 |
+
], key="gallery_cat")
|
| 35 |
+
|
| 36 |
+
# ์นดํ
๊ณ ๋ฆฌ โ landform_type ๋งคํ
|
| 37 |
+
CATEGORY_TO_TYPE = {
|
| 38 |
+
"๐ ํ์ฒ ์งํ": "river",
|
| 39 |
+
"๐บ ์ผ๊ฐ์ฃผ ์ ํ": "river",
|
| 40 |
+
"โ๏ธ ๋นํ ์งํ": "glacial",
|
| 41 |
+
"๐ ํ์ฐ ์งํ": "volcanic",
|
| 42 |
+
"๐ฆ ์นด๋ฅด์คํธ ์งํ": "karst",
|
| 43 |
+
"๐๏ธ ๊ฑด์กฐ ์งํ": "arid",
|
| 44 |
+
"๐๏ธ ํด์ ์งํ": "coastal"
|
| 45 |
+
}
|
| 46 |
+
landform_type = CATEGORY_TO_TYPE.get(category, None)
|
| 47 |
+
|
| 48 |
+
# ์นดํ
๊ณ ๋ฆฌ๋ณ ์ต์
|
| 49 |
+
if category == "๐ ํ์ฒ ์งํ":
|
| 50 |
+
landform_options = {
|
| 51 |
+
"๐ ์ ์์ง (Alluvial Fan)": "alluvial_fan",
|
| 52 |
+
"๐ ์์ ๊ณก๋ฅ (Free Meander)": "free_meander",
|
| 53 |
+
"โฐ๏ธ ๊ฐ์
๊ณก๋ฅ+ํ์๋จ๊ตฌ (Incised Meander)": "incised_meander",
|
| 54 |
+
"๐๏ธ V์๊ณก (V-Valley)": "v_valley",
|
| 55 |
+
"๐ ๋ง์ํ์ฒ (Braided River)": "braided_river",
|
| 56 |
+
"๐ง ํญํฌ (Waterfall)": "waterfall",
|
| 57 |
+
}
|
| 58 |
+
elif category == "๐บ ์ผ๊ฐ์ฃผ ์ ํ":
|
| 59 |
+
landform_options = {
|
| 60 |
+
"๐บ ์ผ๋ฐ ์ผ๊ฐ์ฃผ (Delta)": "delta",
|
| 61 |
+
"๐ฆถ ์กฐ์กฑ์ ์ผ๊ฐ์ฃผ (Bird-foot)": "bird_foot_delta",
|
| 62 |
+
"๐ ํธ์ ์ผ๊ฐ์ฃผ (Arcuate)": "arcuate_delta",
|
| 63 |
+
"๐ ์ฒจ๋์ ์ผ๊ฐ์ฃผ (Cuspate)": "cuspate_delta",
|
| 64 |
+
}
|
| 65 |
+
elif category == "โ๏ธ ๋นํ ์งํ":
|
| 66 |
+
landform_options = {
|
| 67 |
+
"โ๏ธ U์๊ณก (U-Valley)": "u_valley",
|
| 68 |
+
"๐ฅฃ ๊ถ๊ณก (Cirque)": "cirque",
|
| 69 |
+
"๐๏ธ ํธ๋ฅธ (Horn)": "horn",
|
| 70 |
+
"๐ ํผ์ค๋ฅด๋ (Fjord)": "fjord",
|
| 71 |
+
"๐ฅ ๋๋ผ๋ฆฐ (Drumlin)": "drumlin",
|
| 72 |
+
"๐ชจ ๋นํด์ (Moraine)": "moraine",
|
| 73 |
+
}
|
| 74 |
+
elif category == "๐ ํ์ฐ ์งํ":
|
| 75 |
+
landform_options = {
|
| 76 |
+
"๐ก๏ธ ์์ํ์ฐ (Shield)": "shield_volcano",
|
| 77 |
+
"๐ป ์ฑ์ธตํ์ฐ (Stratovolcano)": "stratovolcano",
|
| 78 |
+
"๐ณ๏ธ ์นผ๋ฐ๋ผ (Caldera)": "caldera",
|
| 79 |
+
"๐ง ํ๊ตฌํธ (Crater Lake)": "crater_lake",
|
| 80 |
+
"๐ซ ์ฉ์๋์ง (Lava Plateau)": "lava_plateau",
|
| 81 |
+
}
|
| 82 |
+
elif category == "๐ฆ ์นด๋ฅด์คํธ ์งํ":
|
| 83 |
+
landform_options = {
|
| 84 |
+
"๐ณ๏ธ ๋๋ฆฌ๋ค (Doline)": "karst_doline",
|
| 85 |
+
"๐ฅ ์ฐ๋ฐ๋ผ (Uvala)": "uvala",
|
| 86 |
+
"๐ผ ํ์นด๋ฅด์คํธ (Tower Karst)": "tower_karst",
|
| 87 |
+
"๐ชจ ์นด๋ (Karren)": "karren",
|
| 88 |
+
}
|
| 89 |
+
elif category == "๐๏ธ ๊ฑด์กฐ ์งํ":
|
| 90 |
+
landform_options = {
|
| 91 |
+
"๐ ๋ฐ๋ฅดํ ์ฌ๊ตฌ (Barchan)": "barchan",
|
| 92 |
+
"๐ฐ ํก์ฌ๊ตฌ (Transverse Dune)": "transverse_dune",
|
| 93 |
+
"โญ ์ฑ์ฌ๊ตฌ (Star Dune)": "star_dune",
|
| 94 |
+
"๐ฟ ๋ฉ์ฌ/๋ทฐํธ (Mesa/Butte)": "mesa_butte",
|
| 95 |
+
}
|
| 96 |
+
else: # ํด์ ์งํ
|
| 97 |
+
landform_options = {
|
| 98 |
+
"๐๏ธ ํด์ ์ ๋ฒฝ (Coastal Cliff)": "coastal_cliff",
|
| 99 |
+
"๐ ์ฌ์ทจ+์ํธ (Spit+Lagoon)": "spit_lagoon",
|
| 100 |
+
"๐๏ธ ์ก๊ณ์ฌ์ฃผ (Tombolo)": "tombolo",
|
| 101 |
+
"๐ ๋ฆฌ์์ค ํด์ (Ria Coast)": "ria_coast",
|
| 102 |
+
"๐ ํด์์์น (Sea Arch)": "sea_arch",
|
| 103 |
+
"๐๏ธ ํด์์ฌ๊ตฌ (Coastal Dune)": "coastal_dune",
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
col_sel, col_view = st.columns([1, 3])
|
| 107 |
+
|
| 108 |
+
with col_sel:
|
| 109 |
+
selected_landform = st.selectbox("์งํ ์ ํ", list(landform_options.keys()))
|
| 110 |
+
landform_key = landform_options[selected_landform]
|
| 111 |
+
|
| 112 |
+
st.markdown("---")
|
| 113 |
+
st.subheader("โ๏ธ ํ๋ผ๋ฏธํฐ")
|
| 114 |
+
|
| 115 |
+
gallery_grid_size = st.slider("ํด์๋", 50, 150, 80, 10, key="gallery_res")
|
| 116 |
+
|
| 117 |
+
# ๋์ ์งํ ์์ฑ
|
| 118 |
+
if landform_key in IDEAL_LANDFORM_GENERATORS:
|
| 119 |
+
generator = IDEAL_LANDFORM_GENERATORS[landform_key]
|
| 120 |
+
try:
|
| 121 |
+
elevation = generator(gallery_grid_size)
|
| 122 |
+
except TypeError:
|
| 123 |
+
elevation = generator(gallery_grid_size, 1.0)
|
| 124 |
+
else:
|
| 125 |
+
st.error(f"์งํ '{landform_key}' ์์ฑ๊ธฐ๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.")
|
| 126 |
+
elevation = np.zeros((gallery_grid_size, gallery_grid_size))
|
| 127 |
+
|
| 128 |
+
with col_view:
|
| 129 |
+
# 2D ํ๋ฉด๋
|
| 130 |
+
fig_2d, ax = plt.subplots(figsize=(8, 8))
|
| 131 |
+
cmap = plt.cm.terrain
|
| 132 |
+
water_mask = elevation < 0
|
| 133 |
+
|
| 134 |
+
im = ax.imshow(elevation, cmap=cmap, origin='upper')
|
| 135 |
+
|
| 136 |
+
if water_mask.any():
|
| 137 |
+
water_overlay = np.ma.masked_where(~water_mask, np.ones_like(elevation))
|
| 138 |
+
ax.imshow(water_overlay, cmap='Blues', alpha=0.6, origin='upper')
|
| 139 |
+
|
| 140 |
+
ax.set_title(f"{selected_landform}", fontsize=14)
|
| 141 |
+
ax.axis('off')
|
| 142 |
+
plt.colorbar(im, ax=ax, shrink=0.6, label='๊ณ ๋ (m)')
|
| 143 |
+
|
| 144 |
+
st.pyplot(fig_2d)
|
| 145 |
+
plt.close(fig_2d)
|
| 146 |
+
|
| 147 |
+
# 3D ๋ณด๊ธฐ ๋ฒํผ
|
| 148 |
+
if st.button("๐ฒ 3D ๋ทฐ ๋ณด๊ธฐ", key="show_3d_view"):
|
| 149 |
+
fig_3d = render_terrain_plotly(
|
| 150 |
+
elevation,
|
| 151 |
+
f"{selected_landform} - 3D",
|
| 152 |
+
add_water=(landform_key in ["delta", "meander", "coastal_cliff", "fjord", "ria_coast", "spit_lagoon"]),
|
| 153 |
+
water_level=0 if landform_key in ["delta", "coastal_cliff"] else -999,
|
| 154 |
+
force_camera=True,
|
| 155 |
+
landform_type=landform_type # ์นดํ
๊ณ ๋ฆฌ์ ๋ง๋ ์์ ์ ์ฉ
|
| 156 |
+
)
|
| 157 |
+
st.plotly_chart(fig_3d, use_container_width=True, key="gallery_3d")
|
| 158 |
+
|
| 159 |
+
# ์ค๋ช
|
| 160 |
+
descriptions = {
|
| 161 |
+
"delta": "**์ผ๊ฐ์ฃผ**: ํ์ฒ์ด ๋ฐ๋ค๋ ํธ์์ ์ ์
๋ ๋ ์ ์์ด ๊ฐ์ํ์ฌ ์ด๋ฐ ์ค์ด๋ ํด์ ๋ฌผ์ด ์์ฌ ํ์ฑ๋ฉ๋๋ค.",
|
| 162 |
+
"alluvial_fan": "**์ ์์ง**: ์ฐ์ง์์ ํ์ง๋ก ๋์ค๋ ๊ณณ์์ ๊ฒฝ์ฌ๊ฐ ๊ธ๊ฐํ์ฌ ์ด๋ฐ๋ ฅ์ด ์ค์ด๋ค๋ฉด์ ํด์ ๋ฌผ์ด ๋ถ์ฑ๊ผด๋ก ์์
๋๋ค.",
|
| 163 |
+
"free_meander": "**์์ ๊ณก๋ฅ**: ๋ฒ๋์ ์๋ฅผ ์์ ๋กญ๊ฒ ์ฌํํ๋ ๊ณก๋ฅ. ์์ฐ์ ๋ฐฉ(Levee)๊ณผ ๋ฐฐํ์ต์ง๊ฐ ํน์ง์
๋๋ค.",
|
| 164 |
+
"incised_meander": "**๊ฐ์
๊ณก๋ฅ**: ์ต๊ธฐ๋ก ์ธํด ๊ณก๋ฅ๊ฐ ๊ธฐ๋ฐ์์ ํ๊ณ ๋ค๋ฉด์ ํ์ฑ. ํ์๋จ๊ตฌ(River Terrace)๊ฐ ํจ๊ป ๋ํ๋ฉ๋๋ค.",
|
| 165 |
+
"v_valley": "**V์๊ณก**: ํ์ฒ์ ํ๋ฐฉ ์นจ์์ด ์ฐ์ธํ๊ฒ ์์ฉํ์ฌ ํ์ฑ๋ V์ ๋จ๋ฉด์ ๊ณจ์ง๊ธฐ.",
|
| 166 |
+
"braided_river": "**๋ง์ํ์ฒ**: ํด์ ๋ฌผ์ด ๋ง๊ณ ๊ฒฝ์ฌ๊ฐ ๊ธํ ๋ ์ฌ๋ฌ ์๋ก๊ฐ ๊ฐ๋ผ์ก๋ค ํฉ์ณ์ง๋ ํ์ฒ.",
|
| 167 |
+
"waterfall": "**ํญํฌ**: ๊ฒฝ์๊ณผ ์ฐ์์ ์ฐจ๋ณ์นจ์์ผ๋ก ํ์ฑ๋ ๊ธ๊ฒฝ์ฌ ๋์ฐจ. ํํดํ๋ฉฐ ํ๊ณก ํ์ฑ.",
|
| 168 |
+
"bird_foot_delta": "**์กฐ์กฑ์ ์ผ๊ฐ์ฃผ**: ๋ฏธ์์ํผ๊ฐํ. ํ๋ ์ฝํ๊ณ ํด์ ๋ฌผ ๊ณต๊ธ ๋ง์ ๋ ์๋ฐ ๋ชจ์์ผ๋ก ๊ธธ๊ฒ ๋ป์ต๋๋ค.",
|
| 169 |
+
"arcuate_delta": "**ํธ์ ์ผ๊ฐ์ฃผ**: ๋์ผ๊ฐํ. ํ๋๊ณผ ํด์ ๋ฌผ ๊ณต๊ธ์ด ๊ท ํ์ ์ด๋ฃจ์ด ๋ถ๋๋ฌ์ด ํธ(Arc) ํํ.",
|
| 170 |
+
"cuspate_delta": "**์ฒจ๋์ ์ผ๊ฐ์ฃผ**: ํฐ๋ฒ ๋ฅด๊ฐํ. ํ๋์ด ๊ฐํด ์ผ๊ฐ์ฃผ๊ฐ ๋พฐ์กฑํ ํ์ด์ด ๋ชจ์์ผ๋ก ํ์ฑ.",
|
| 171 |
+
"u_valley": "**U์๊ณก**: ๋นํ์ ์นจ์์ผ๋ก ํ์ฑ๋ U์ ๋จ๋ฉด์ ๊ณจ์ง๊ธฐ. ์ธก๋ฒฝ์ด ๊ธํ๊ณ ๋ฐ๋ฅ์ด ํํํฉ๋๋ค.",
|
| 172 |
+
"cirque": "**๊ถ๊ณก**: ๋นํ์ ์์์ . ๋ฐ์ํ ์ํน ํ์ธ ์งํ์ผ๋ก, ๋นํ ์ตํด ํ ํธ์(Tarn)๊ฐ ํ์ฑ๋ฉ๋๋ค.",
|
| 173 |
+
"horn": "**ํธ๋ฅธ**: ์ฌ๋ฌ ๊ถ๊ณก์ด ๋ง๋๋ ๊ณณ์์ ์นจ์๋์ง ์๊ณ ๋จ์ ๋พฐ์กฑํ ํผ๋ผ๋ฏธ๋ํ ๋ด์ฐ๋ฆฌ.",
|
| 174 |
+
"fjord": "**ํผ์ค๋ฅด๋**: ๋นํ๊ฐ ํ๋ธ U์๊ณก์ ๋ฐ๋ค๊ฐ ์ ์
๋ ์ข๊ณ ๊น์ ๋ง.",
|
| 175 |
+
"drumlin": "**๋๋ผ๋ฆฐ**: ๋นํ ํด์ ๋ฌผ์ด ๋นํ ํ๋ฆ ๋ฐฉํฅ์ผ๋ก ๊ธธ์ญํ๊ฒ ์์ธ ํ์ํ ์ธ๋.",
|
| 176 |
+
"moraine": "**๋นํด์**: ๋นํ๊ฐ ์ด๋ฐํ ์์ค์ด ํด์ ๋ ์งํ. ์ธกํด์, ์ข
ํด์ ๋ฑ์ด ์์ต๋๋ค.",
|
| 177 |
+
"shield_volcano": "**์์ํ์ฐ**: ์ ๋์ฑ ๋์ ํ๋ฌด์์ง ์ฉ์์ด ์๋งํ๊ฒ ์์ฌ ๋ฐฉํจ ํํ.",
|
| 178 |
+
"stratovolcano": "**์ฑ์ธตํ์ฐ**: ์ฉ์๊ณผ ํ์ฐ์์ค๋ฌผ์ด ๊ต๋๋ก ์์ฌ ๊ธํ ์๋ฟํ.",
|
| 179 |
+
"caldera": "**์นผ๋ฐ๋ผ**: ๋๊ท๋ชจ ๋ถํ ํ ๋ง๊ทธ๋ง๋ฐฉ ํจ๋ชฐ๋ก ํ์ฑ๋ ๊ฑฐ๋ํ ๋ถ์ง.",
|
| 180 |
+
"crater_lake": "**ํ๊ตฌํธ**: ํ๊ตฌ๋ ์นผ๋ฐ๋ผ์ ๋ฌผ์ด ๊ณ ์ฌ ํ์ฑ๋ ํธ์.",
|
| 181 |
+
"lava_plateau": "**์ฉ์๋์ง**: ์ด๊ทน ๋ถ์ถ๋ก ํ๋ฌด์์ง ์ฉ์์ด ๋๊ฒ ํผ์ณ์ ธ ํํํ ๋์ง ํ์ฑ.",
|
| 182 |
+
"barchan": "**๋ฐ๋ฅดํ ์ฌ๊ตฌ**: ๋ฐ๋์ด ํ ๋ฐฉํฅ์์ ๋ถ ๋ ํ์ฑ๋๋ ์ด์น๋ฌ ๋ชจ์์ ์ฌ๊ตฌ.",
|
| 183 |
+
"mesa_butte": "**๋ฉ์ฌ/๋ทฐํธ**: ์ฐจ๋ณ์นจ์์ผ๋ก ๋จ์ ํ์์ง. ๋ฉ์ฌ๋ ํฌ๊ณ ํํ, ๋ทฐํธ๋ ์๊ณ ๋์ต๋๋ค.",
|
| 184 |
+
"karst_doline": "**๋๋ฆฌ๋ค**: ์ํ์ ์ฉ์์ผ๋ก ํ์ฑ๋ ์ํน ํ์ธ ์์ง.",
|
| 185 |
+
"coastal_cliff": "**ํด์ ์ ๋ฒฝ**: ํ๋์ ์นจ์์ผ๋ก ํ์ฑ๋ ์ ๋ฒฝ.",
|
| 186 |
+
"spit_lagoon": "**์ฌ์ทจ+์ํธ**: ์ฐ์๋ฅ์ ์ํด ํด์ ๋ฌผ์ด ๊ธธ๊ฒ ์์ธ ์ฌ์ทจ๊ฐ ๋ง์ ๋ง์ ์ํธ๋ฅผ ํ์ฑํฉ๋๋ค.",
|
| 187 |
+
"tombolo": "**์ก๊ณ์ฌ์ฃผ**: ์ฐ์๋ฅ์ ์ํ ํด์ ์ผ๋ก ์ก์ง์ ์ฌ์ด ๋ชจ๋ํฑ์ผ๋ก ์ฐ๊ฒฐ๋ ์งํ.",
|
| 188 |
+
"ria_coast": "**๋ฆฌ์์ค์ ํด์**: ๊ณผ๊ฑฐ ํ๊ณก์ด ํด์๋ฉด ์์น์ผ๋ก ์นจ์๋์ด ํ์ฑ๋ ํฑ๋ ๋ชจ์ ํด์์ .",
|
| 189 |
+
"sea_arch": "**ํด์์์น**: ๊ณถ์์ ํ๋ ์นจ์์ผ๋ก ํ์ฑ๋ ์์นํ ์งํ.",
|
| 190 |
+
"coastal_dune": "**ํด์์ฌ๊ตฌ**: ํด๋น์ ๋ชจ๋๊ฐ ๋ฐ๋์ ์ํด ์ก์ง ์ชฝ์ผ๋ก ์ด๋ฐ๋์ด ํ์ฑ๋ ๋ชจ๋ ์ธ๋.",
|
| 191 |
+
# ์๋ก ์ถ๊ฐ๋ ์งํ
|
| 192 |
+
"uvala": "**์ฐ๋ฐ๋ผ**: ์ฌ๋ฌ ๋๋ฆฌ๋ค๊ฐ ํฉ๏ฟฝ๏ฟฝ์ ธ ํ์ฑ๋ ๋ณตํฉ ์์ง. ๋๋ฆฌ๋ค๋ณด๋ค ํฌ๊ณ ๋ถ๊ท์นํ ํํ.",
|
| 193 |
+
"tower_karst": "**ํ์นด๋ฅด์คํธ**: ์์ง ์ ๋ฒฝ์ ๊ฐ์ง ํ ๋ชจ์ ์ํ์ ๋ด์ฐ๋ฆฌ. ์ค๊ตญ ๊ตฌ์ด๋ฆฐ์ด ๋ํ์ .",
|
| 194 |
+
"karren": "**์นด๋ **: ๋น๋ฌผ์ ์ํ ์ฉ์์ผ๋ก ์ํ์ ํ๋ฉด์ ํ์ฑ๋ ํ๊ณผ ๋ฆฟ์ง. ํด๋ฆฐํธ/๊ทธ๋ผ์ดํฌ ํฌํจ.",
|
| 195 |
+
"transverse_dune": "**ํก์ฌ๊ตฌ**: ๋ฐ๋ ๋ฐฉํฅ์ ์์ง์ผ๋ก ๊ธธ๊ฒ ํ์ฑ๋ ์ฌ๊ตฌ์ด. ๋ชจ๋ ๊ณต๊ธ์ด ํ๋ถํ ๋ ๋ฐ๋ฌ.",
|
| 196 |
+
"star_dune": "**์ฑ์ฌ๊ตฌ**: ๋ค๋ฐฉํฅ ๋ฐ๋์ ์ํด ๋ณ ๋ชจ์์ผ๋ก ํ์ฑ๋ ์ฌ๊ตฌ. ๋์ด๊ฐ ๋๊ณ ์ด๋์ด ์ ์.",
|
| 197 |
+
}
|
| 198 |
+
st.info(descriptions.get(landform_key, "์ค๋ช
์ค๋น ์ค์
๋๋ค."))
|
| 199 |
+
|
| 200 |
+
# ========== ํ์ฑ ๊ณผ์ ์ ๋๋ฉ์ด์
==========
|
| 201 |
+
if landform_key in ANIMATED_LANDFORM_GENERATORS:
|
| 202 |
+
st.markdown("---")
|
| 203 |
+
st.subheader("๐ฌ ํ์ฑ ๊ณผ์ ")
|
| 204 |
+
|
| 205 |
+
# ์๋ ์ฌ์ ์ค์ด๋ฉด session_state์ stage ์ฌ์ฉ
|
| 206 |
+
if st.session_state.get('auto_playing', False):
|
| 207 |
+
stage_value = st.session_state.get('auto_stage', 0.0)
|
| 208 |
+
st.slider(
|
| 209 |
+
"ํ์ฑ ๋จ๊ณ (์๋ ์ฌ์ ์ค...)",
|
| 210 |
+
0.0, 1.0, stage_value, 0.05,
|
| 211 |
+
key="gallery_stage_slider",
|
| 212 |
+
disabled=True
|
| 213 |
+
)
|
| 214 |
+
else:
|
| 215 |
+
stage_value = st.slider(
|
| 216 |
+
"ํ์ฑ ๋จ๊ณ (0% = ์์, 100% = ์์ฑ)",
|
| 217 |
+
0.0, 1.0, 1.0, 0.05,
|
| 218 |
+
key="gallery_stage_slider"
|
| 219 |
+
)
|
| 220 |
+
|
| 221 |
+
anim_func = ANIMATED_LANDFORM_GENERATORS[landform_key]
|
| 222 |
+
stage_elev = anim_func(gallery_grid_size, stage_value)
|
| 223 |
+
|
| 224 |
+
# ๋ฌผ ์์ฑ
|
| 225 |
+
stage_water = np.maximum(0, -stage_elev + 1.0)
|
| 226 |
+
stage_water[stage_elev > 2] = 0
|
| 227 |
+
|
| 228 |
+
# ์ ์์ง ๋ฌผ ์ฒ๋ฆฌ
|
| 229 |
+
if landform_key == "alluvial_fan":
|
| 230 |
+
apex_y = int(gallery_grid_size * 0.15)
|
| 231 |
+
center = gallery_grid_size // 2
|
| 232 |
+
for r in range(apex_y + 5):
|
| 233 |
+
for dc in range(-2, 3):
|
| 234 |
+
c = center + dc
|
| 235 |
+
if 0 <= c < gallery_grid_size:
|
| 236 |
+
stage_water[r, c] = 3.0
|
| 237 |
+
|
| 238 |
+
# 3D ๋ ๋๋ง
|
| 239 |
+
fig_stage = render_terrain_plotly(
|
| 240 |
+
stage_elev,
|
| 241 |
+
f"{selected_landform} - {int(stage_value*100)}%",
|
| 242 |
+
add_water=True,
|
| 243 |
+
water_depth_grid=stage_water,
|
| 244 |
+
water_level=-999,
|
| 245 |
+
force_camera=False, # ์นด๋ฉ๋ผ ์ด๋ ํ์ฉ
|
| 246 |
+
landform_type=landform_type
|
| 247 |
+
)
|
| 248 |
+
st.plotly_chart(fig_stage, use_container_width=True, key="stage_view")
|
| 249 |
+
|
| 250 |
+
# ์๋ ์ฌ์ (์ธ์
์ํ ํ์ฉ)
|
| 251 |
+
col_play, col_step = st.columns(2)
|
| 252 |
+
with col_play:
|
| 253 |
+
if st.button("โถ๏ธ ์๋ ์ฌ์ ์์", key="auto_play"):
|
| 254 |
+
st.session_state['auto_playing'] = True
|
| 255 |
+
st.session_state['auto_stage'] = 0.0
|
| 256 |
+
with col_step:
|
| 257 |
+
if st.button("โน๏ธ ์ ์ง", key="stop_play"):
|
| 258 |
+
st.session_state['auto_playing'] = False
|
| 259 |
+
|
| 260 |
+
# ์๋ ์ฌ์ ์ค์ด๋ฉด stage ์๋ ์ฆ๊ฐ
|
| 261 |
+
if st.session_state.get('auto_playing', False):
|
| 262 |
+
current_stage = st.session_state.get('auto_stage', 0.0)
|
| 263 |
+
if current_stage < 1.0:
|
| 264 |
+
st.session_state['auto_stage'] = current_stage + 0.1
|
| 265 |
+
import time
|
| 266 |
+
time.sleep(0.5)
|
| 267 |
+
st.rerun()
|
| 268 |
+
else:
|
| 269 |
+
st.session_state['auto_playing'] = False
|
| 270 |
+
st.success("โ
์๋ฃ!")
|
| 271 |
+
|
| 272 |
+
st.caption("๐ก **Tip:** ์นด๋ฉ๋ผ ๊ฐ๋๋ฅผ ๋จผ์ ์กฐ์ ํ ํ ์๋ ์ฌ์ํ๋ฉด ์ ์ง๋ฉ๋๋ค.")
|
engine/ideal_landforms.py
CHANGED
|
@@ -484,58 +484,131 @@ def create_alluvial_fan_animated(grid_size: int, stage: float,
|
|
| 484 |
|
| 485 |
def create_meander_animated(grid_size: int, stage: float,
|
| 486 |
amplitude: float = 0.3, num_bends: int = 3) -> np.ndarray:
|
| 487 |
-
"""๊ณก๋ฅ ํ์ฑ๊ณผ์ ์ ๋๋ฉ์ด์
(์ง์
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 488 |
h, w = grid_size, grid_size
|
| 489 |
elevation = np.zeros((h, w))
|
| 490 |
-
elevation[:, :] = 10.0 # ๋ฒ๋์
|
| 491 |
|
| 492 |
center_x = w // 2
|
| 493 |
channel_width = max(3, w // 20)
|
| 494 |
|
| 495 |
-
# Stage์ ๋ฐ๋ฅธ ์ฌํ ์งํญ ๋ณํ
|
| 496 |
-
|
| 497 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 498 |
|
|
|
|
| 499 |
for r in range(h):
|
| 500 |
theta = 2 * np.pi * r / wl
|
| 501 |
meander_x = center_x + current_amp * np.sin(theta)
|
| 502 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 503 |
for c in range(w):
|
| 504 |
-
dist =
|
| 505 |
-
|
| 506 |
-
|
| 507 |
-
|
| 508 |
-
|
|
|
|
|
|
|
| 509 |
|
| 510 |
-
|
| 511 |
-
|
| 512 |
-
|
| 513 |
-
|
| 514 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 515 |
|
| 516 |
-
|
| 517 |
-
|
|
|
|
|
|
|
|
|
|
| 518 |
if 0 <= r < h:
|
| 519 |
-
|
| 520 |
-
ox_x = center_x + oxbow_amp * np.sin(theta)
|
| 521 |
for dc in range(-channel_width, channel_width + 1):
|
| 522 |
-
c =
|
| 523 |
if 0 <= c < w:
|
| 524 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 525 |
|
| 526 |
return elevation
|
| 527 |
|
| 528 |
|
| 529 |
def create_u_valley_animated(grid_size: int, stage: float,
|
| 530 |
valley_depth: float = 100.0, valley_width: float = 0.4) -> np.ndarray:
|
| 531 |
-
"""U์๊ณก ํ์ฑ๊ณผ์ (
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 532 |
h, w = grid_size, grid_size
|
| 533 |
elevation = np.zeros((h, w))
|
| 534 |
center = w // 2
|
| 535 |
|
| 536 |
-
# V์
|
| 537 |
-
|
| 538 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 539 |
|
| 540 |
for r in range(h):
|
| 541 |
for c in range(w):
|
|
@@ -546,14 +619,49 @@ def create_u_valley_animated(grid_size: int, stage: float,
|
|
| 546 |
elevation[r, c] = 0
|
| 547 |
else:
|
| 548 |
# V์์ U๋ก ์ ํ
|
| 549 |
-
# V: linear, U: parabolic
|
| 550 |
normalized_x = (dx - half_width) / max(1, w // 2 - half_width)
|
| 551 |
v_height = valley_depth * normalized_x # V shape
|
| 552 |
u_height = valley_depth * (normalized_x ** 2) # U shape
|
| 553 |
-
elevation[r, c] = v_height * (1 -
|
| 554 |
|
|
|
|
| 555 |
elevation[r, :] += (h - r) / h * 30.0
|
| 556 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 557 |
return elevation
|
| 558 |
|
| 559 |
|
|
@@ -642,47 +750,91 @@ def create_v_valley_animated(grid_size: int, stage: float,
|
|
| 642 |
|
| 643 |
def create_barchan_animated(grid_size: int, stage: float,
|
| 644 |
num_dunes: int = 3) -> np.ndarray:
|
| 645 |
-
"""๋ฐ๋ฅดํ ์ฌ๊ตฌ
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 646 |
h, w = grid_size, grid_size
|
| 647 |
elevation = np.zeros((h, w))
|
| 648 |
|
| 649 |
# ์ฌ๋ง ๊ธฐ๋ฐ๋ฉด
|
| 650 |
elevation[:, :] = 5.0
|
| 651 |
|
| 652 |
-
|
| 653 |
-
|
|
|
|
|
|
|
| 654 |
|
| 655 |
for i in range(num_dunes):
|
| 656 |
-
|
| 657 |
-
|
| 658 |
-
|
| 659 |
-
|
| 660 |
-
final_height = 15.0 + (i * 5.0)
|
| 661 |
-
dune_height = final_height * stage
|
| 662 |
|
| 663 |
-
|
| 664 |
-
dune_length = int((w // 5) * (0.3 + 0.7 * stage))
|
| 665 |
-
dune_width = int((w // 8) * (0.3 + 0.7 * stage))
|
| 666 |
-
|
| 667 |
-
if dune_length < 1 or dune_width < 1:
|
| 668 |
continue
|
| 669 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 670 |
for r in range(h):
|
| 671 |
for c in range(w):
|
| 672 |
dy = r - cy
|
| 673 |
dx = c - cx
|
| 674 |
|
| 675 |
-
|
|
|
|
| 676 |
|
| 677 |
-
|
| 678 |
-
|
| 679 |
-
|
| 680 |
-
|
| 681 |
-
|
| 682 |
-
|
| 683 |
-
|
| 684 |
-
|
|
|
|
| 685 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 686 |
return elevation
|
| 687 |
# ============================================
|
| 688 |
# ํ์ฅ ์งํ (Extended Landforms)
|
|
@@ -1121,35 +1273,85 @@ def create_spit_lagoon(grid_size: int = 100, stage: float = 1.0) -> np.ndarray:
|
|
| 1121 |
# ============================================
|
| 1122 |
|
| 1123 |
def create_fjord(grid_size: int = 100, stage: float = 1.0) -> np.ndarray:
|
| 1124 |
-
"""ํผ์ค๋ฅด๋ (Fjord) - ๋นํ
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1125 |
h, w = grid_size, grid_size
|
| 1126 |
elevation = np.zeros((h, w))
|
| 1127 |
|
| 1128 |
-
# ์ฐ์
์งํ
|
| 1129 |
-
elevation[:, :] =
|
| 1130 |
|
| 1131 |
center = w // 2
|
| 1132 |
-
valley_width = int(w * 0.
|
| 1133 |
-
|
| 1134 |
-
# U์๊ณก + ๋ฐ๋ค ์ ์
|
| 1135 |
-
sea_line = int(h * 0.7)
|
| 1136 |
|
|
|
|
| 1137 |
for r in range(h):
|
| 1138 |
for c in range(w):
|
| 1139 |
dx = abs(c - center)
|
| 1140 |
|
| 1141 |
if dx < valley_width:
|
| 1142 |
# U์ ๋ฐ๋ฅ
|
| 1143 |
-
|
| 1144 |
-
|
| 1145 |
-
|
| 1146 |
-
|
| 1147 |
-
|
| 1148 |
-
|
| 1149 |
-
|
| 1150 |
-
|
| 1151 |
-
|
| 1152 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1153 |
return elevation
|
| 1154 |
|
| 1155 |
|
|
@@ -1274,46 +1476,79 @@ def create_braided_river(grid_size: int = 100, stage: float = 1.0) -> np.ndarray
|
|
| 1274 |
|
| 1275 |
def create_waterfall(grid_size: int = 100, stage: float = 1.0,
|
| 1276 |
drop_height: float = 50.0) -> np.ndarray:
|
| 1277 |
-
"""ํญํฌ (Waterfall) -
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1278 |
h, w = grid_size, grid_size
|
| 1279 |
elevation = np.zeros((h, w))
|
| 1280 |
-
|
| 1281 |
center = w // 2
|
| 1282 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1283 |
|
| 1284 |
# ์๋ฅ (๋์ ๊ฒฝ์์ธต)
|
|
|
|
| 1285 |
for r in range(fall_r):
|
| 1286 |
for c in range(w):
|
| 1287 |
-
|
| 1288 |
-
|
| 1289 |
-
|
| 1290 |
-
|
|
|
|
|
|
|
|
|
|
| 1291 |
for c in range(w):
|
| 1292 |
-
t = (r - fall_r) /
|
| 1293 |
-
|
| 1294 |
-
|
| 1295 |
-
|
| 1296 |
-
|
|
|
|
| 1297 |
for c in range(w):
|
| 1298 |
-
|
| 1299 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1300 |
# ํ์ฒ ์๋ก
|
| 1301 |
for r in range(h):
|
| 1302 |
for dc in range(-4, 5):
|
| 1303 |
c = center + dc
|
| 1304 |
if 0 <= c < w:
|
| 1305 |
-
elevation[r, c] -=
|
| 1306 |
-
|
| 1307 |
-
# ํ๋ฐ์งํ (ํญํธ)
|
| 1308 |
-
pool_r = fall_r +
|
| 1309 |
-
|
| 1310 |
-
|
|
|
|
| 1311 |
r, c = pool_r + dr, center + dc
|
| 1312 |
if 0 <= r < h and 0 <= c < w:
|
| 1313 |
dist = np.sqrt(dr**2 + dc**2)
|
| 1314 |
-
if dist <
|
| 1315 |
-
|
| 1316 |
-
|
|
|
|
| 1317 |
return elevation
|
| 1318 |
|
| 1319 |
|
|
@@ -1344,34 +1579,48 @@ def create_karst_doline(grid_size: int = 100, stage: float = 1.0,
|
|
| 1344 |
|
| 1345 |
|
| 1346 |
def create_ria_coast(grid_size: int = 100, stage: float = 1.0) -> np.ndarray:
|
| 1347 |
-
"""๋ฆฌ์์ค์ ํด์ (Ria Coast) - ์นจ์๋ ํ๊ณก
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1348 |
h, w = grid_size, grid_size
|
| 1349 |
elevation = np.zeros((h, w))
|
| 1350 |
|
| 1351 |
-
# ์ฐ์ง ๋ฐฐ๊ฒฝ
|
| 1352 |
-
elevation[:, :] =
|
| 1353 |
|
| 1354 |
-
# ์ฌ๋ฌ ๊ฐ์ V์
|
| 1355 |
-
num_valleys =
|
| 1356 |
-
|
| 1357 |
|
| 1358 |
for i in range(num_valleys):
|
| 1359 |
-
valley_x =
|
|
|
|
|
|
|
| 1360 |
|
| 1361 |
for r in range(h):
|
| 1362 |
for c in range(w):
|
| 1363 |
dx = abs(c - valley_x)
|
| 1364 |
|
| 1365 |
-
if dx <
|
| 1366 |
-
# V์๊ณก
|
| 1367 |
-
|
| 1368 |
-
|
| 1369 |
|
| 1370 |
-
|
| 1371 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1372 |
for c in range(w):
|
| 1373 |
-
if elevation[r, c] <
|
| 1374 |
-
|
|
|
|
| 1375 |
|
| 1376 |
return elevation
|
| 1377 |
|
|
@@ -1416,40 +1665,64 @@ def create_tombolo(grid_size: int = 100, stage: float = 1.0) -> np.ndarray:
|
|
| 1416 |
|
| 1417 |
|
| 1418 |
def create_sea_arch(grid_size: int = 100, stage: float = 1.0) -> np.ndarray:
|
| 1419 |
-
"""ํด์์์น (Sea Arch) - ๋๊ตด์ด ๊ดํต
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1420 |
h, w = grid_size, grid_size
|
| 1421 |
elevation = np.zeros((h, w))
|
| 1422 |
|
| 1423 |
-
# ๋ฐ๋ค (
|
| 1424 |
-
sea_line = int(h * 0.
|
| 1425 |
-
elevation[sea_line:, :] = -
|
| 1426 |
|
| 1427 |
# ์ก์ง ์ ๋ฒฝ
|
| 1428 |
-
cliff_height =
|
| 1429 |
for r in range(sea_line):
|
| 1430 |
-
|
| 1431 |
-
|
| 1432 |
-
|
| 1433 |
-
|
|
|
|
|
|
|
| 1434 |
headland_cx = w // 2
|
| 1435 |
-
|
|
|
|
| 1436 |
|
| 1437 |
for r in range(sea_line, sea_line + headland_length):
|
| 1438 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1439 |
if 0 <= c < w:
|
| 1440 |
-
|
| 1441 |
-
|
| 1442 |
-
|
|
|
|
|
|
|
| 1443 |
arch_r = sea_line + int(headland_length * 0.5)
|
| 1444 |
-
|
|
|
|
| 1445 |
|
| 1446 |
-
for dr in range(-
|
| 1447 |
-
for dc in range(-arch_width
|
| 1448 |
-
r
|
|
|
|
|
|
|
| 1449 |
if 0 <= r < h and 0 <= c < w:
|
| 1450 |
-
|
| 1451 |
-
|
| 1452 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1453 |
return elevation
|
| 1454 |
|
| 1455 |
|
|
@@ -1481,30 +1754,75 @@ def create_crater_lake(grid_size: int = 100, stage: float = 1.0,
|
|
| 1481 |
|
| 1482 |
|
| 1483 |
def create_lava_plateau(grid_size: int = 100, stage: float = 1.0) -> np.ndarray:
|
| 1484 |
-
"""์ฉ์๋์ง (Lava Plateau) -
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1485 |
h, w = grid_size, grid_size
|
| 1486 |
elevation = np.zeros((h, w))
|
|
|
|
| 1487 |
|
| 1488 |
-
# ๊ธฐ๋ฐ
|
| 1489 |
-
|
| 1490 |
-
|
| 1491 |
-
|
| 1492 |
-
|
| 1493 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1494 |
|
| 1495 |
-
for r in range(
|
| 1496 |
-
for c in range(
|
| 1497 |
-
|
| 1498 |
-
noise = np.sin(r * 0.2) * np.cos(c * 0.2) * 2.0
|
| 1499 |
-
elevation[r, c] = plateau_height + noise
|
| 1500 |
|
| 1501 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1502 |
for r in range(h):
|
| 1503 |
for c in range(w):
|
| 1504 |
edge_dist = min(r, h - r - 1, c, w - c - 1)
|
| 1505 |
if edge_dist < margin:
|
| 1506 |
t = edge_dist / margin
|
| 1507 |
-
elevation[r, c] =
|
| 1508 |
|
| 1509 |
return elevation
|
| 1510 |
|
|
@@ -1545,6 +1863,206 @@ def create_coastal_dune(grid_size: int = 100, stage: float = 1.0,
|
|
| 1545 |
return elevation
|
| 1546 |
|
| 1547 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1548 |
# ์ ๋๋ฉ์ด์
์์ฑ๊ธฐ ๋งคํ
|
| 1549 |
ANIMATED_LANDFORM_GENERATORS = {
|
| 1550 |
'delta': create_delta_animated,
|
|
@@ -1580,6 +2098,12 @@ ANIMATED_LANDFORM_GENERATORS = {
|
|
| 1580 |
'crater_lake': create_crater_lake,
|
| 1581 |
'lava_plateau': create_lava_plateau,
|
| 1582 |
'coastal_dune': create_coastal_dune,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1583 |
}
|
| 1584 |
|
| 1585 |
# ์งํ ์์ฑ ํจ์ ๋งคํ
|
|
@@ -1617,5 +2141,11 @@ IDEAL_LANDFORM_GENERATORS = {
|
|
| 1617 |
'crater_lake': lambda gs: create_crater_lake(gs, 1.0),
|
| 1618 |
'lava_plateau': lambda gs: create_lava_plateau(gs, 1.0),
|
| 1619 |
'coastal_dune': lambda gs: create_coastal_dune(gs, 1.0),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1620 |
}
|
| 1621 |
|
|
|
|
| 484 |
|
| 485 |
def create_meander_animated(grid_size: int, stage: float,
|
| 486 |
amplitude: float = 0.3, num_bends: int = 3) -> np.ndarray:
|
| 487 |
+
"""๊ณก๋ฅ ํ์ฑ๊ณผ์ ์ ๋๋ฉ์ด์
(์ง์ โ ์ฌํ โ ์ฐ๊ฐํธ โ ํ์ค๋)
|
| 488 |
+
|
| 489 |
+
Stage 0.0~0.3: ์ง์ ํ์ฒ โ ์ฝํ ์ฌํ ์์
|
| 490 |
+
Stage 0.3~0.6: ์ฌํ ๋ฐ๋ฌ + ๊ณต๊ฒฉ์ฌ๋ฉด ์นจ์ + ํ์ฃผ์ฌ๋ฉด ํด์
|
| 491 |
+
Stage 0.6~0.8: ๊ณก๋ฅ ๋ชฉ ์ ๋จ โ ์ฐ๊ฐํธ ํ์ฑ
|
| 492 |
+
Stage 0.8~1.0: ํ์ค๋(river island) ํ์ฑ + ๊ตฌํ๋ ์์ ํ
|
| 493 |
+
"""
|
| 494 |
h, w = grid_size, grid_size
|
| 495 |
elevation = np.zeros((h, w))
|
| 496 |
+
elevation[:, :] = 10.0 # ๋ฒ๋์ ๊ธฐ์ค๋ฉด
|
| 497 |
|
| 498 |
center_x = w // 2
|
| 499 |
channel_width = max(3, w // 20)
|
| 500 |
|
| 501 |
+
# Stage์ ๋ฐ๋ฅธ ์ฌํ ์งํญ ๋ณํ
|
| 502 |
+
if stage < 0.6:
|
| 503 |
+
current_amp = w * amplitude * (stage / 0.6)
|
| 504 |
+
else:
|
| 505 |
+
current_amp = w * amplitude # ์ต๋ ์งํญ ์ ์ง
|
| 506 |
+
|
| 507 |
+
wl = h / num_bends # ํ์ฅ
|
| 508 |
|
| 509 |
+
# ๋ฉ์ธ ํ์ฒ ๊ทธ๋ฆฌ๊ธฐ
|
| 510 |
for r in range(h):
|
| 511 |
theta = 2 * np.pi * r / wl
|
| 512 |
meander_x = center_x + current_amp * np.sin(theta)
|
| 513 |
|
| 514 |
+
# ๊ณต๊ฒฉ์ฌ๋ฉด (attack slope) - ๋ฐ๊นฅ์ชฝ, ์นจ์
|
| 515 |
+
# ํ์ฃผ์ฌ๋ฉด (slip-off slope) - ์์ชฝ, ํด์
|
| 516 |
+
dtheta = np.cos(theta) # ๊ณก๋ฅ ๋ฐฉํฅ
|
| 517 |
+
|
| 518 |
for c in range(w):
|
| 519 |
+
dist = c - meander_x
|
| 520 |
+
|
| 521 |
+
# ํ์ฒ ์ฑ๋
|
| 522 |
+
if abs(dist) < channel_width:
|
| 523 |
+
# ์์ฌ (์ค์์ด ๊น์)
|
| 524 |
+
depth_factor = 1 - (abs(dist) / channel_width)
|
| 525 |
+
elevation[r, c] = 5.0 - depth_factor * 3.0 # 2~5m
|
| 526 |
|
| 527 |
+
# ๊ณต๊ฒฉ์ฌ๋ฉด (์ธ์ธก) - ์ ๋ฒฝ
|
| 528 |
+
elif dist * dtheta > 0 and abs(dist) < channel_width * 2:
|
| 529 |
+
# ์ธ์ธก์ ์นจ์์ผ๋ก ๊ฐํ๋ฆ
|
| 530 |
+
erosion_factor = (abs(dist) - channel_width) / channel_width
|
| 531 |
+
elevation[r, c] = 8.0 + erosion_factor * 3.0
|
| 532 |
+
|
| 533 |
+
# ํ์ฃผ์ฌ๋ฉด (๋ด์ธก) - ํฌ์ธํธ๋ฐ
|
| 534 |
+
elif dist * dtheta < 0 and abs(dist) < channel_width * 3:
|
| 535 |
+
# ๋ด์ธก์ ํด์ ์ผ๋ก ์๋ง
|
| 536 |
+
deposit_factor = (abs(dist) - channel_width) / (channel_width * 2)
|
| 537 |
+
elevation[r, c] = 6.0 + deposit_factor * 4.0
|
| 538 |
+
|
| 539 |
+
# ์์ฐ์ ๋ฐฉ (levee)
|
| 540 |
+
elif abs(dist) < channel_width * 4:
|
| 541 |
+
levee_height = 11.0 - (abs(dist) - channel_width * 2) * 0.5
|
| 542 |
+
elevation[r, c] = max(levee_height, 10.0)
|
| 543 |
+
|
| 544 |
+
# ์ฐ๊ฐํธ ํ์ฑ (stage > 0.6)
|
| 545 |
+
if stage > 0.6:
|
| 546 |
+
oxbow_intensity = min((stage - 0.6) / 0.2, 1.0)
|
| 547 |
|
| 548 |
+
# ๊ณก๋ฅ ๋ชฉ ์ง์ ํ (cutoff)
|
| 549 |
+
cutoff_y = int(h * 0.5)
|
| 550 |
+
cutoff_width = int(wl * 0.3)
|
| 551 |
+
|
| 552 |
+
for r in range(cutoff_y - cutoff_width // 2, cutoff_y + cutoff_width // 2):
|
| 553 |
if 0 <= r < h:
|
| 554 |
+
# ์ง์ ์ฑ๋
|
|
|
|
| 555 |
for dc in range(-channel_width, channel_width + 1):
|
| 556 |
+
c = center_x + dc
|
| 557 |
if 0 <= c < w:
|
| 558 |
+
new_elev = 4.0 * oxbow_intensity + elevation[r, c] * (1 - oxbow_intensity)
|
| 559 |
+
elevation[r, c] = new_elev
|
| 560 |
+
|
| 561 |
+
# ๊ตฌํ๋ (์ฐ๊ฐํธ) - ๋ฌผ์ด ๊ณ ์ธ ๊ณณ
|
| 562 |
+
for r in range(cutoff_y - int(wl * 0.4), cutoff_y + int(wl * 0.4)):
|
| 563 |
+
if 0 <= r < h:
|
| 564 |
+
theta = 2 * np.pi * r / wl
|
| 565 |
+
old_channel_x = center_x + current_amp * np.sin(theta)
|
| 566 |
+
|
| 567 |
+
# ๊ตฌํ๋๊ฐ ๋ฉ์ธ ์ฑ๋๊ณผ ๊ฒน์น์ง ์๋ ๊ณณ๋ง
|
| 568 |
+
if abs(old_channel_x - center_x) > channel_width * 2:
|
| 569 |
+
for dc in range(-channel_width, channel_width + 1):
|
| 570 |
+
c = int(old_channel_x + dc)
|
| 571 |
+
if 0 <= c < w:
|
| 572 |
+
# ๊ตฌํ๋๋ ๋ฌผ์ด ๊ณ ์ฌ ๋ฎ์
|
| 573 |
+
elevation[r, c] = 3.0 * oxbow_intensity + elevation[r, c] * (1 - oxbow_intensity)
|
| 574 |
+
|
| 575 |
+
# ํ์ค๋ ํ์ฑ (stage > 0.8)
|
| 576 |
+
if stage > 0.8:
|
| 577 |
+
island_intensity = (stage - 0.8) / 0.2
|
| 578 |
+
|
| 579 |
+
# ํ๋ฅ์ ํ์ค๋ ์์ฑ
|
| 580 |
+
island_y = int(h * 0.75)
|
| 581 |
+
island_size = max(3, channel_width // 2)
|
| 582 |
+
|
| 583 |
+
for dy in range(-island_size, island_size + 1):
|
| 584 |
+
for dx in range(-island_size, island_size + 1):
|
| 585 |
+
if dy**2 + dx**2 < island_size**2:
|
| 586 |
+
r, c = island_y + dy, center_x + dx
|
| 587 |
+
if 0 <= r < h and 0 <= c < w:
|
| 588 |
+
elevation[r, c] = 7.0 * island_intensity + elevation[r, c] * (1 - island_intensity)
|
| 589 |
|
| 590 |
return elevation
|
| 591 |
|
| 592 |
|
| 593 |
def create_u_valley_animated(grid_size: int, stage: float,
|
| 594 |
valley_depth: float = 100.0, valley_width: float = 0.4) -> np.ndarray:
|
| 595 |
+
"""U์๊ณก ํ์ฑ๊ณผ์ (๋นํ ์ฑ์ฅ โ ์นจ์ โ ๋นํ ํํด โ U์๊ณก)
|
| 596 |
+
|
| 597 |
+
Stage 0.0~0.3: ๋นํ ์ฑ์ฅ (V์๊ณก์ ๋นํ ์ฑ์์ง)
|
| 598 |
+
Stage 0.3~0.6: ๋นํ ์นจ์ (U์ ํํ๋ก ๋ณํ)
|
| 599 |
+
Stage 0.6~1.0: ๋นํ ํํด (๋นํ ๋
น์ผ๋ฉด์ U์๊ณก ๋๋ฌ๋จ)
|
| 600 |
+
"""
|
| 601 |
h, w = grid_size, grid_size
|
| 602 |
elevation = np.zeros((h, w))
|
| 603 |
center = w // 2
|
| 604 |
|
| 605 |
+
# 1๋จ๊ณ: V์๊ณก โ U์๊ณก ๋ณํ (์นจ์)
|
| 606 |
+
if stage < 0.6:
|
| 607 |
+
u_factor = min(stage / 0.6, 1.0) # 0~1๋ก ์ ๊ทํ
|
| 608 |
+
else:
|
| 609 |
+
u_factor = 1.0 # ์์ U์
|
| 610 |
+
|
| 611 |
+
half_width = int(w * valley_width / 2) * u_factor # U ๋ฐ๋ฅ ๋๋น
|
| 612 |
|
| 613 |
for r in range(h):
|
| 614 |
for c in range(w):
|
|
|
|
| 619 |
elevation[r, c] = 0
|
| 620 |
else:
|
| 621 |
# V์์ U๋ก ์ ํ
|
|
|
|
| 622 |
normalized_x = (dx - half_width) / max(1, w // 2 - half_width)
|
| 623 |
v_height = valley_depth * normalized_x # V shape
|
| 624 |
u_height = valley_depth * (normalized_x ** 2) # U shape
|
| 625 |
+
elevation[r, c] = v_height * (1 - u_factor) + u_height * u_factor
|
| 626 |
|
| 627 |
+
# ์๋ฅ๋ก ๊ฐ์๋ก ๋์์ง
|
| 628 |
elevation[r, :] += (h - r) / h * 30.0
|
| 629 |
+
|
| 630 |
+
# 2๋จ๊ณ: ๋นํ ์ถ๊ฐ (stage์ ๋ฐ๋ผ ์ฑ์ฅ/ํํด)
|
| 631 |
+
# stage 0~0.3: ๋นํ ์ฑ์ฅ (ํ๋ฅ๋ก ์ ์ง)
|
| 632 |
+
# stage 0.3~0.6: ์ต๋ ๋ฒ์
|
| 633 |
+
# stage 0.6~1.0: ๋นํ ํํด (์๋ฅ๋ก ํํด)
|
| 634 |
+
|
| 635 |
+
glacier_grid = np.zeros((h, w))
|
| 636 |
+
|
| 637 |
+
if stage < 0.3:
|
| 638 |
+
# ๋นํ ์ฑ์ฅ: ์๋ฅ์์ ํ๋ฅ๋ก ์ ์ง
|
| 639 |
+
glacier_extent = int(h * (stage / 0.3) * 0.8) # ์ต๋ 80%๊น์ง ์ ์ง
|
| 640 |
+
glacier_start = 0
|
| 641 |
+
glacier_end = glacier_extent
|
| 642 |
+
elif stage < 0.6:
|
| 643 |
+
# ์ต๋ ๋นํ ๋ฒ์
|
| 644 |
+
glacier_start = 0
|
| 645 |
+
glacier_end = int(h * 0.8)
|
| 646 |
+
else:
|
| 647 |
+
# ๋นํ ํํด
|
| 648 |
+
retreat_factor = (stage - 0.6) / 0.4
|
| 649 |
+
glacier_start = int(h * 0.8 * retreat_factor) # ํ๋ฅ์์ ๋
น์
|
| 650 |
+
glacier_end = int(h * 0.8 * (1 - retreat_factor * 0.5)) # ์๋ฅ๋ ์ค์ด๋ฆ
|
| 651 |
+
|
| 652 |
+
# ๋นํ ํ์ (๊ณจ์ง๊ธฐ ์ฑ์)
|
| 653 |
+
for r in range(glacier_start, min(glacier_end, h)):
|
| 654 |
+
for c in range(w):
|
| 655 |
+
dx = abs(c - center)
|
| 656 |
+
if dx < half_width + 5: # U์๊ณก ๋ฐ๋ฅ + ์ฝ๊ฐ ๋๊ฒ
|
| 657 |
+
glacier_thickness = 20.0 * (1 - abs(c - center) / (half_width + 5))
|
| 658 |
+
if stage < 0.6:
|
| 659 |
+
elevation[r, c] += glacier_thickness
|
| 660 |
+
else:
|
| 661 |
+
# ํํด ์ค: ๋นํ ๋์ด ๊ฐ์
|
| 662 |
+
retreat_factor = (stage - 0.6) / 0.4
|
| 663 |
+
elevation[r, c] += glacier_thickness * (1 - retreat_factor)
|
| 664 |
+
|
| 665 |
return elevation
|
| 666 |
|
| 667 |
|
|
|
|
| 750 |
|
| 751 |
def create_barchan_animated(grid_size: int, stage: float,
|
| 752 |
num_dunes: int = 3) -> np.ndarray:
|
| 753 |
+
"""๋ฐ๋ฅดํ ์ฌ๊ตฌ ์ด๋ ์ ๋๋ฉ์ด์
|
| 754 |
+
|
| 755 |
+
์์์ ๋ณผ ๋ ์ด์น๋ฌ(๐) ๋ชจ์:
|
| 756 |
+
- ๋ณผ๋ก๋ฉด(convex): ๋ฐ๋ ๋ถ์ด์ค๋ ์ชฝ (์๋จ)
|
| 757 |
+
- ์ค๋ชฉ๋ฉด(concave): ๋ฐ๋ ๊ฐ๋ ์ชฝ (ํ๋จ) + ๋ฟ
|
| 758 |
+
- ๋ฟ(horns): ๋ฐ๋ ๋ฐฉํฅ์ผ๋ก ๋ป์
|
| 759 |
+
|
| 760 |
+
๋ฐ๋ ๋ฐฉํฅ: ์ โ ์๋
|
| 761 |
+
"""
|
| 762 |
h, w = grid_size, grid_size
|
| 763 |
elevation = np.zeros((h, w))
|
| 764 |
|
| 765 |
# ์ฌ๋ง ๊ธฐ๋ฐ๋ฉด
|
| 766 |
elevation[:, :] = 5.0
|
| 767 |
|
| 768 |
+
np.random.seed(42)
|
| 769 |
+
|
| 770 |
+
# ์ฌ๊ตฌ ์ด๋
|
| 771 |
+
move_distance = int(h * 0.5 * stage)
|
| 772 |
|
| 773 |
for i in range(num_dunes):
|
| 774 |
+
# ์์น
|
| 775 |
+
initial_y = h // 5 + i * (h // (num_dunes + 1))
|
| 776 |
+
cx = w // 4 + (i % 2) * (w // 2)
|
| 777 |
+
cy = initial_y + move_distance
|
|
|
|
|
|
|
| 778 |
|
| 779 |
+
if cy >= h - 20:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 780 |
continue
|
| 781 |
|
| 782 |
+
# ์ฌ๊ตฌ ํฌ๊ธฐ
|
| 783 |
+
dune_height = 10.0 + i * 2.0
|
| 784 |
+
outer_r = w // 7 # ๋ฐ๊นฅ ์ ๋ฐ์ง๋ฆ
|
| 785 |
+
inner_r = outer_r * 0.6 # ์์ชฝ ์ ๋ฐ์ง๋ฆ
|
| 786 |
+
inner_offset = outer_r * 0.5 # ์์ชฝ ์ ์คํ์
(์๋๋ก)
|
| 787 |
+
|
| 788 |
for r in range(h):
|
| 789 |
for c in range(w):
|
| 790 |
dy = r - cy
|
| 791 |
dx = c - cx
|
| 792 |
|
| 793 |
+
# ๋ฐ๊นฅ ์ (๋ณผ๋ก๋ฉด - ์๋จ)
|
| 794 |
+
dist_outer = np.sqrt(dx**2 + dy**2)
|
| 795 |
|
| 796 |
+
# ์์ชฝ ์ (์ค๋ชฉ๋ฉด - ํ๋จ์ผ๋ก ์คํ์
)
|
| 797 |
+
dist_inner = np.sqrt(dx**2 + (dy - inner_offset)**2)
|
| 798 |
+
|
| 799 |
+
# ์ด์น๋ฌ ์์ญ: ๋ฐ๊นฅ ์ ์ AND ์์ชฝ ์ ๋ฐ
|
| 800 |
+
in_crescent = (dist_outer < outer_r) and (dist_inner > inner_r)
|
| 801 |
+
|
| 802 |
+
if in_crescent:
|
| 803 |
+
# ๋์ด ๊ณ์ฐ: ์ค์ฌ์์ ๋ฉ์๋ก ๋ฎ์์ง
|
| 804 |
+
height_factor = 1 - (dist_outer / outer_r)
|
| 805 |
|
| 806 |
+
# ๋ฐ๋๋ฐ์ด(์๋จ) ์๋ง, ๋ฐ๋๊ทธ๋(ํ๋จ) ๊ธ
|
| 807 |
+
if dy < 0:
|
| 808 |
+
# ๋ฐ๋๋ฐ์ด: ์๋งํ ๊ฒฝ์ฌ
|
| 809 |
+
slope = height_factor * 0.8
|
| 810 |
+
else:
|
| 811 |
+
# ๋ฐ๋๊ทธ๋: ๋ ๋๊ฒ (๊ธ๊ฒฝ์ฌ ํจ๊ณผ)
|
| 812 |
+
slope = height_factor * 1.2
|
| 813 |
+
|
| 814 |
+
z = dune_height * slope
|
| 815 |
+
elevation[r, c] = max(elevation[r, c], 5.0 + z)
|
| 816 |
+
|
| 817 |
+
# ๋ฟ (horn) - ์์ชฝ์ผ๋ก ๋ฐ๋ ๋ฐฉํฅ์ผ๋ก ๋ป์
|
| 818 |
+
horn_width = outer_r * 0.3
|
| 819 |
+
horn_length = outer_r * 0.8
|
| 820 |
+
|
| 821 |
+
for side in [-1, 1]: # ์ผ์ชฝ, ์ค๋ฅธ์ชฝ ๋ฟ
|
| 822 |
+
horn_cx = cx + side * (outer_r - horn_width)
|
| 823 |
+
horn_cy = cy + inner_offset
|
| 824 |
+
|
| 825 |
+
dx_horn = c - horn_cx
|
| 826 |
+
dy_horn = r - horn_cy
|
| 827 |
+
|
| 828 |
+
# ๋ฟ ์์ญ (๋ฐ๋ ๋ฐฉํฅ์ผ๋ก ๊ธธ์ญ)
|
| 829 |
+
if abs(dx_horn) < horn_width and 0 < dy_horn < horn_length:
|
| 830 |
+
# ๋ฟ ๋์ด: ๋์ผ๋ก ๊ฐ์๋ก ๋ฎ์์ง
|
| 831 |
+
horn_factor = 1 - dy_horn / horn_length
|
| 832 |
+
width_factor = 1 - abs(dx_horn) / horn_width
|
| 833 |
+
z = dune_height * 0.5 * horn_factor * width_factor
|
| 834 |
+
|
| 835 |
+
if z > 0.3:
|
| 836 |
+
elevation[r, c] = max(elevation[r, c], 5.0 + z)
|
| 837 |
+
|
| 838 |
return elevation
|
| 839 |
# ============================================
|
| 840 |
# ํ์ฅ ์งํ (Extended Landforms)
|
|
|
|
| 1273 |
# ============================================
|
| 1274 |
|
| 1275 |
def create_fjord(grid_size: int = 100, stage: float = 1.0) -> np.ndarray:
|
| 1276 |
+
"""ํผ์ค๋ฅด๋ (Fjord) - ๋นํ ํํด ํ ๋ฐ๋ค ์ ์
|
| 1277 |
+
|
| 1278 |
+
Stage 0.0~0.4: ๋นํ๊ฐ U์๊ณก์ ์ฑ์ (๋นํ๊ธฐ)
|
| 1279 |
+
Stage 0.4~0.7: ๋นํ ํํด ์์ (๋ฐ๋ค ์ ์
์์)
|
| 1280 |
+
Stage 0.7~1.0: ๋นํ ์์ ํํด (ํผ์ค๋ฅด๋ ์์ฑ)
|
| 1281 |
+
"""
|
| 1282 |
h, w = grid_size, grid_size
|
| 1283 |
elevation = np.zeros((h, w))
|
| 1284 |
|
| 1285 |
+
# ์ฐ์
์งํ (๋์ ์ฐ)
|
| 1286 |
+
elevation[:, :] = 100.0
|
| 1287 |
|
| 1288 |
center = w // 2
|
| 1289 |
+
valley_width = int(w * 0.25)
|
| 1290 |
+
valley_depth = 60.0
|
|
|
|
|
|
|
| 1291 |
|
| 1292 |
+
# U์๊ณก ํ์ฑ
|
| 1293 |
for r in range(h):
|
| 1294 |
for c in range(w):
|
| 1295 |
dx = abs(c - center)
|
| 1296 |
|
| 1297 |
if dx < valley_width:
|
| 1298 |
# U์ ๋ฐ๋ฅ
|
| 1299 |
+
base_height = 10.0
|
| 1300 |
+
elevation[r, c] = base_height
|
| 1301 |
+
elif dx < valley_width + 15:
|
| 1302 |
+
# U์ ์ธก๋ฒฝ (์์ง์ ๊ฐ๊น์)
|
| 1303 |
+
t = (dx - valley_width) / 15
|
| 1304 |
+
elevation[r, c] = 10.0 + 90.0 * (t ** 0.5) # ๊ธ๊ฒฝ์ฌ
|
| 1305 |
+
|
| 1306 |
+
# ๋นํ / ๋ฐ๋ค ์ํ
|
| 1307 |
+
if stage < 0.4:
|
| 1308 |
+
# ๋นํ๊ธฐ: U์๊ณก์ ๋นํ ์ฑ์
|
| 1309 |
+
glacier_extent = int(h * 0.9) # ๊ฑฐ์ ์ ์ฒด ์ฑ์
|
| 1310 |
+
glacier_thickness = 40.0
|
| 1311 |
+
|
| 1312 |
+
for r in range(glacier_extent):
|
| 1313 |
+
for c in range(w):
|
| 1314 |
+
dx = abs(c - center)
|
| 1315 |
+
if dx < valley_width:
|
| 1316 |
+
# ๋นํ ํ๋ฉด (๋ณผ๋ก)
|
| 1317 |
+
cross_profile = glacier_thickness * (1 - (dx / valley_width) ** 2)
|
| 1318 |
+
elevation[r, c] = 10.0 + cross_profile
|
| 1319 |
+
|
| 1320 |
+
elif stage < 0.7:
|
| 1321 |
+
# ๋นํ ํํด ์ค: ์ผ๋ถ ๋นํ + ๋ฐ๋ค ์ ์
|
| 1322 |
+
retreat_factor = (stage - 0.4) / 0.3
|
| 1323 |
+
|
| 1324 |
+
# ๋นํ ์๋ฅ (์๋ฅ์๋ง)
|
| 1325 |
+
glacier_end = int(h * (0.9 - 0.6 * retreat_factor))
|
| 1326 |
+
glacier_thickness = 40.0 * (1 - retreat_factor * 0.5)
|
| 1327 |
+
|
| 1328 |
+
for r in range(glacier_end):
|
| 1329 |
+
for c in range(w):
|
| 1330 |
+
dx = abs(c - center)
|
| 1331 |
+
if dx < valley_width:
|
| 1332 |
+
cross_profile = glacier_thickness * (1 - (dx / valley_width) ** 2)
|
| 1333 |
+
elevation[r, c] = 10.0 + cross_profile
|
| 1334 |
+
|
| 1335 |
+
# ๋ฐ๋ค ์ ์
(ํ๋ฅ๋ถํฐ)
|
| 1336 |
+
sea_start = glacier_end
|
| 1337 |
+
for r in range(sea_start, h):
|
| 1338 |
+
for c in range(w):
|
| 1339 |
+
dx = abs(c - center)
|
| 1340 |
+
if dx < valley_width:
|
| 1341 |
+
# ๊น์ ๋ฐ๋ค
|
| 1342 |
+
elevation[r, c] = -30.0 * retreat_factor
|
| 1343 |
+
else:
|
| 1344 |
+
# ํผ์ค๋ฅด๋ ์์ฑ: ๊น์ ๋ฐ๋ค๋ง
|
| 1345 |
+
sea_depth = -50.0 # ๊น์ ํผ์ค๋ฅด๋
|
| 1346 |
+
|
| 1347 |
+
for r in range(h):
|
| 1348 |
+
for c in range(w):
|
| 1349 |
+
dx = abs(c - center)
|
| 1350 |
+
if dx < valley_width:
|
| 1351 |
+
# ์๋ฅ๋ก ๊ฐ์๋ก ์์์ง
|
| 1352 |
+
depth_gradient = 1 - (r / h) * 0.3
|
| 1353 |
+
elevation[r, c] = sea_depth * depth_gradient
|
| 1354 |
+
|
| 1355 |
return elevation
|
| 1356 |
|
| 1357 |
|
|
|
|
| 1476 |
|
| 1477 |
def create_waterfall(grid_size: int = 100, stage: float = 1.0,
|
| 1478 |
drop_height: float = 50.0) -> np.ndarray:
|
| 1479 |
+
"""ํญํฌ (Waterfall) - ๋๋ถ์นจ์์ผ๋ก ํํด
|
| 1480 |
+
|
| 1481 |
+
Stage 0.0: ํญํฌ๊ฐ ํ๋ฅ์ ์์น
|
| 1482 |
+
Stage 1.0: ํญํฌ๊ฐ ์๋ฅ๋ก ํํด (๋๋ถ์นจ์)
|
| 1483 |
+
- ๊ฒฝ์์ธต๊ณผ ์ฐ์์ธต์ ์ฐจ๋ณ์นจ์
|
| 1484 |
+
- ํ๋ฐ์งํ(ํญํธ) ๋ฐ๋ฌ
|
| 1485 |
+
- ํํดํ๋ฉด์ ํ๊ณก ํ์ฑ
|
| 1486 |
+
"""
|
| 1487 |
h, w = grid_size, grid_size
|
| 1488 |
elevation = np.zeros((h, w))
|
|
|
|
| 1489 |
center = w // 2
|
| 1490 |
+
|
| 1491 |
+
# ํญํฌ ์์น (stage์ ๋ฐ๋ผ ์๋ฅ๋ก ํํด)
|
| 1492 |
+
# stage 0: ํ๋ฅ(h*0.7), stage 1: ์๋ฅ(h*0.3)
|
| 1493 |
+
initial_fall = int(h * 0.7)
|
| 1494 |
+
final_fall = int(h * 0.3)
|
| 1495 |
+
fall_r = int(initial_fall - (initial_fall - final_fall) * stage)
|
| 1496 |
|
| 1497 |
# ์๋ฅ (๋์ ๊ฒฝ์์ธต)
|
| 1498 |
+
hard_rock_height = drop_height + 30.0
|
| 1499 |
for r in range(fall_r):
|
| 1500 |
for c in range(w):
|
| 1501 |
+
# ์๋ฅ๋ก ๊ฐ์๋ก ๋์์ง
|
| 1502 |
+
upstream_rise = (fall_r - r) * 0.3
|
| 1503 |
+
elevation[r, c] = hard_rock_height + upstream_rise
|
| 1504 |
+
|
| 1505 |
+
# ํญํฌ ์ ๋ฒฝ (๊ธ๊ฒฝ์ฌ)
|
| 1506 |
+
cliff_width = 5
|
| 1507 |
+
for r in range(fall_r, min(fall_r + cliff_width, h)):
|
| 1508 |
for c in range(w):
|
| 1509 |
+
t = (r - fall_r) / cliff_width
|
| 1510 |
+
# ์์ง ๋ํ
|
| 1511 |
+
elevation[r, c] = hard_rock_height * (1 - t) + 10.0 * t
|
| 1512 |
+
|
| 1513 |
+
# ํ๋ฅ (์ฐ์์ธต ์นจ์๋จ)
|
| 1514 |
+
for r in range(fall_r + cliff_width, h):
|
| 1515 |
for c in range(w):
|
| 1516 |
+
# ํ๋ฅ๋ก ๊ฐ์๋ก ๋ฎ์์ง
|
| 1517 |
+
downstream_drop = (r - fall_r - cliff_width) * 0.2
|
| 1518 |
+
elevation[r, c] = 10.0 - downstream_drop
|
| 1519 |
+
|
| 1520 |
+
# ํ๊ณก (ํญํฌ ํํด ๊ฒฝ๋ก)
|
| 1521 |
+
gorge_start = fall_r + cliff_width
|
| 1522 |
+
gorge_end = initial_fall + 10 # ์๋ ํญํฌ ์์น๊น์ง
|
| 1523 |
+
gorge_depth = 8.0
|
| 1524 |
+
|
| 1525 |
+
for r in range(gorge_start, min(gorge_end, h)):
|
| 1526 |
+
for dc in range(-6, 7):
|
| 1527 |
+
c = center + dc
|
| 1528 |
+
if 0 <= c < w:
|
| 1529 |
+
# V์ ํ๊ณก ๋จ๋ฉด
|
| 1530 |
+
depth = gorge_depth * (1 - abs(dc) / 6)
|
| 1531 |
+
elevation[r, c] -= depth
|
| 1532 |
+
|
| 1533 |
# ํ์ฒ ์๋ก
|
| 1534 |
for r in range(h):
|
| 1535 |
for dc in range(-4, 5):
|
| 1536 |
c = center + dc
|
| 1537 |
if 0 <= c < w:
|
| 1538 |
+
elevation[r, c] -= 3.0
|
| 1539 |
+
|
| 1540 |
+
# ํ๋ฐ์งํ (ํญํธ) - ํญํฌ ๋ฐ๋ก ์๋
|
| 1541 |
+
pool_r = fall_r + cliff_width + 2
|
| 1542 |
+
pool_depth = 15.0
|
| 1543 |
+
for dr in range(-6, 7):
|
| 1544 |
+
for dc in range(-7, 8):
|
| 1545 |
r, c = pool_r + dr, center + dc
|
| 1546 |
if 0 <= r < h and 0 <= c < w:
|
| 1547 |
dist = np.sqrt(dr**2 + dc**2)
|
| 1548 |
+
if dist < 7:
|
| 1549 |
+
pool_effect = pool_depth * (1 - dist / 7)
|
| 1550 |
+
elevation[r, c] = min(elevation[r, c], 5.0 - pool_effect)
|
| 1551 |
+
|
| 1552 |
return elevation
|
| 1553 |
|
| 1554 |
|
|
|
|
| 1579 |
|
| 1580 |
|
| 1581 |
def create_ria_coast(grid_size: int = 100, stage: float = 1.0) -> np.ndarray:
|
| 1582 |
+
"""๋ฆฌ์์ค์ ํด์ (Ria Coast) - ์นจ์๋ ํ๊ณก
|
| 1583 |
+
|
| 1584 |
+
ํด์๋ฉด ์์น์ผ๋ก V์๊ณก์ด ์นจ์๋์ด ํ์ฑ
|
| 1585 |
+
- ํฑ๋ ๋ชจ์ ํด์์
|
| 1586 |
+
- ์ข๊ณ ๊น์ ๋ง (๋ฆฌ์)
|
| 1587 |
+
"""
|
| 1588 |
h, w = grid_size, grid_size
|
| 1589 |
elevation = np.zeros((h, w))
|
| 1590 |
|
| 1591 |
+
# ์ฐ์ง ๋ฐฐ๊ฒฝ (๋์ ์ก์ง)
|
| 1592 |
+
elevation[:, :] = 50.0
|
| 1593 |
|
| 1594 |
+
# ์ฌ๋ฌ ๊ฐ์ V์ ํ๊ณก
|
| 1595 |
+
num_valleys = 5
|
| 1596 |
+
valley_spacing = w // (num_valleys + 1)
|
| 1597 |
|
| 1598 |
for i in range(num_valleys):
|
| 1599 |
+
valley_x = valley_spacing * (i + 1)
|
| 1600 |
+
valley_width = 12 + (i % 2) * 4 # ์ฝ๊ฐ์ ๋ณํ
|
| 1601 |
+
valley_depth = 40.0 + (i % 3) * 10
|
| 1602 |
|
| 1603 |
for r in range(h):
|
| 1604 |
for c in range(w):
|
| 1605 |
dx = abs(c - valley_x)
|
| 1606 |
|
| 1607 |
+
if dx < valley_width:
|
| 1608 |
+
# V์๊ณก (์๋ฅ๋ก ๊ฐ์๋ก ์ข์์ง)
|
| 1609 |
+
upstream_factor = 1 - r / h * 0.5
|
| 1610 |
+
effective_width = valley_width * upstream_factor
|
| 1611 |
|
| 1612 |
+
if dx < effective_width:
|
| 1613 |
+
depth = valley_depth * (1 - dx / effective_width)
|
| 1614 |
+
elevation[r, c] = min(elevation[r, c], 50.0 - depth)
|
| 1615 |
+
|
| 1616 |
+
# ํด์๋ฉด (stage์ ๋ฐ๋ผ ์์น)
|
| 1617 |
+
sea_level = 15.0 * stage # ๋์์๋ก ๋ง์ด ์นจ์
|
| 1618 |
+
|
| 1619 |
+
for r in range(h):
|
| 1620 |
for c in range(w):
|
| 1621 |
+
if elevation[r, c] < sea_level:
|
| 1622 |
+
# ํด์๋ฉด ์๋ = ๋ฐ๋ค (๋ฆฌ์)
|
| 1623 |
+
elevation[r, c] = -10.0 - (sea_level - elevation[r, c]) * 0.3
|
| 1624 |
|
| 1625 |
return elevation
|
| 1626 |
|
|
|
|
| 1665 |
|
| 1666 |
|
| 1667 |
def create_sea_arch(grid_size: int = 100, stage: float = 1.0) -> np.ndarray:
|
| 1668 |
+
"""ํด์์์น (Sea Arch) - ํด์๋๊ตด์ด ๊ดํต
|
| 1669 |
+
|
| 1670 |
+
๊ณถ์ ์์ชฝ์์ ํ๋ ์นจ์ โ ํด์๋๊ตด โ ๊ดํต = ์์น
|
| 1671 |
+
Stage: ์์น ํฌ๊ธฐ ๋ฐ๋ฌ
|
| 1672 |
+
"""
|
| 1673 |
h, w = grid_size, grid_size
|
| 1674 |
elevation = np.zeros((h, w))
|
| 1675 |
|
| 1676 |
+
# ๋ฐ๋ค (ํ๋จ)
|
| 1677 |
+
sea_line = int(h * 0.4)
|
| 1678 |
+
elevation[sea_line:, :] = -8.0
|
| 1679 |
|
| 1680 |
# ์ก์ง ์ ๋ฒฝ
|
| 1681 |
+
cliff_height = 35.0
|
| 1682 |
for r in range(sea_line):
|
| 1683 |
+
for c in range(w):
|
| 1684 |
+
# ๊ฑฐ๋ฆฌ์ ๋ฐ๋ฅธ ์ก์ง ๋์ด
|
| 1685 |
+
dist_from_edge = min(r, c, w - c - 1)
|
| 1686 |
+
elevation[r, c] = cliff_height
|
| 1687 |
+
|
| 1688 |
+
# ๋์ถ๋ถ (๊ณถ - headland)
|
| 1689 |
headland_cx = w // 2
|
| 1690 |
+
headland_width = int(w * 0.35)
|
| 1691 |
+
headland_length = int(h * 0.4)
|
| 1692 |
|
| 1693 |
for r in range(sea_line, sea_line + headland_length):
|
| 1694 |
+
# ๊ณถ ํญ์ด ๋์ผ๋ก ๊ฐ์๋ก ์ข์์ง
|
| 1695 |
+
taper = 1 - (r - sea_line) / headland_length * 0.5
|
| 1696 |
+
current_width = int(headland_width * taper)
|
| 1697 |
+
|
| 1698 |
+
for c in range(headland_cx - current_width // 2, headland_cx + current_width // 2):
|
| 1699 |
if 0 <= c < w:
|
| 1700 |
+
# ๊ณถ ๋์ด (๋์ผ๋ก ๊ฐ์๋ก ์ฝ๊ฐ ๋ฎ์์ง)
|
| 1701 |
+
height = cliff_height * (1 - (r - sea_line) / headland_length * 0.2)
|
| 1702 |
+
elevation[r, c] = height
|
| 1703 |
+
|
| 1704 |
+
# ํด์์์น (๊ณถ ์ค๊ฐ์ ๊ดํต)
|
| 1705 |
arch_r = sea_line + int(headland_length * 0.5)
|
| 1706 |
+
arch_height = int(cliff_height * 0.6 * stage) # ์์น ๋์ด
|
| 1707 |
+
arch_width = int(headland_width * 0.3 * stage) # ์์น ํญ
|
| 1708 |
|
| 1709 |
+
for dr in range(-8, 9):
|
| 1710 |
+
for dc in range(-arch_width, arch_width + 1):
|
| 1711 |
+
r = arch_r + dr
|
| 1712 |
+
c = headland_cx + dc
|
| 1713 |
+
|
| 1714 |
if 0 <= r < h and 0 <= c < w:
|
| 1715 |
+
# ์์น ํํ (๋ฐ์ํ ํฐ๋)
|
| 1716 |
+
arch_profile = arch_height * np.sqrt(max(0, 1 - (dc / max(arch_width, 1))**2))
|
| 1717 |
+
|
| 1718 |
+
if abs(dr) < 3 and arch_profile > 5:
|
| 1719 |
+
# ํฐ๋ ๊ดํต
|
| 1720 |
+
elevation[r, c] = -5.0
|
| 1721 |
+
elif abs(dr) < 5:
|
| 1722 |
+
# ์์น ์ฒ์ฅ
|
| 1723 |
+
if elevation[r, c] > arch_profile:
|
| 1724 |
+
elevation[r, c] = min(elevation[r, c], cliff_height - arch_profile * 0.3)
|
| 1725 |
+
|
| 1726 |
return elevation
|
| 1727 |
|
| 1728 |
|
|
|
|
| 1754 |
|
| 1755 |
|
| 1756 |
def create_lava_plateau(grid_size: int = 100, stage: float = 1.0) -> np.ndarray:
|
| 1757 |
+
"""์ฉ์๋์ง (Lava Plateau) - ํํ๊ฐ ํ์ฑ๊ณผ์
|
| 1758 |
+
|
| 1759 |
+
Stage 0.0~0.3: ์๋ V์๊ณก ์กด์ฌ
|
| 1760 |
+
Stage 0.3~0.6: ์ดํ๋ถ์ถ๋ก V์๊ณก ๋ฉ์์ง (์ฉ์๋์ง ํ์ฑ)
|
| 1761 |
+
Stage 0.6~1.0: ํ์ฒ ์ฌ์นจ์์ผ๋ก ์๋ก์ด ํ๊ณก ํ์ฑ
|
| 1762 |
+
"""
|
| 1763 |
h, w = grid_size, grid_size
|
| 1764 |
elevation = np.zeros((h, w))
|
| 1765 |
+
center = w // 2
|
| 1766 |
|
| 1767 |
+
# ๊ธฐ๋ฐ ๊ณ ์ ๋์ด
|
| 1768 |
+
plateau_base = 30.0
|
| 1769 |
+
|
| 1770 |
+
if stage < 0.3:
|
| 1771 |
+
# ์๋ V์๊ณก ์ํ
|
| 1772 |
+
v_factor = 1.0
|
| 1773 |
+
lava_fill = 0.0
|
| 1774 |
+
new_valley = 0.0
|
| 1775 |
+
elif stage < 0.6:
|
| 1776 |
+
# ์ดํ๋ถ์ถ๋ก V์๊ณก ๋ฉ์์ง
|
| 1777 |
+
v_factor = 1.0 - ((stage - 0.3) / 0.3) # V์๊ณก ์ ์ ์ฌ๋ผ์ง
|
| 1778 |
+
lava_fill = (stage - 0.3) / 0.3 # ์ฉ์ ์ฑ์์ง
|
| 1779 |
+
new_valley = 0.0
|
| 1780 |
+
else:
|
| 1781 |
+
# ์ ํ๊ณก ํ์ฑ
|
| 1782 |
+
v_factor = 0.0 # ์๋ V์๊ณก ์์ ํ ๋ฎ์
|
| 1783 |
+
lava_fill = 1.0
|
| 1784 |
+
new_valley = (stage - 0.6) / 0.4 # ์ ํ๊ณก ๋ฐ๋ฌ
|
| 1785 |
|
| 1786 |
+
for r in range(h):
|
| 1787 |
+
for c in range(w):
|
| 1788 |
+
dx = abs(c - center)
|
|
|
|
|
|
|
| 1789 |
|
| 1790 |
+
# ๊ธฐ๋ณธ ๊ณ ์
|
| 1791 |
+
elevation[r, c] = plateau_base
|
| 1792 |
+
|
| 1793 |
+
# ์๋ V์๊ณก (์ดํ๋ถ์ถ ์ )
|
| 1794 |
+
if v_factor > 0:
|
| 1795 |
+
valley_depth = 25.0 * v_factor
|
| 1796 |
+
if dx < 15:
|
| 1797 |
+
v_shape = valley_depth * (1 - dx / 15)
|
| 1798 |
+
elevation[r, c] -= v_shape
|
| 1799 |
+
|
| 1800 |
+
# ์ฉ์ ์ฑ์ (ํํํ)
|
| 1801 |
+
if lava_fill > 0:
|
| 1802 |
+
# ์ฉ์์ด V์๊ณก์ ๋ฉ์
|
| 1803 |
+
if dx < 15:
|
| 1804 |
+
fill_amount = 25.0 * lava_fill * (1 - dx / 15)
|
| 1805 |
+
elevation[r, c] += fill_amount * 0.8 # ์ฝ๊ฐ ๋ฎ๊ฒ
|
| 1806 |
+
|
| 1807 |
+
# ์๋ก์ด ํ๊ณก (ํ์ฒ ์ฌ์นจ์)
|
| 1808 |
+
if new_valley > 0:
|
| 1809 |
+
# ์ ํ์ฒ์ด ์ฉ์๋์ง๋ฅผ ํ๊ณ ๋ฆ
|
| 1810 |
+
new_valley_width = int(8 * new_valley)
|
| 1811 |
+
new_valley_depth = 20.0 * new_valley
|
| 1812 |
+
|
| 1813 |
+
if dx < new_valley_width:
|
| 1814 |
+
# ๋ ์ข๊ณ ๊น์ ํ๊ณก
|
| 1815 |
+
gorge_shape = new_valley_depth * (1 - dx / max(new_valley_width, 1))
|
| 1816 |
+
elevation[r, c] -= gorge_shape
|
| 1817 |
+
|
| 1818 |
+
# ๊ฐ์ฅ์๋ฆฌ ๊ฒฝ์ฌ
|
| 1819 |
+
margin = int(w * 0.1)
|
| 1820 |
for r in range(h):
|
| 1821 |
for c in range(w):
|
| 1822 |
edge_dist = min(r, h - r - 1, c, w - c - 1)
|
| 1823 |
if edge_dist < margin:
|
| 1824 |
t = edge_dist / margin
|
| 1825 |
+
elevation[r, c] = elevation[r, c] * t + 5.0 * (1 - t)
|
| 1826 |
|
| 1827 |
return elevation
|
| 1828 |
|
|
|
|
| 1863 |
return elevation
|
| 1864 |
|
| 1865 |
|
| 1866 |
+
# ============================================
|
| 1867 |
+
# ์๋ก ์ถ๊ฐ๋ ์งํ๋ค
|
| 1868 |
+
# ============================================
|
| 1869 |
+
|
| 1870 |
+
def create_uvala(grid_size: int = 100, stage: float = 1.0,
|
| 1871 |
+
num_dolines: int = 4) -> np.ndarray:
|
| 1872 |
+
"""์ฐ๋ฐ๋ผ (Uvala) - ๋ณตํฉ ๋๋ฆฌ๋ค
|
| 1873 |
+
|
| 1874 |
+
์ฌ๋ฌ ๋๋ฆฌ๋ค๊ฐ ํฉ์ณ์ ธ์ ํ์ฑ๋ ํฐ ์์ง
|
| 1875 |
+
Stage 0~0.5: ๊ฐ๋ณ ๋๋ฆฌ๋ค ํ์ฑ
|
| 1876 |
+
Stage 0.5~1.0: ๋๋ฆฌ๋ค๋ค์ด ํฉ์ณ์ง
|
| 1877 |
+
"""
|
| 1878 |
+
h, w = grid_size, grid_size
|
| 1879 |
+
elevation = np.zeros((h, w))
|
| 1880 |
+
elevation[:, :] = 30.0 # ์ํ์ ๋์ง
|
| 1881 |
+
|
| 1882 |
+
center = w // 2
|
| 1883 |
+
|
| 1884 |
+
# ๋๋ฆฌ๋ค ์์น๋ค
|
| 1885 |
+
doline_positions = [
|
| 1886 |
+
(h // 3, center - w // 6),
|
| 1887 |
+
(h // 3, center + w // 6),
|
| 1888 |
+
(h * 2 // 3, center - w // 6),
|
| 1889 |
+
(h * 2 // 3, center + w // 6),
|
| 1890 |
+
]
|
| 1891 |
+
|
| 1892 |
+
doline_radius = int(w * 0.15)
|
| 1893 |
+
doline_depth = 20.0 * stage
|
| 1894 |
+
|
| 1895 |
+
for i, (cy, cx) in enumerate(doline_positions[:num_dolines]):
|
| 1896 |
+
for r in range(h):
|
| 1897 |
+
for c in range(w):
|
| 1898 |
+
dist = np.sqrt((r - cy)**2 + (c - cx)**2)
|
| 1899 |
+
if dist < doline_radius:
|
| 1900 |
+
# ๋๋ฆฌ๋ค ํํ (๊ฐ์ฅ์๋ฆฌ ๋๊ณ ์ค์ ๋ฎ์)
|
| 1901 |
+
depth = doline_depth * (1 - dist / doline_radius)
|
| 1902 |
+
elevation[r, c] = min(elevation[r, c], 30.0 - depth)
|
| 1903 |
+
|
| 1904 |
+
# Stage > 0.5: ๋๋ฆฌ๋ค ์ฌ์ด ์ฐ๊ฒฐ (ํฉ์ณ์ง)
|
| 1905 |
+
if stage > 0.5:
|
| 1906 |
+
merge_factor = (stage - 0.5) / 0.5
|
| 1907 |
+
merge_depth = 10.0 * merge_factor
|
| 1908 |
+
|
| 1909 |
+
# ์ค์ ์ฐ๊ฒฐ๋ถ
|
| 1910 |
+
for r in range(h):
|
| 1911 |
+
for c in range(w):
|
| 1912 |
+
dist_center = np.sqrt((r - h//2)**2 + (c - center)**2)
|
| 1913 |
+
if dist_center < doline_radius * 1.5:
|
| 1914 |
+
elevation[r, c] = min(elevation[r, c], 30.0 - merge_depth)
|
| 1915 |
+
|
| 1916 |
+
return elevation
|
| 1917 |
+
|
| 1918 |
+
|
| 1919 |
+
def create_tower_karst(grid_size: int = 100, stage: float = 1.0,
|
| 1920 |
+
num_towers: int = 6) -> np.ndarray:
|
| 1921 |
+
"""ํ์นด๋ฅด์คํธ (Tower Karst) - ๋ด์ฐ๋ฆฌ ํํ ์นด๋ฅด์คํธ
|
| 1922 |
+
|
| 1923 |
+
์ค๊ตญ ๊ตฌ์ด๋ฆฐ ๊ฐ์ ํ ๋ชจ์ ์ํ์ ๋ด์ฐ๋ฆฌ
|
| 1924 |
+
"""
|
| 1925 |
+
h, w = grid_size, grid_size
|
| 1926 |
+
elevation = np.zeros((h, w))
|
| 1927 |
+
elevation[:, :] = 5.0 # ์ ์ง๋
|
| 1928 |
+
|
| 1929 |
+
np.random.seed(42)
|
| 1930 |
+
|
| 1931 |
+
for i in range(num_towers):
|
| 1932 |
+
cy = int(h * 0.2 + (i % 3) * h * 0.3)
|
| 1933 |
+
cx = int(w * 0.2 + (i // 3) * w * 0.3 + np.random.randint(-10, 10))
|
| 1934 |
+
|
| 1935 |
+
tower_height = (40.0 + np.random.rand() * 30) * stage
|
| 1936 |
+
tower_radius = int(w * 0.08 + np.random.rand() * w * 0.04)
|
| 1937 |
+
|
| 1938 |
+
for r in range(h):
|
| 1939 |
+
for c in range(w):
|
| 1940 |
+
dist = np.sqrt((r - cy)**2 + (c - cx)**2)
|
| 1941 |
+
if dist < tower_radius:
|
| 1942 |
+
# ์์ง ์ ๋ฒฝ ํํ (๊ฐํ๋ฅธ ์ธก๋ฉด)
|
| 1943 |
+
edge_factor = 1 - (dist / tower_radius) ** 3
|
| 1944 |
+
z = tower_height * edge_factor
|
| 1945 |
+
elevation[r, c] = max(elevation[r, c], 5.0 + z)
|
| 1946 |
+
|
| 1947 |
+
return elevation
|
| 1948 |
+
|
| 1949 |
+
|
| 1950 |
+
def create_karren(grid_size: int = 100, stage: float = 1.0) -> np.ndarray:
|
| 1951 |
+
"""์นด๋ (Karren/Lapies) - ์ํ์ ์ฉ์ ํ
|
| 1952 |
+
|
| 1953 |
+
๋น๋ฌผ์ ์ํ ์ฉ์์ผ๋ก ํ์ฑ๋ ํ๊ณผ ๋ฆฟ์ง
|
| 1954 |
+
"""
|
| 1955 |
+
h, w = grid_size, grid_size
|
| 1956 |
+
elevation = np.zeros((h, w))
|
| 1957 |
+
elevation[:, :] = 20.0 # ์ํ์ ํ๋ฉด
|
| 1958 |
+
|
| 1959 |
+
# ์ฉ์ ํ (Rillenkarren) - ํํํ ํ
|
| 1960 |
+
groove_spacing = max(3, w // 20)
|
| 1961 |
+
groove_depth = 3.0 * stage
|
| 1962 |
+
|
| 1963 |
+
for c in range(w):
|
| 1964 |
+
if c % groove_spacing < groove_spacing // 2:
|
| 1965 |
+
for r in range(h):
|
| 1966 |
+
# ๊ธธ์ญํ ํ
|
| 1967 |
+
depth = groove_depth * (1 - abs(c % groove_spacing - groove_spacing // 4) / (groove_spacing // 4))
|
| 1968 |
+
elevation[r, c] -= depth
|
| 1969 |
+
|
| 1970 |
+
# ํด๋ฆฐํธ/๊ทธ๋ผ์ดํฌ (Clint/Grike) - ์ง๊ฐ ํจํด
|
| 1971 |
+
block_size = max(8, w // 8)
|
| 1972 |
+
grike_depth = 5.0 * stage
|
| 1973 |
+
grike_width = 2
|
| 1974 |
+
|
| 1975 |
+
for r in range(h):
|
| 1976 |
+
for c in range(w):
|
| 1977 |
+
if r % block_size < grike_width or c % block_size < grike_width:
|
| 1978 |
+
elevation[r, c] -= grike_depth
|
| 1979 |
+
|
| 1980 |
+
return elevation
|
| 1981 |
+
|
| 1982 |
+
|
| 1983 |
+
def create_transverse_dune(grid_size: int = 100, stage: float = 1.0,
|
| 1984 |
+
num_ridges: int = 4) -> np.ndarray:
|
| 1985 |
+
"""ํก์ฌ๊ตฌ (Transverse Dune) - ๋ฐ๋์ ์ง๊ฐ์ธ ์ฌ๊ตฌ์ด
|
| 1986 |
+
|
| 1987 |
+
๋ฐ๋ ๋ฐฉํฅ์ ์์ง์ผ๋ก ํ์ฑ๋ ๊ธด ์ฌ๊ตฌ
|
| 1988 |
+
"""
|
| 1989 |
+
h, w = grid_size, grid_size
|
| 1990 |
+
elevation = np.zeros((h, w))
|
| 1991 |
+
elevation[:, :] = 5.0 # ์ฌ๋ง ๊ธฐ๋ฐ
|
| 1992 |
+
|
| 1993 |
+
# ํก์ฌ๊ตฌ (๋ฐ๋ ๋ฐฉํฅ ์โํ์ ์์ง = ์ข์ฐ๋ก ๊ธธ๊ฒ)
|
| 1994 |
+
ridge_spacing = h // (num_ridges + 1)
|
| 1995 |
+
ridge_height = 12.0 * stage
|
| 1996 |
+
ridge_width = max(5, h // 10)
|
| 1997 |
+
|
| 1998 |
+
for i in range(num_ridges):
|
| 1999 |
+
ridge_r = ridge_spacing * (i + 1)
|
| 2000 |
+
|
| 2001 |
+
for r in range(h):
|
| 2002 |
+
for c in range(w):
|
| 2003 |
+
dr = r - ridge_r
|
| 2004 |
+
|
| 2005 |
+
if abs(dr) < ridge_width:
|
| 2006 |
+
# ๋น๋์นญ: ๋ฐ๋๋ฐ์ด ์๋ง, ๋ฐ๋๊ทธ๋ ๊ธ
|
| 2007 |
+
if dr < 0:
|
| 2008 |
+
# ๋ฐ๋๋ฐ์ด
|
| 2009 |
+
z = ridge_height * (1 - abs(dr) / (ridge_width * 1.5))
|
| 2010 |
+
else:
|
| 2011 |
+
# ๋ฐ๋๊ทธ๋
|
| 2012 |
+
z = ridge_height * (1 - dr / (ridge_width * 0.6))
|
| 2013 |
+
z = max(0, z)
|
| 2014 |
+
elevation[r, c] = max(elevation[r, c], 5.0 + z)
|
| 2015 |
+
|
| 2016 |
+
return elevation
|
| 2017 |
+
|
| 2018 |
+
|
| 2019 |
+
def create_star_dune(grid_size: int = 100, stage: float = 1.0,
|
| 2020 |
+
num_dunes: int = 2) -> np.ndarray:
|
| 2021 |
+
"""์ฑ์ฌ๊ตฌ (Star Dune) - ๋ณ ๋ชจ์ ์ฌ๊ตฌ
|
| 2022 |
+
|
| 2023 |
+
๋ค๋ฐฉํฅ ๋ฐ๋์ผ๋ก ํ์ฑ๋ ๋ฐฉ์ฌ์ ์ฌ๊ตฌ
|
| 2024 |
+
"""
|
| 2025 |
+
h, w = grid_size, grid_size
|
| 2026 |
+
elevation = np.zeros((h, w))
|
| 2027 |
+
elevation[:, :] = 5.0 # ์ฌ๋ง ๊ธฐ๋ฐ
|
| 2028 |
+
|
| 2029 |
+
for d in range(num_dunes):
|
| 2030 |
+
cy = h // 3 + d * h // 3
|
| 2031 |
+
cx = w // 3 + d * w // 3
|
| 2032 |
+
|
| 2033 |
+
dune_height = 20.0 * stage
|
| 2034 |
+
arm_length = int(w * 0.2)
|
| 2035 |
+
arm_width = max(3, w // 20)
|
| 2036 |
+
num_arms = 5 # ๋ณ ๋ชจ์ ํ ๊ฐ์
|
| 2037 |
+
|
| 2038 |
+
for r in range(h):
|
| 2039 |
+
for c in range(w):
|
| 2040 |
+
dx = c - cx
|
| 2041 |
+
dy = r - cy
|
| 2042 |
+
dist = np.sqrt(dx**2 + dy**2)
|
| 2043 |
+
|
| 2044 |
+
# ์ค์ ๋ด์ฐ๋ฆฌ
|
| 2045 |
+
if dist < arm_width * 2:
|
| 2046 |
+
z = dune_height * (1 - dist / (arm_width * 2))
|
| 2047 |
+
elevation[r, c] = max(elevation[r, c], 5.0 + z)
|
| 2048 |
+
|
| 2049 |
+
# ํ (๋ฐฉ์ฌ์)
|
| 2050 |
+
for arm in range(num_arms):
|
| 2051 |
+
angle = arm * 2 * np.pi / num_arms
|
| 2052 |
+
# ํ ์ค์ฌ์ ๊น์ง์ ๊ฑฐ๋ฆฌ
|
| 2053 |
+
arm_dir = np.array([np.cos(angle), np.sin(angle)])
|
| 2054 |
+
pos = np.array([dx, dy])
|
| 2055 |
+
proj = np.dot(pos, arm_dir)
|
| 2056 |
+
perp = np.abs(np.cross(arm_dir, pos))
|
| 2057 |
+
|
| 2058 |
+
if proj > 0 and proj < arm_length and perp < arm_width:
|
| 2059 |
+
# ํ ๋์ด: ์ค์์์ ๋ฉ์ด์ง์๋ก ๋ฎ์์ง
|
| 2060 |
+
z = dune_height * 0.6 * (1 - proj / arm_length) * (1 - perp / arm_width)
|
| 2061 |
+
elevation[r, c] = max(elevation[r, c], 5.0 + z)
|
| 2062 |
+
|
| 2063 |
+
return elevation
|
| 2064 |
+
|
| 2065 |
+
|
| 2066 |
# ์ ๋๋ฉ์ด์
์์ฑ๊ธฐ ๋งคํ
|
| 2067 |
ANIMATED_LANDFORM_GENERATORS = {
|
| 2068 |
'delta': create_delta_animated,
|
|
|
|
| 2098 |
'crater_lake': create_crater_lake,
|
| 2099 |
'lava_plateau': create_lava_plateau,
|
| 2100 |
'coastal_dune': create_coastal_dune,
|
| 2101 |
+
# ์๋ก ์ถ๊ฐ๋ ์งํ
|
| 2102 |
+
'uvala': create_uvala,
|
| 2103 |
+
'tower_karst': create_tower_karst,
|
| 2104 |
+
'karren': create_karren,
|
| 2105 |
+
'transverse_dune': create_transverse_dune,
|
| 2106 |
+
'star_dune': create_star_dune,
|
| 2107 |
}
|
| 2108 |
|
| 2109 |
# ์งํ ์์ฑ ํจ์ ๋งคํ
|
|
|
|
| 2141 |
'crater_lake': lambda gs: create_crater_lake(gs, 1.0),
|
| 2142 |
'lava_plateau': lambda gs: create_lava_plateau(gs, 1.0),
|
| 2143 |
'coastal_dune': lambda gs: create_coastal_dune(gs, 1.0),
|
| 2144 |
+
# ์๋ก ์ถ๊ฐ๋ ์งํ
|
| 2145 |
+
'uvala': lambda gs: create_uvala(gs, 1.0),
|
| 2146 |
+
'tower_karst': lambda gs: create_tower_karst(gs, 1.0),
|
| 2147 |
+
'karren': lambda gs: create_karren(gs, 1.0),
|
| 2148 |
+
'transverse_dune': lambda gs: create_transverse_dune(gs, 1.0),
|
| 2149 |
+
'star_dune': lambda gs: create_star_dune(gs, 1.0),
|
| 2150 |
}
|
| 2151 |
|
pages/1_๐_Gallery.py
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
๐ ์ด์์ ์งํ ๊ฐค๋ฌ๋ฆฌ
|
| 3 |
+
31์ข
์ ๊ต๊ณผ์์ ์งํ์ ์๊ฐํํฉ๋๋ค.
|
| 4 |
+
"""
|
| 5 |
+
import streamlit as st
|
| 6 |
+
import numpy as np
|
| 7 |
+
import matplotlib.pyplot as plt
|
| 8 |
+
import sys
|
| 9 |
+
import os
|
| 10 |
+
|
| 11 |
+
# ์์ ๋๋ ํ ๋ฆฌ๋ฅผ ๊ฒฝ๋ก์ ์ถ๊ฐ
|
| 12 |
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
| 13 |
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
| 14 |
+
|
| 15 |
+
from engine.ideal_landforms import IDEAL_LANDFORM_GENERATORS, ANIMATED_LANDFORM_GENERATORS
|
| 16 |
+
from app.main import render_terrain_plotly
|
| 17 |
+
|
| 18 |
+
st.header("๐ ์ด์์ ์งํ ๊ฐค๋ฌ๋ฆฌ")
|
| 19 |
+
st.markdown("_๊ต๊ณผ์์ ์ธ ์งํ ํํ๋ฅผ ๊ธฐํํ์ ๋ชจ๋ธ๋ก ์๊ฐํํฉ๋๋ค._")
|
| 20 |
+
|
| 21 |
+
# ๊ฐ์กฐ ๋ฉ์์ง
|
| 22 |
+
st.info("๐ก **Tip:** ์งํ ์ ํ ํ **์๋๋ก ์คํฌ๋กค**ํ๋ฉด **๐ฌ ํ์ฑ ๊ณผ์ ์ ๋๋ฉ์ด์
**์ ํ์ธํ ์ ์์ต๋๋ค!")
|
| 23 |
+
|
| 24 |
+
# ์นดํ
๊ณ ๋ฆฌ๋ณ ์งํ
|
| 25 |
+
st.sidebar.subheader("๐๏ธ ์งํ ์นดํ
๊ณ ๋ฆฌ")
|
| 26 |
+
category = st.sidebar.radio("์นดํ
๊ณ ๋ฆฌ ์ ํ", [
|
| 27 |
+
"๐ ํ์ฒ ์งํ",
|
| 28 |
+
"๐บ ์ผ๊ฐ์ฃผ ์ ํ",
|
| 29 |
+
"โ๏ธ ๋นํ ์งํ",
|
| 30 |
+
"๐ ํ์ฐ ์งํ",
|
| 31 |
+
"๐ฆ ์นด๋ฅด์คํธ ์งํ",
|
| 32 |
+
"๐๏ธ ๊ฑด์กฐ ์งํ",
|
| 33 |
+
"๐๏ธ ํด์ ์งํ"
|
| 34 |
+
], key="gallery_cat")
|
| 35 |
+
|
| 36 |
+
# ์นดํ
๊ณ ๋ฆฌ โ landform_type ๋งคํ
|
| 37 |
+
CATEGORY_TO_TYPE = {
|
| 38 |
+
"๐ ํ์ฒ ์งํ": "river",
|
| 39 |
+
"๐บ ์ผ๊ฐ์ฃผ ์ ํ": "river",
|
| 40 |
+
"โ๏ธ ๋นํ ์งํ": "glacial",
|
| 41 |
+
"๐ ํ์ฐ ์งํ": "volcanic",
|
| 42 |
+
"๐ฆ ์นด๋ฅด์คํธ ์งํ": "karst",
|
| 43 |
+
"๐๏ธ ๊ฑด์กฐ ์งํ": "arid",
|
| 44 |
+
"๐๏ธ ํด์ ์งํ": "coastal"
|
| 45 |
+
}
|
| 46 |
+
landform_type = CATEGORY_TO_TYPE.get(category, None)
|
| 47 |
+
|
| 48 |
+
# ์นดํ
๊ณ ๋ฆฌ๋ณ ์ต์
|
| 49 |
+
if category == "๐ ํ์ฒ ์งํ":
|
| 50 |
+
landform_options = {
|
| 51 |
+
"๐ ์ ์์ง (Alluvial Fan)": "alluvial_fan",
|
| 52 |
+
"๐ ์์ ๊ณก๋ฅ (Free Meander)": "free_meander",
|
| 53 |
+
"โฐ๏ธ ๊ฐ์
๊ณก๋ฅ+ํ์๋จ๊ตฌ (Incised Meander)": "incised_meander",
|
| 54 |
+
"๐๏ธ V์๊ณก (V-Valley)": "v_valley",
|
| 55 |
+
"๐ ๋ง์ํ์ฒ (Braided River)": "braided_river",
|
| 56 |
+
"๐ง ํญํฌ (Waterfall)": "waterfall",
|
| 57 |
+
}
|
| 58 |
+
elif category == "๐บ ์ผ๊ฐ์ฃผ ์ ํ":
|
| 59 |
+
landform_options = {
|
| 60 |
+
"๐บ ์ผ๋ฐ ์ผ๊ฐ์ฃผ (Delta)": "delta",
|
| 61 |
+
"๐ฆถ ์กฐ์กฑ์ ์ผ๊ฐ์ฃผ (Bird-foot)": "bird_foot_delta",
|
| 62 |
+
"๐ ํธ์ ์ผ๊ฐ์ฃผ (Arcuate)": "arcuate_delta",
|
| 63 |
+
"๐ ์ฒจ๋์ ์ผ๊ฐ์ฃผ (Cuspate)": "cuspate_delta",
|
| 64 |
+
}
|
| 65 |
+
elif category == "โ๏ธ ๋นํ ์งํ":
|
| 66 |
+
landform_options = {
|
| 67 |
+
"โ๏ธ U์๊ณก (U-Valley)": "u_valley",
|
| 68 |
+
"๐ฅฃ ๊ถ๊ณก (Cirque)": "cirque",
|
| 69 |
+
"๐๏ธ ํธ๋ฅธ (Horn)": "horn",
|
| 70 |
+
"๐ ํผ์ค๋ฅด๋ (Fjord)": "fjord",
|
| 71 |
+
"๐ฅ ๋๋ผ๋ฆฐ (Drumlin)": "drumlin",
|
| 72 |
+
"๐ชจ ๋นํด์ (Moraine)": "moraine",
|
| 73 |
+
}
|
| 74 |
+
elif category == "๐ ํ์ฐ ์งํ":
|
| 75 |
+
landform_options = {
|
| 76 |
+
"๐ก๏ธ ์์ํ์ฐ (Shield)": "shield_volcano",
|
| 77 |
+
"๐ป ์ฑ์ธตํ์ฐ (Stratovolcano)": "stratovolcano",
|
| 78 |
+
"๐ณ๏ธ ์นผ๋ฐ๋ผ (Caldera)": "caldera",
|
| 79 |
+
"๐ง ํ๊ตฌํธ (Crater Lake)": "crater_lake",
|
| 80 |
+
"๐ซ ์ฉ์๋์ง (Lava Plateau)": "lava_plateau",
|
| 81 |
+
}
|
| 82 |
+
elif category == "๐ฆ ์นด๋ฅด์คํธ ์งํ":
|
| 83 |
+
landform_options = {
|
| 84 |
+
"๐ณ๏ธ ๋๋ฆฌ๋ค (Doline)": "karst_doline",
|
| 85 |
+
"๐ฅ ์ฐ๋ฐ๋ผ (Uvala)": "uvala",
|
| 86 |
+
"๐ผ ํ์นด๋ฅด์คํธ (Tower Karst)": "tower_karst",
|
| 87 |
+
"๐ชจ ์นด๋ (Karren)": "karren",
|
| 88 |
+
}
|
| 89 |
+
elif category == "๐๏ธ ๊ฑด์กฐ ์งํ":
|
| 90 |
+
landform_options = {
|
| 91 |
+
"๐ ๋ฐ๋ฅดํ ์ฌ๊ตฌ (Barchan)": "barchan",
|
| 92 |
+
"๐ฐ ํก์ฌ๊ตฌ (Transverse Dune)": "transverse_dune",
|
| 93 |
+
"โญ ์ฑ์ฌ๊ตฌ (Star Dune)": "star_dune",
|
| 94 |
+
"๐ฟ ๋ฉ์ฌ/๋ทฐํธ (Mesa/Butte)": "mesa_butte",
|
| 95 |
+
}
|
| 96 |
+
else: # ํด์ ์งํ
|
| 97 |
+
landform_options = {
|
| 98 |
+
"๐๏ธ ํด์ ์ ๋ฒฝ (Coastal Cliff)": "coastal_cliff",
|
| 99 |
+
"๐ ์ฌ์ทจ+์ํธ (Spit+Lagoon)": "spit_lagoon",
|
| 100 |
+
"๐๏ธ ์ก๊ณ์ฌ์ฃผ (Tombolo)": "tombolo",
|
| 101 |
+
"๐ ๋ฆฌ์์ค ํด์ (Ria Coast)": "ria_coast",
|
| 102 |
+
"๐ ํด์์์น (Sea Arch)": "sea_arch",
|
| 103 |
+
"๐๏ธ ํด์์ฌ๊ตฌ (Coastal Dune)": "coastal_dune",
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
col_sel, col_view = st.columns([1, 3])
|
| 107 |
+
|
| 108 |
+
with col_sel:
|
| 109 |
+
selected_landform = st.selectbox("์งํ ์ ํ", list(landform_options.keys()))
|
| 110 |
+
landform_key = landform_options[selected_landform]
|
| 111 |
+
|
| 112 |
+
st.markdown("---")
|
| 113 |
+
st.subheader("โ๏ธ ํ๋ผ๋ฏธํฐ")
|
| 114 |
+
|
| 115 |
+
gallery_grid_size = st.slider("ํด์๋", 50, 150, 80, 10, key="gallery_res")
|
| 116 |
+
|
| 117 |
+
# ๋์ ์งํ ์์ฑ
|
| 118 |
+
if landform_key in IDEAL_LANDFORM_GENERATORS:
|
| 119 |
+
generator = IDEAL_LANDFORM_GENERATORS[landform_key]
|
| 120 |
+
try:
|
| 121 |
+
elevation = generator(gallery_grid_size)
|
| 122 |
+
except TypeError:
|
| 123 |
+
elevation = generator(gallery_grid_size, 1.0)
|
| 124 |
+
else:
|
| 125 |
+
st.error(f"์งํ '{landform_key}' ์์ฑ๊ธฐ๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.")
|
| 126 |
+
elevation = np.zeros((gallery_grid_size, gallery_grid_size))
|
| 127 |
+
|
| 128 |
+
with col_view:
|
| 129 |
+
# 2D ํ๋ฉด๋
|
| 130 |
+
fig_2d, ax = plt.subplots(figsize=(8, 8))
|
| 131 |
+
cmap = plt.cm.terrain
|
| 132 |
+
water_mask = elevation < 0
|
| 133 |
+
|
| 134 |
+
im = ax.imshow(elevation, cmap=cmap, origin='upper')
|
| 135 |
+
|
| 136 |
+
if water_mask.any():
|
| 137 |
+
water_overlay = np.ma.masked_where(~water_mask, np.ones_like(elevation))
|
| 138 |
+
ax.imshow(water_overlay, cmap='Blues', alpha=0.6, origin='upper')
|
| 139 |
+
|
| 140 |
+
ax.set_title(f"{selected_landform}", fontsize=14)
|
| 141 |
+
ax.axis('off')
|
| 142 |
+
plt.colorbar(im, ax=ax, shrink=0.6, label='๊ณ ๋ (m)')
|
| 143 |
+
|
| 144 |
+
st.pyplot(fig_2d)
|
| 145 |
+
plt.close(fig_2d)
|
| 146 |
+
|
| 147 |
+
# 3D ๋ณด๊ธฐ ๋ฒํผ
|
| 148 |
+
if st.button("๐ฒ 3D ๋ทฐ ๋ณด๊ธฐ", key="show_3d_view"):
|
| 149 |
+
fig_3d = render_terrain_plotly(
|
| 150 |
+
elevation,
|
| 151 |
+
f"{selected_landform} - 3D",
|
| 152 |
+
add_water=(landform_key in ["delta", "meander", "coastal_cliff", "fjord", "ria_coast", "spit_lagoon"]),
|
| 153 |
+
water_level=0 if landform_key in ["delta", "coastal_cliff"] else -999,
|
| 154 |
+
force_camera=True,
|
| 155 |
+
landform_type=landform_type # ์นดํ
๊ณ ๋ฆฌ์ ๋ง๋ ์์ ์ ์ฉ
|
| 156 |
+
)
|
| 157 |
+
st.plotly_chart(fig_3d, use_container_width=True, key="gallery_3d")
|
| 158 |
+
|
| 159 |
+
# ์ค๋ช
|
| 160 |
+
descriptions = {
|
| 161 |
+
"delta": "**์ผ๊ฐ์ฃผ**: ํ์ฒ์ด ๋ฐ๋ค๋ ํธ์์ ์ ์
๋ ๋ ์ ์์ด ๊ฐ์ํ์ฌ ์ด๋ฐ ์ค์ด๋ ํด์ ๋ฌผ์ด ์์ฌ ํ์ฑ๋ฉ๋๋ค.",
|
| 162 |
+
"alluvial_fan": "**์ ์์ง**: ์ฐ์ง์์ ํ์ง๋ก ๋์ค๋ ๊ณณ์์ ๊ฒฝ์ฌ๊ฐ ๊ธ๊ฐํ์ฌ ์ด๋ฐ๋ ฅ์ด ์ค์ด๋ค๋ฉด์ ํด์ ๋ฌผ์ด ๋ถ์ฑ๊ผด๋ก ์์
๋๋ค.",
|
| 163 |
+
"free_meander": "**์์ ๊ณก๋ฅ**: ๋ฒ๋์ ์๋ฅผ ์์ ๋กญ๊ฒ ์ฌํํ๋ ๊ณก๋ฅ. ์์ฐ์ ๋ฐฉ(Levee)๊ณผ ๋ฐฐํ์ต์ง๊ฐ ํน์ง์
๋๋ค.",
|
| 164 |
+
"incised_meander": "**๊ฐ์
๊ณก๋ฅ**: ์ต๊ธฐ๋ก ์ธํด ๊ณก๋ฅ๊ฐ ๊ธฐ๋ฐ์์ ํ๊ณ ๋ค๋ฉด์ ํ์ฑ. ํ์๋จ๊ตฌ(River Terrace)๊ฐ ํจ๊ป ๋ํ๋ฉ๋๋ค.",
|
| 165 |
+
"v_valley": "**V์๊ณก**: ํ์ฒ์ ํ๋ฐฉ ์นจ์์ด ์ฐ์ธํ๊ฒ ์์ฉํ์ฌ ํ์ฑ๋ V์ ๋จ๋ฉด์ ๊ณจ์ง๊ธฐ.",
|
| 166 |
+
"braided_river": "**๋ง์ํ์ฒ**: ํด์ ๋ฌผ์ด ๋ง๊ณ ๊ฒฝ์ฌ๊ฐ ๊ธํ ๋ ์ฌ๋ฌ ์๋ก๊ฐ ๊ฐ๋ผ์ก๋ค ํฉ์ณ์ง๋ ํ์ฒ.",
|
| 167 |
+
"waterfall": "**ํญํฌ**: ๊ฒฝ์๊ณผ ์ฐ์์ ์ฐจ๋ณ์นจ์์ผ๋ก ํ์ฑ๋ ๊ธ๊ฒฝ์ฌ ๋์ฐจ. ํํดํ๋ฉฐ ํ๊ณก ํ์ฑ.",
|
| 168 |
+
"bird_foot_delta": "**์กฐ์กฑ์ ์ผ๊ฐ์ฃผ**: ๋ฏธ์์ํผ๊ฐํ. ํ๋ ์ฝํ๊ณ ํด์ ๋ฌผ ๊ณต๊ธ ๋ง์ ๋ ์๋ฐ ๋ชจ์์ผ๋ก ๊ธธ๊ฒ ๋ป์ต๋๋ค.",
|
| 169 |
+
"arcuate_delta": "**ํธ์ ์ผ๊ฐ์ฃผ**: ๋์ผ๊ฐํ. ํ๋๊ณผ ํด์ ๋ฌผ ๊ณต๊ธ์ด ๊ท ํ์ ์ด๋ฃจ์ด ๋ถ๋๋ฌ์ด ํธ(Arc) ํํ.",
|
| 170 |
+
"cuspate_delta": "**์ฒจ๋์ ์ผ๊ฐ์ฃผ**: ํฐ๋ฒ ๋ฅด๊ฐํ. ํ๋์ด ๊ฐํด ์ผ๊ฐ์ฃผ๊ฐ ๋พฐ์กฑํ ํ์ด์ด ๋ชจ์์ผ๋ก ํ์ฑ.",
|
| 171 |
+
"u_valley": "**U์๊ณก**: ๋นํ์ ์นจ์์ผ๋ก ํ์ฑ๋ U์ ๋จ๋ฉด์ ๊ณจ์ง๊ธฐ. ์ธก๋ฒฝ์ด ๊ธํ๊ณ ๋ฐ๋ฅ์ด ํํํฉ๋๋ค.",
|
| 172 |
+
"cirque": "**๊ถ๊ณก**: ๋นํ์ ์์์ . ๋ฐ์ํ ์ํน ํ์ธ ์งํ์ผ๋ก, ๋นํ ์ตํด ํ ํธ์(Tarn)๊ฐ ํ์ฑ๋ฉ๋๋ค.",
|
| 173 |
+
"horn": "**ํธ๋ฅธ**: ์ฌ๋ฌ ๊ถ๊ณก์ด ๋ง๋๋ ๊ณณ์์ ์นจ์๋์ง ์๊ณ ๋จ์ ๋พฐ์กฑํ ํผ๋ผ๋ฏธ๋ํ ๋ด์ฐ๋ฆฌ.",
|
| 174 |
+
"fjord": "**ํผ์ค๋ฅด๋**: ๋นํ๊ฐ ํ๋ธ U์๊ณก์ ๋ฐ๋ค๊ฐ ์ ์
๋ ์ข๊ณ ๊น์ ๋ง.",
|
| 175 |
+
"drumlin": "**๋๋ผ๋ฆฐ**: ๋นํ ํด์ ๋ฌผ์ด ๋นํ ํ๋ฆ ๋ฐฉํฅ์ผ๋ก ๊ธธ์ญํ๊ฒ ์์ธ ํ์ํ ์ธ๋.",
|
| 176 |
+
"moraine": "**๋นํด์**: ๋นํ๊ฐ ์ด๋ฐํ ์์ค์ด ํด์ ๋ ์งํ. ์ธกํด์, ์ข
ํด์ ๋ฑ์ด ์์ต๋๋ค.",
|
| 177 |
+
"shield_volcano": "**์์ํ์ฐ**: ์ ๋์ฑ ๋์ ํ๋ฌด์์ง ์ฉ์์ด ์๋งํ๊ฒ ์์ฌ ๋ฐฉํจ ํํ.",
|
| 178 |
+
"stratovolcano": "**์ฑ์ธตํ์ฐ**: ์ฉ์๊ณผ ํ์ฐ์์ค๋ฌผ์ด ๊ต๋๋ก ์์ฌ ๊ธํ ์๋ฟํ.",
|
| 179 |
+
"caldera": "**์นผ๋ฐ๋ผ**: ๋๊ท๋ชจ ๋ถํ ํ ๋ง๊ทธ๋ง๋ฐฉ ํจ๋ชฐ๋ก ํ์ฑ๋ ๊ฑฐ๋ํ ๋ถ์ง.",
|
| 180 |
+
"crater_lake": "**ํ๊ตฌํธ**: ํ๊ตฌ๋ ์นผ๋ฐ๋ผ์ ๋ฌผ์ด ๊ณ ์ฌ ํ์ฑ๋ ํธ์.",
|
| 181 |
+
"lava_plateau": "**์ฉ์๋์ง**: ์ด๊ทน ๋ถ์ถ๋ก ํ๋ฌด์์ง ์ฉ์์ด ๋๊ฒ ํผ์ณ์ ธ ํํํ ๋์ง ํ์ฑ.",
|
| 182 |
+
"barchan": "**๋ฐ๋ฅดํ ์ฌ๊ตฌ**: ๋ฐ๋์ด ํ ๋ฐฉํฅ์์ ๋ถ ๋ ํ์ฑ๋๋ ์ด์น๋ฌ ๋ชจ์์ ์ฌ๊ตฌ.",
|
| 183 |
+
"mesa_butte": "**๋ฉ์ฌ/๋ทฐํธ**: ์ฐจ๋ณ์นจ์์ผ๋ก ๋จ์ ํ์์ง. ๋ฉ์ฌ๋ ํฌ๊ณ ํํ, ๋ทฐํธ๋ ์๊ณ ๋์ต๋๋ค.",
|
| 184 |
+
"karst_doline": "**๋๋ฆฌ๋ค**: ์ํ์ ์ฉ์์ผ๋ก ํ์ฑ๋ ์ํน ํ์ธ ์์ง.",
|
| 185 |
+
"coastal_cliff": "**ํด์ ์ ๋ฒฝ**: ํ๋์ ์นจ์์ผ๋ก ํ์ฑ๋ ์ ๋ฒฝ.",
|
| 186 |
+
"spit_lagoon": "**์ฌ์ทจ+์ํธ**: ์ฐ์๋ฅ์ ์ํด ํด์ ๋ฌผ์ด ๊ธธ๊ฒ ์์ธ ์ฌ์ทจ๊ฐ ๋ง์ ๋ง์ ์ํธ๋ฅผ ํ์ฑํฉ๋๋ค.",
|
| 187 |
+
"tombolo": "**์ก๊ณ์ฌ์ฃผ**: ์ฐ์๋ฅ์ ์ํ ํด์ ์ผ๋ก ์ก์ง์ ์ฌ์ด ๋ชจ๋ํฑ์ผ๋ก ์ฐ๊ฒฐ๋ ์งํ.",
|
| 188 |
+
"ria_coast": "**๋ฆฌ์์ค์ ํด์**: ๊ณผ๊ฑฐ ํ๊ณก์ด ํด์๋ฉด ์์น์ผ๋ก ์นจ์๋์ด ํ์ฑ๋ ํฑ๋ ๋ชจ์ ํด์์ .",
|
| 189 |
+
"sea_arch": "**ํด์์์น**: ๊ณถ์์ ํ๋ ์นจ์์ผ๋ก ํ์ฑ๋ ์์นํ ์งํ.",
|
| 190 |
+
"coastal_dune": "**ํด์์ฌ๊ตฌ**: ํด๋น์ ๋ชจ๋๊ฐ ๋ฐ๋์ ์ํด ์ก์ง ์ชฝ์ผ๋ก ์ด๋ฐ๋์ด ํ์ฑ๋ ๋ชจ๋ ์ธ๋.",
|
| 191 |
+
# ์๋ก ์ถ๊ฐ๋ ์งํ
|
| 192 |
+
"uvala": "**์ฐ๋ฐ๋ผ**: ์ฌ๋ฌ ๋๋ฆฌ๋ค๊ฐ ํฉ๏ฟฝ๏ฟฝ์ ธ ํ์ฑ๋ ๋ณตํฉ ์์ง. ๋๋ฆฌ๋ค๋ณด๋ค ํฌ๊ณ ๋ถ๊ท์นํ ํํ.",
|
| 193 |
+
"tower_karst": "**ํ์นด๋ฅด์คํธ**: ์์ง ์ ๋ฒฝ์ ๊ฐ์ง ํ ๋ชจ์ ์ํ์ ๋ด์ฐ๋ฆฌ. ์ค๊ตญ ๊ตฌ์ด๋ฆฐ์ด ๋ํ์ .",
|
| 194 |
+
"karren": "**์นด๋ **: ๋น๋ฌผ์ ์ํ ์ฉ์์ผ๋ก ์ํ์ ํ๋ฉด์ ํ์ฑ๋ ํ๊ณผ ๋ฆฟ์ง. ํด๋ฆฐํธ/๊ทธ๋ผ์ดํฌ ํฌํจ.",
|
| 195 |
+
"transverse_dune": "**ํก์ฌ๊ตฌ**: ๋ฐ๋ ๋ฐฉํฅ์ ์์ง์ผ๋ก ๊ธธ๊ฒ ํ์ฑ๋ ์ฌ๊ตฌ์ด. ๋ชจ๋ ๊ณต๊ธ์ด ํ๋ถํ ๋ ๋ฐ๋ฌ.",
|
| 196 |
+
"star_dune": "**์ฑ์ฌ๊ตฌ**: ๋ค๋ฐฉํฅ ๋ฐ๋์ ์ํด ๋ณ ๋ชจ์์ผ๋ก ํ์ฑ๋ ์ฌ๊ตฌ. ๋์ด๊ฐ ๋๊ณ ์ด๋์ด ์ ์.",
|
| 197 |
+
}
|
| 198 |
+
st.info(descriptions.get(landform_key, "์ค๋ช
์ค๋น ์ค์
๋๋ค."))
|
| 199 |
+
|
| 200 |
+
# ========== ํ์ฑ ๊ณผ์ ์ ๋๋ฉ์ด์
==========
|
| 201 |
+
if landform_key in ANIMATED_LANDFORM_GENERATORS:
|
| 202 |
+
st.markdown("---")
|
| 203 |
+
st.subheader("๐ฌ ํ์ฑ ๊ณผ์ ")
|
| 204 |
+
|
| 205 |
+
# ์๋ ์ฌ์ ์ค์ด๋ฉด session_state์ stage ์ฌ์ฉ
|
| 206 |
+
if st.session_state.get('auto_playing', False):
|
| 207 |
+
stage_value = st.session_state.get('auto_stage', 0.0)
|
| 208 |
+
st.slider(
|
| 209 |
+
"ํ์ฑ ๋จ๊ณ (์๋ ์ฌ์ ์ค...)",
|
| 210 |
+
0.0, 1.0, stage_value, 0.05,
|
| 211 |
+
key="gallery_stage_slider",
|
| 212 |
+
disabled=True
|
| 213 |
+
)
|
| 214 |
+
else:
|
| 215 |
+
stage_value = st.slider(
|
| 216 |
+
"ํ์ฑ ๋จ๊ณ (0% = ์์, 100% = ์์ฑ)",
|
| 217 |
+
0.0, 1.0, 1.0, 0.05,
|
| 218 |
+
key="gallery_stage_slider"
|
| 219 |
+
)
|
| 220 |
+
|
| 221 |
+
anim_func = ANIMATED_LANDFORM_GENERATORS[landform_key]
|
| 222 |
+
stage_elev = anim_func(gallery_grid_size, stage_value)
|
| 223 |
+
|
| 224 |
+
# ๋ฌผ ์์ฑ
|
| 225 |
+
stage_water = np.maximum(0, -stage_elev + 1.0)
|
| 226 |
+
stage_water[stage_elev > 2] = 0
|
| 227 |
+
|
| 228 |
+
# ์ ์์ง ๋ฌผ ์ฒ๋ฆฌ
|
| 229 |
+
if landform_key == "alluvial_fan":
|
| 230 |
+
apex_y = int(gallery_grid_size * 0.15)
|
| 231 |
+
center = gallery_grid_size // 2
|
| 232 |
+
for r in range(apex_y + 5):
|
| 233 |
+
for dc in range(-2, 3):
|
| 234 |
+
c = center + dc
|
| 235 |
+
if 0 <= c < gallery_grid_size:
|
| 236 |
+
stage_water[r, c] = 3.0
|
| 237 |
+
|
| 238 |
+
# 3D ๋ ๋๋ง
|
| 239 |
+
fig_stage = render_terrain_plotly(
|
| 240 |
+
stage_elev,
|
| 241 |
+
f"{selected_landform} - {int(stage_value*100)}%",
|
| 242 |
+
add_water=True,
|
| 243 |
+
water_depth_grid=stage_water,
|
| 244 |
+
water_level=-999,
|
| 245 |
+
force_camera=False, # ์นด๋ฉ๋ผ ์ด๋ ํ์ฉ
|
| 246 |
+
landform_type=landform_type
|
| 247 |
+
)
|
| 248 |
+
st.plotly_chart(fig_stage, use_container_width=True, key="stage_view")
|
| 249 |
+
|
| 250 |
+
# ์๋ ์ฌ์ (์ธ์
์ํ ํ์ฉ)
|
| 251 |
+
col_play, col_step = st.columns(2)
|
| 252 |
+
with col_play:
|
| 253 |
+
if st.button("โถ๏ธ ์๋ ์ฌ์ ์์", key="auto_play"):
|
| 254 |
+
st.session_state['auto_playing'] = True
|
| 255 |
+
st.session_state['auto_stage'] = 0.0
|
| 256 |
+
with col_step:
|
| 257 |
+
if st.button("โน๏ธ ์ ์ง", key="stop_play"):
|
| 258 |
+
st.session_state['auto_playing'] = False
|
| 259 |
+
|
| 260 |
+
# ์๋ ์ฌ์ ์ค์ด๋ฉด stage ์๋ ์ฆ๊ฐ
|
| 261 |
+
if st.session_state.get('auto_playing', False):
|
| 262 |
+
current_stage = st.session_state.get('auto_stage', 0.0)
|
| 263 |
+
if current_stage < 1.0:
|
| 264 |
+
st.session_state['auto_stage'] = current_stage + 0.1
|
| 265 |
+
import time
|
| 266 |
+
time.sleep(0.5)
|
| 267 |
+
st.rerun()
|
| 268 |
+
else:
|
| 269 |
+
st.session_state['auto_playing'] = False
|
| 270 |
+
st.success("โ
์๋ฃ!")
|
| 271 |
+
|
| 272 |
+
st.caption("๐ก **Tip:** ์นด๋ฉ๋ผ ๊ฐ๋๋ฅผ ๋จผ์ ์กฐ์ ํ ํ ์๋ ์ฌ์ํ๋ฉด ์ ์ง๋ฉ๋๋ค.")
|
terrain_processes.html
ADDED
|
@@ -0,0 +1,474 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
๏ปฟ<!DOCTYPE html>
|
| 2 |
+
<html lang="ko">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8" />
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 6 |
+
<title>์งํ ํ์ฑ ์์ฉ | Geo Lab</title>
|
| 7 |
+
<style>
|
| 8 |
+
@import url("https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;600;700&family=Noto+Sans+KR:wght@400;500;700&display=swap");
|
| 9 |
+
|
| 10 |
+
:root {
|
| 11 |
+
--ink: #e7eef9;
|
| 12 |
+
--muted: #a5b8d4;
|
| 13 |
+
--panel: #0f1c32;
|
| 14 |
+
--panel-2: #0c1628;
|
| 15 |
+
--stroke: #1f2c46;
|
| 16 |
+
--accent: #f59e0b;
|
| 17 |
+
--accent-2: #7dd3fc;
|
| 18 |
+
--accent-3: #34d399;
|
| 19 |
+
--glow: rgba(126, 200, 255, 0.14);
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
* {
|
| 23 |
+
box-sizing: border-box;
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
body {
|
| 27 |
+
margin: 0;
|
| 28 |
+
min-height: 100vh;
|
| 29 |
+
font-family: "Noto Sans KR", "Space Grotesk", system-ui, -apple-system, sans-serif;
|
| 30 |
+
color: var(--ink);
|
| 31 |
+
background:
|
| 32 |
+
radial-gradient(circle at 15% 20%, #192742 0%, #0b1220 35%),
|
| 33 |
+
radial-gradient(circle at 80% 0%, #13233f 0%, transparent 30%),
|
| 34 |
+
linear-gradient(120deg, #0c1424 0%, #0b1220 70%);
|
| 35 |
+
overflow-x: hidden;
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
.canvas {
|
| 39 |
+
position: relative;
|
| 40 |
+
max-width: 1200px;
|
| 41 |
+
margin: 0 auto;
|
| 42 |
+
padding: 48px 22px 64px;
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
.grid-bg {
|
| 46 |
+
position: fixed;
|
| 47 |
+
inset: 0;
|
| 48 |
+
pointer-events: none;
|
| 49 |
+
background-image:
|
| 50 |
+
linear-gradient(var(--glow) 1px, transparent 1px),
|
| 51 |
+
linear-gradient(90deg, var(--glow) 1px, transparent 1px);
|
| 52 |
+
background-size: 48px 48px;
|
| 53 |
+
mask-image: radial-gradient(circle at 60% 40%, #000 0%, rgba(0, 0, 0, 0.12) 50%, transparent 70%);
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
header.hero {
|
| 57 |
+
display: grid;
|
| 58 |
+
grid-template-columns: 1.2fr 0.8fr;
|
| 59 |
+
gap: 28px;
|
| 60 |
+
align-items: end;
|
| 61 |
+
margin-bottom: 28px;
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
.eyebrow {
|
| 65 |
+
display: inline-flex;
|
| 66 |
+
align-items: center;
|
| 67 |
+
gap: 8px;
|
| 68 |
+
padding: 8px 12px;
|
| 69 |
+
border-radius: 999px;
|
| 70 |
+
background: rgba(255, 255, 255, 0.05);
|
| 71 |
+
border: 1px solid var(--stroke);
|
| 72 |
+
font-size: 13px;
|
| 73 |
+
letter-spacing: 0.6px;
|
| 74 |
+
text-transform: uppercase;
|
| 75 |
+
color: var(--muted);
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
h1 {
|
| 79 |
+
margin: 10px 0 12px;
|
| 80 |
+
font-family: "Space Grotesk", "Noto Sans KR", sans-serif;
|
| 81 |
+
font-size: clamp(30px, 4vw, 42px);
|
| 82 |
+
letter-spacing: -0.5px;
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
.lede {
|
| 86 |
+
margin: 0;
|
| 87 |
+
font-size: 17px;
|
| 88 |
+
line-height: 1.5;
|
| 89 |
+
color: var(--muted);
|
| 90 |
+
}
|
| 91 |
+
|
| 92 |
+
.tags {
|
| 93 |
+
display: flex;
|
| 94 |
+
flex-wrap: wrap;
|
| 95 |
+
gap: 10px;
|
| 96 |
+
margin-top: 16px;
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
.tag {
|
| 100 |
+
padding: 7px 11px;
|
| 101 |
+
border-radius: 10px;
|
| 102 |
+
border: 1px solid var(--stroke);
|
| 103 |
+
color: var(--ink);
|
| 104 |
+
background: rgba(255, 255, 255, 0.03);
|
| 105 |
+
font-size: 13px;
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
.panel {
|
| 109 |
+
background: linear-gradient(140deg, var(--panel) 0%, var(--panel-2) 100%);
|
| 110 |
+
border: 1px solid var(--stroke);
|
| 111 |
+
border-radius: 18px;
|
| 112 |
+
padding: 20px;
|
| 113 |
+
position: relative;
|
| 114 |
+
overflow: hidden;
|
| 115 |
+
}
|
| 116 |
+
|
| 117 |
+
.panel::before {
|
| 118 |
+
content: "";
|
| 119 |
+
position: absolute;
|
| 120 |
+
inset: 0;
|
| 121 |
+
background: radial-gradient(circle at 20% 20%, rgba(255, 255, 255, 0.04), transparent 35%);
|
| 122 |
+
pointer-events: none;
|
| 123 |
+
}
|
| 124 |
+
|
| 125 |
+
.panel h3 {
|
| 126 |
+
margin: 0 0 8px;
|
| 127 |
+
font-size: 17px;
|
| 128 |
+
letter-spacing: -0.2px;
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
+
.panel p {
|
| 132 |
+
margin: 0;
|
| 133 |
+
color: var(--muted);
|
| 134 |
+
line-height: 1.5;
|
| 135 |
+
font-size: 14px;
|
| 136 |
+
}
|
| 137 |
+
|
| 138 |
+
.stack {
|
| 139 |
+
display: grid;
|
| 140 |
+
gap: 14px;
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
.process-grid {
|
| 144 |
+
display: grid;
|
| 145 |
+
gap: 16px;
|
| 146 |
+
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
| 147 |
+
margin: 18px 0 32px;
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
.pill {
|
| 151 |
+
display: inline-flex;
|
| 152 |
+
align-items: center;
|
| 153 |
+
gap: 7px;
|
| 154 |
+
padding: 6px 10px;
|
| 155 |
+
border-radius: 999px;
|
| 156 |
+
font-size: 12px;
|
| 157 |
+
color: var(--ink);
|
| 158 |
+
border: 1px solid var(--stroke);
|
| 159 |
+
background: rgba(255, 255, 255, 0.03);
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
.pill.accent {
|
| 163 |
+
background: rgba(245, 158, 11, 0.12);
|
| 164 |
+
border-color: rgba(245, 158, 11, 0.5);
|
| 165 |
+
color: #ffd58f;
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
.pill.cool {
|
| 169 |
+
background: rgba(125, 211, 252, 0.12);
|
| 170 |
+
border-color: rgba(125, 211, 252, 0.5);
|
| 171 |
+
color: #c8f1ff;
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
.pill.green {
|
| 175 |
+
background: rgba(52, 211, 153, 0.12);
|
| 176 |
+
border-color: rgba(52, 211, 153, 0.5);
|
| 177 |
+
color: #c1ffe3;
|
| 178 |
+
}
|
| 179 |
+
|
| 180 |
+
.timeline {
|
| 181 |
+
display: grid;
|
| 182 |
+
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
| 183 |
+
gap: 14px;
|
| 184 |
+
margin: 10px 0;
|
| 185 |
+
}
|
| 186 |
+
|
| 187 |
+
.step {
|
| 188 |
+
border-left: 2px solid var(--stroke);
|
| 189 |
+
padding-left: 14px;
|
| 190 |
+
position: relative;
|
| 191 |
+
}
|
| 192 |
+
|
| 193 |
+
.step::before {
|
| 194 |
+
content: "";
|
| 195 |
+
position: absolute;
|
| 196 |
+
width: 9px;
|
| 197 |
+
height: 9px;
|
| 198 |
+
border-radius: 50%;
|
| 199 |
+
background: var(--accent-2);
|
| 200 |
+
border: 2px solid #081020;
|
| 201 |
+
left: -6px;
|
| 202 |
+
top: 6px;
|
| 203 |
+
box-shadow: 0 0 12px rgba(125, 211, 252, 0.5);
|
| 204 |
+
}
|
| 205 |
+
|
| 206 |
+
.step strong {
|
| 207 |
+
display: block;
|
| 208 |
+
margin-bottom: 6px;
|
| 209 |
+
font-size: 14px;
|
| 210 |
+
letter-spacing: -0.2px;
|
| 211 |
+
}
|
| 212 |
+
|
| 213 |
+
.legend {
|
| 214 |
+
display: flex;
|
| 215 |
+
flex-wrap: wrap;
|
| 216 |
+
gap: 12px;
|
| 217 |
+
margin-top: 10px;
|
| 218 |
+
}
|
| 219 |
+
|
| 220 |
+
.legend span {
|
| 221 |
+
display: inline-flex;
|
| 222 |
+
align-items: center;
|
| 223 |
+
gap: 8px;
|
| 224 |
+
font-size: 13px;
|
| 225 |
+
color: var(--muted);
|
| 226 |
+
}
|
| 227 |
+
|
| 228 |
+
.legend b {
|
| 229 |
+
display: inline-block;
|
| 230 |
+
width: 12px;
|
| 231 |
+
height: 12px;
|
| 232 |
+
border-radius: 4px;
|
| 233 |
+
}
|
| 234 |
+
|
| 235 |
+
.flow {
|
| 236 |
+
display: grid;
|
| 237 |
+
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
| 238 |
+
gap: 16px;
|
| 239 |
+
margin-top: 18px;
|
| 240 |
+
}
|
| 241 |
+
|
| 242 |
+
details {
|
| 243 |
+
border: 1px solid var(--stroke);
|
| 244 |
+
border-radius: 14px;
|
| 245 |
+
padding: 12px 14px;
|
| 246 |
+
background: rgba(255, 255, 255, 0.02);
|
| 247 |
+
transition: border-color 150ms ease, background 150ms ease;
|
| 248 |
+
}
|
| 249 |
+
|
| 250 |
+
details[open] {
|
| 251 |
+
border-color: rgba(125, 211, 252, 0.6);
|
| 252 |
+
background: rgba(125, 211, 252, 0.04);
|
| 253 |
+
}
|
| 254 |
+
|
| 255 |
+
summary {
|
| 256 |
+
cursor: pointer;
|
| 257 |
+
font-weight: 600;
|
| 258 |
+
font-size: 14px;
|
| 259 |
+
outline: none;
|
| 260 |
+
}
|
| 261 |
+
|
| 262 |
+
.note {
|
| 263 |
+
margin: 10px 0 0;
|
| 264 |
+
color: var(--muted);
|
| 265 |
+
font-size: 13px;
|
| 266 |
+
line-height: 1.5;
|
| 267 |
+
}
|
| 268 |
+
|
| 269 |
+
@media (max-width: 900px) {
|
| 270 |
+
header.hero {
|
| 271 |
+
grid-template-columns: 1fr;
|
| 272 |
+
}
|
| 273 |
+
}
|
| 274 |
+
</style>
|
| 275 |
+
</head>
|
| 276 |
+
<body>
|
| 277 |
+
<div class="grid-bg"></div>
|
| 278 |
+
<div class="canvas">
|
| 279 |
+
<header class="hero">
|
| 280 |
+
<div>
|
| 281 |
+
<div class="eyebrow">Geomorph ยท Process Map</div>
|
| 282 |
+
<h1>์งํ ํ์ฑ ์์ฉ์ ํ๋ฆ์ ํ๋์</h1>
|
| 283 |
+
<p class="lede">
|
| 284 |
+
๋ด์ (ํ๊ตฌ์กฐยทํ์ฐ) ์๋์ง์ ์ธ์ (๊ธฐํยท์ค๋ ฅ) ์๋์ง๊ฐ ์ํธ์์ฉํ๋ฉฐ ์งํ๋ฅผ ๊น๊ณ ์๊ณ ๋ค์ด์ฌ๋ฆฝ๋๋ค.
|
| 285 |
+
์ฃผ์ ๋จ๊ณ์ ์งํ ๊ฒฐ๊ณผ๋ฅผ ๋น ๋ฅด๊ฒ ํ์ด๋ณผ ์ ์๋ ์์ฝ HTML์
๋๋ค.
|
| 286 |
+
</p>
|
| 287 |
+
<div class="tags">
|
| 288 |
+
<div class="tag">๋ด์ ์๋์ง: ํ๊ตฌ์กฐ, ์ต๊ธฐ, ํ์ฐ</div>
|
| 289 |
+
<div class="tag">์ธ์ ์๋์ง: ํํ, ์ค๋ ฅ, ์นจ์ยท์ด๋ฐยทํด์ </div>
|
| 290 |
+
<div class="tag">์๊ฐ ์ค์ผ์ผ: ๋จ๋ฐ์ฑ(์ฌ๋ฉด ๋ถ๊ดด) โ ์ฅ๊ธฐ(์ต๊ธฐยทํ์คํ)</div>
|
| 291 |
+
</div>
|
| 292 |
+
</div>
|
| 293 |
+
<div class="panel stack">
|
| 294 |
+
<div class="pill accent">ํต์ฌ ๋งฅ๋ฝ</div>
|
| 295 |
+
<h3>์งํ์ ๋ง๋๋ ์ธ ํ</h3>
|
| 296 |
+
<p>
|
| 297 |
+
1) ์ง๊ตฌ ๋ด๋ถ ์๋์ง(ํ๊ตฌ์กฐ ์ด๋, ๋ง๊ทธ๋ง)๋ก ์งํ๊ฐ ์ต๊ธฐยท๋ณํ๋๋ค.
|
| 298 |
+
2) ๊ธฐํ๊ฐ ์ง๋ฐฐํ๋ ํํยท์นจ์ยท์ด๋ฐยทํด์ ์ด ๋์ด๋ฅผ ๊น๊ณ ๋ฉ์ด๋ค.
|
| 299 |
+
3) ์ค๋ ฅ์ ์ฌ๋ฉด ์์ ์ฑ์ ํต์ ํ๋ฉฐ ๋๋ฐ์ ๋ถ๊ดด๋ก ๊ท ํ์ ์ฌ์กฐ์ ํ๋ค.
|
| 300 |
+
</p>
|
| 301 |
+
<div class="legend">
|
| 302 |
+
<span><b style="background: var(--accent);"></b> ๋ด์ ์์ฉ</span>
|
| 303 |
+
<span><b style="background: var(--accent-2);"></b> ์ธ์ ์์ฉ</span>
|
| 304 |
+
<span><b style="background: var(--accent-3);"></b> ๊ฒฐ๊ณผ ์งํ</span>
|
| 305 |
+
</div>
|
| 306 |
+
</div>
|
| 307 |
+
</header>
|
| 308 |
+
|
| 309 |
+
<section class="process-grid">
|
| 310 |
+
<div class="panel">
|
| 311 |
+
<div class="pill accent">๋ด์ ยท ์๋์ง ๊ณต๊ธ</div>
|
| 312 |
+
<h3>ํ๊ตฌ์กฐ ์ด๋ & ์ต๊ธฐ</h3>
|
| 313 |
+
<p>
|
| 314 |
+
์์ถยท์ธ์ฅยทํก์ด๋์ ๋ฐ๋ผ ์ง๊ฐ์ด ์ ํยท๋จ์ธตยท์ต๊ธฐํ๋ฉฐ ๊ณ ๋ ๋๋น๋ฅผ ๋ง๋ ๋ค.
|
| 315 |
+
๋์ ์์น์ ๋์ธ ์์ฒด๋ ์ดํ ์ธ์ ์์ฉ์ ํ์ ์ด ๋๋ค.
|
| 316 |
+
</p>
|
| 317 |
+
</div>
|
| 318 |
+
<div class="panel">
|
| 319 |
+
<div class="pill accent">๋ด์ ยท ๋ฌผ์ง ๊ณต๊ธ</div>
|
| 320 |
+
<h3>ํ์ฐ ํ๋ / ๊ด์
</h3>
|
| 321 |
+
<p>
|
| 322 |
+
๋ง๊ทธ๋ง ๋ถ์ถยท๊ด์
์ผ๋ก ์๋ก์ด ์์์ด ์์ด๊ณ ์ด๋ณํ์ด ์ผ์ด๋๋ค.
|
| 323 |
+
์ฉ์๋์ง, ์์ยท๋ณตํฉํ์ฐ, ํ์ฐ๋, ์ํํ ๋ฑ์ด ํ์ฑ๋๋ค.
|
| 324 |
+
</p>
|
| 325 |
+
</div>
|
| 326 |
+
<div class="panel">
|
| 327 |
+
<div class="pill cool">์ธ์ ยท ์ฌ๋ฃ ์ฝํ</div>
|
| 328 |
+
<h3>ํํ (๋ฌผ๋ฆฌยทํํ)</h3>
|
| 329 |
+
<p>
|
| 330 |
+
๋๊ฒฐ์ตํดยท๋ฐ๋ฆฌยทๅกฉ์ ยทํ์ฐยท์ฐํ ๋ฑ์ด ์์์ ๋ถ์/์ฉํดํด ์
๋๋ฅผ ์ค์ด๊ณ
|
| 331 |
+
์ฌ๋ฉด์ ๋์จํ๊ฒ ๋ง๋ค์ด ์นจ์๊ณผ ๋ถ๊ดด๋ฅผ ์ค๋นํ๋ค.
|
| 332 |
+
</p>
|
| 333 |
+
</div>
|
| 334 |
+
<div class="panel">
|
| 335 |
+
<div class="pill cool">์ธ์ ยท ์ค๋ ฅ</div>
|
| 336 |
+
<h3>์ฌ๋ฉด ๋ถ๊ดด / ์ง๋ ์ด๋</h3>
|
| 337 |
+
<p>
|
| 338 |
+
๋์, ์ฌํ, ์งํ๋ฆผ, ํฌํ ๋ฑ์ผ๋ก ๊ณ ๋์ฐจ๊ฐ ๊ธ๊ฒฉํ ์๋งํด์ง๋ค.
|
| 339 |
+
๊ฐ์ฐยท์ง์งยทํํ ์ฌํ๊ฐ ๋ฐฉ์์ ์ญํ ์ ํ๋ค.
|
| 340 |
+
</p>
|
| 341 |
+
</div>
|
| 342 |
+
<div class="panel">
|
| 343 |
+
<div class="pill cool">์ธ์ ยท ์นจ์ยท์ด๋ฐ</div>
|
| 344 |
+
<h3>๋ฌผ ยท ๋ฐ๋ ยท ์ผ์</h3>
|
| 345 |
+
<p>
|
| 346 |
+
ํ์ฒ(์์), ํด์(ํ์), ๋นํ(์ค๋ง์ฐฐ), ์ฌ๋ง(ํ์)์ด ์ฌ๋ฃ๋ฅผ ๊น๊ณ ๋จผ ๊ณณ์ผ๋ก ์ด๋ฐํ๋ค.
|
| 347 |
+
์์ฉ๋์ดยท๋๋ฅยท๋น์์ด ์ฃผ์ ๋ฉ์ปค๋์ฆ์ด๋ค.
|
| 348 |
+
</p>
|
| 349 |
+
</div>
|
| 350 |
+
<div class="panel">
|
| 351 |
+
<div class="pill cool">์ธ์ ยท ์ถ์ </div>
|
| 352 |
+
<h3>ํด์ & ๊ณ ๊ฒฐ</h3>
|
| 353 |
+
<p>
|
| 354 |
+
์๋์ง๊ฐ ๋ฎ์์ง๋ ์ง์ (์ผ๊ฐ์ฃผ, ์ถฉ์ ์ ์์ง, ๋นํด์, ์ฌ๊ตฌ, ํธ์)์ ์
์๊ฐ ์์ฌ
|
| 355 |
+
์ธต๋ฆฌ๊ฐ ๋ฐ๋ฌํ๊ณ ์๊ฐ์ด ์ง๋๋ฉด ๊ณ ๊ฒฐยท๊ต๊ฒฐ๋๋ค.
|
| 356 |
+
</p>
|
| 357 |
+
</div>
|
| 358 |
+
</section>
|
| 359 |
+
|
| 360 |
+
<section class="panel">
|
| 361 |
+
<div class="pill green">๋จ๊ณ๋ณ ํ๋ฆ</div>
|
| 362 |
+
<h3>์งํ ํ์ฑ ํ์๋ผ์ธ</h3>
|
| 363 |
+
<div class="timeline">
|
| 364 |
+
<div class="step">
|
| 365 |
+
<strong>1. ์๋์ง ๋ถ์ฌ (๋ด์ )</strong>
|
| 366 |
+
ํ๊ตฌ์กฐ ๋ณํยท์ต๊ธฐยทํ์ฐ ๋ถ์ถ๋ก ๊ณ ๋ ๋๋น๊ฐ ์ปค์ง๊ณ ์๋ก์ด ์์์ด ๋
ธ์ถ๋๋ค.
|
| 367 |
+
</div>
|
| 368 |
+
<div class="step">
|
| 369 |
+
<strong>2. ์ฝํ (ํํ)</strong>
|
| 370 |
+
๋๊ฒฐ์ตํด, ์ดํฝ์ฐฝ, ํํ ์ฉํด๊ฐ ๊ท ์ด์ ๋ํ ์์ฒด ๊ฐ๋๋ฅผ ๋ฎ์ถ๋ค.
|
| 371 |
+
</div>
|
| 372 |
+
<div class="step">
|
| 373 |
+
<strong>3. ์ค๋ ฅ ๋ถ์์ </strong>
|
| 374 |
+
์ฌ๋ฉด ๊ฒฝ์ฌ์ ์งํ์ ์์น์ด ์๊ณ ์ํ๋ฅผ ๋ง๋ค๊ณ ๋ถ๊ดดยทํ๊ฐ์ด ๋ฐ์ํ๋ค.
|
| 375 |
+
</div>
|
| 376 |
+
<div class="step">
|
| 377 |
+
<strong>4. ์นจ์ ยท ์ด๋ฐ</strong>
|
| 378 |
+
ํ์ฒยท๋นํยทํ๋ยท๋ฐ๋์ด ์
์๋ฅผ ๋ผ์ด๋ด์ด ํ๋ฅ๋ ํด์์ผ๋ก ์ด๋์ํจ๋ค.
|
| 379 |
+
</div>
|
| 380 |
+
<div class="step">
|
| 381 |
+
<strong>5. ํด์ ยท ์ถ์ </strong>
|
| 382 |
+
์ ์/ํ์์ด ๊ฐ์ํ๋ ์์ฌ๋ฉดยท๊ณก๋ฅ ์์ชฝยทํ๊ตฌยทํธ์ยท๋นํ ๋ง๋จ์ ์ฌ๋ฃ๊ฐ ์์ธ๋ค.
|
| 383 |
+
</div>
|
| 384 |
+
<div class="step">
|
| 385 |
+
<strong>6. ์ฌ๊ท ํ</strong>
|
| 386 |
+
์ฅ๊ธฐ์ ์ผ๋ก ์ต๊ธฐ์ ์นจ์์ด ๋ง๋ฌผ๋ ค ํ์คํ๊ฐ ์งํ๋๋ฉฐ, ์๋ก์ด ์ต๊ธฐ๊ฐ ์์๋๋ฉด ์ฃผ๊ธฐ๊ฐ ๋ฐ๋ณต๋๋ค.
|
| 387 |
+
</div>
|
| 388 |
+
</div>
|
| 389 |
+
</section>
|
| 390 |
+
|
| 391 |
+
<section class="flow">
|
| 392 |
+
<div class="panel stack">
|
| 393 |
+
<div class="pill accent">๋ด์ ์์ฉ โ ๊ฒฐ๊ณผ ์งํ</div>
|
| 394 |
+
<details>
|
| 395 |
+
<summary>์ง๊ฐ ์์ถยท์ธ์ฅ</summary>
|
| 396 |
+
<p class="note">
|
| 397 |
+
์กฐ์ฐ๋: ์ต๊ณก์ฐ๋งฅ, ์ถฉ์๋จ์ธต, ์ต๊ณก ๊ณก์ . <br />
|
| 398 |
+
์ธ์ฅ๋: ๊ทธ๋ฉ๋ฒค(๋ฅต), ํธ๋ฅด์คํธ, ๋ฆฌํํธ ๊ณ๊ณก. <br />
|
| 399 |
+
์ ๋จ๋: ์ฌ๊ต๋จ์ธต, ํธ๋ง์ ํธ๋ฆฌ ๋ฐ๋ฌ.
|
| 400 |
+
</p>
|
| 401 |
+
</details>
|
| 402 |
+
<details>
|
| 403 |
+
<summary>ํ์ฐ / ์ด์ ํ๋</summary>
|
| 404 |
+
<p class="note">
|
| 405 |
+
์ฉ์๋์ง, ์์ยท๋ณตํฉยท์ํํ์ฐ, ์นผ๋ฐ๋ผ, ํ์ฐ๋, ์ด์ ์จ์ฒ ํ
๋ผ์ค.
|
| 406 |
+
</p>
|
| 407 |
+
</details>
|
| 408 |
+
</div>
|
| 409 |
+
|
| 410 |
+
<div class="panel stack">
|
| 411 |
+
<div class="pill cool">์ธ์ ์์ฉ โ ๊ฒฐ๊ณผ ์งํ</div>
|
| 412 |
+
<details open>
|
| 413 |
+
<summary>ํํ + ์ค๋ ฅ</summary>
|
| 414 |
+
<p class="note">
|
| 415 |
+
์ ๋ฆฌ ๋ฐ๋ฌ ์๊ดด๋ฅ, ํ ๋ฅด, ํํ๋(์ฐจ๋ณํํ), ํค๋ฌ์ค ์ฌ๋ฉด, ์คํฌ๋ฆฌ ์ฝ.
|
| 416 |
+
</p>
|
| 417 |
+
</details>
|
| 418 |
+
<details>
|
| 419 |
+
<summary>ํ์ฒ ยท ํธ์</summary>
|
| 420 |
+
<p class="note">
|
| 421 |
+
์์๊ณก, V์๊ณก, ํฌํธํ, ๊ณก๋ฅยท์ฐ๊ฐํธ, ๋ฒ๋์, ํ์ฒ๋จ๊ตฌ, ์ผ๊ฐ์ฃผ/์ ์์ง.
|
| 422 |
+
</p>
|
| 423 |
+
</details>
|
| 424 |
+
<details>
|
| 425 |
+
<summary>ํด์</summary>
|
| 426 |
+
<p class="note">
|
| 427 |
+
ํด์์ ยทํด์๋, ํ์๋์ง, ํด์๋/์ฃผ์์ ๋ฆฌ ์ ๋ฒฝ, ์์คํยท์์น,
|
| 428 |
+
์ฌ๋นยท์ํธยท์ฌ์ทจยทํฐ๋ณผ๋ก.
|
| 429 |
+
</p>
|
| 430 |
+
</details>
|
| 431 |
+
<details>
|
| 432 |
+
<summary>๋นํ</summary>
|
| 433 |
+
<p class="note">
|
| 434 |
+
U์๊ณก, ์ํทยท์๋ ํธยทํผ, ํ๋ฌด์์ฃผ์ ๋ฐ ๋ก์๋ฌดํค๋ค,
|
| 435 |
+
๋ชจ๋ ์ธ(์ข
ยท๋จยท์ธกยทํด์์ฑ), ๋๋ผ๋ฆฐ, ์์ค์ปค, ์ผ์.
|
| 436 |
+
</p>
|
| 437 |
+
</details>
|
| 438 |
+
<details>
|
| 439 |
+
<summary>๊ฑด์กฐ ยท ํ์</summary>
|
| 440 |
+
<p class="note">
|
| 441 |
+
ํฌํธํํ ์ฌ๋งํฌํธ, ๋ฒํฐ๋งํฌ, ์ฌ๊ตฌ(๋ฐ๋ฅดํยทํ๋ผ๋ณผ๋ฆญยท์คํ), ๋ฐํ๋ ์ด์
์ค,
|
| 442 |
+
๋ฒค์นํ ํ๋ฐ์คํ, ๋ฉ์ฌยท๋ทฐํธ.
|
| 443 |
+
</p>
|
| 444 |
+
</details>
|
| 445 |
+
</div>
|
| 446 |
+
|
| 447 |
+
<div class="panel stack">
|
| 448 |
+
<div class="pill green">๋ณ์์ ๊ด์ </div>
|
| 449 |
+
<details open>
|
| 450 |
+
<summary>๊ธฐํ</summary>
|
| 451 |
+
<p class="note">
|
| 452 |
+
์๋ถยท์จ๋ ์งํญ์ด ํํ ์์์ ๊ฒฐ์ ํ๊ณ , ๋ชฌ์/ํญ์ฐ๋ ์ฌ๋ฉด ํ๊ดด์ ํ์ฒ
|
| 453 |
+
์๋์ง๋ฅผ ์ฆํญ์ํจ๋ค.
|
| 454 |
+
</p>
|
| 455 |
+
</details>
|
| 456 |
+
<details>
|
| 457 |
+
<summary>์์ํ</summary>
|
| 458 |
+
<p class="note">
|
| 459 |
+
์์ ๊ฐ๋ยท๊ท ์ง์ฑยท์ ๋ฆฌ ๊ฐ๊ฒฉ์ด ์นจ์ ์ ํญ์ ์ข์ฐํ๋ฉฐ ์ฐจ๋ณ์นจ์ ์งํ์ ๋ง๋ ๋ค.
|
| 460 |
+
</p>
|
| 461 |
+
</details>
|
| 462 |
+
<details>
|
| 463 |
+
<summary>์๊ฐ ์ค์ผ์ผ</summary>
|
| 464 |
+
<p class="note">
|
| 465 |
+
์๊ฐ: ๋์ยท์ฌํยทํ์ฐ ๋ถ์ถ. <br />
|
| 466 |
+
์~์๋ง ๋
: ๊ณก๋ฅ ๋ฐ๋ฌ, ๋นํ ์ ์งยทํํด. <br />
|
| 467 |
+
๋ฐฑ๋ง ๋
+: ์ต๊ธฐยทํ์คํ(ํ๋ํ๋ ์ธ) ์ฌ์ดํด.
|
| 468 |
+
</p>
|
| 469 |
+
</details>
|
| 470 |
+
</div>
|
| 471 |
+
</section>
|
| 472 |
+
</div>
|
| 473 |
+
</body>
|
| 474 |
+
</html>
|
terrain_processes_3d.html
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
terrain_processes_stages.html
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|