HANSOL commited on
Commit ยท
0f8040e
1
Parent(s): e60b579
Add PhD-level formation processes for 8 landforms with metadata and stage descriptions
Browse files- engine/ideal_landforms.py +762 -192
- pages/1_๐_Gallery.py +70 -2
engine/ideal_landforms.py
CHANGED
|
@@ -332,60 +332,136 @@ def create_barchan_dune(grid_size: int = 100,
|
|
| 332 |
return elevation
|
| 333 |
|
| 334 |
|
| 335 |
-
def create_coastal_cliff(grid_size: int = 100,
|
| 336 |
cliff_height: float = 30.0,
|
| 337 |
-
num_stacks: int = 2
|
|
|
|
| 338 |
"""
|
| 339 |
-
ํด์ ์ ๋ฒฝ (Coastal Cliff) + ์์คํ
|
| 340 |
|
| 341 |
-
|
| 342 |
-
|
| 343 |
-
|
| 344 |
-
|
| 345 |
-
|
| 346 |
-
|
| 347 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 348 |
"""
|
| 349 |
h, w = grid_size, grid_size
|
| 350 |
elevation = np.zeros((h, w))
|
| 351 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 352 |
# ๋ฐ๋ค (ํ๋จ)
|
| 353 |
-
|
| 354 |
-
|
|
|
|
|
|
|
|
|
|
| 355 |
|
| 356 |
# ์ก์ง + ์ ๋ฒฝ
|
|
|
|
| 357 |
for r in range(sea_line):
|
| 358 |
cliff_dist = sea_line - r
|
| 359 |
-
if cliff_dist <
|
| 360 |
-
# ์ ๋ฒฝ๋ฉด
|
| 361 |
-
|
|
|
|
| 362 |
else:
|
| 363 |
# ํํํ ์ก์ง
|
| 364 |
-
elevation[r, :] =
|
| 365 |
-
|
| 366 |
-
#
|
| 367 |
-
|
| 368 |
-
|
| 369 |
-
|
| 370 |
-
|
| 371 |
-
# ์์คํ (Sea Stacks)
|
| 372 |
-
for i in range(num_stacks):
|
| 373 |
-
sx = w // 3 + i * (w // 3)
|
| 374 |
-
sy = sea_line + 5 + i * 3
|
| 375 |
|
| 376 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 377 |
|
| 378 |
-
for
|
| 379 |
-
|
| 380 |
-
|
| 381 |
-
|
| 382 |
-
|
| 383 |
-
|
| 384 |
-
|
| 385 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 386 |
return elevation
|
| 387 |
|
| 388 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 389 |
# ============================================
|
| 390 |
# ์ ๋๋ฉ์ด์
์ฉ ํ์ฑ๊ณผ์ ํจ์ (Stage-based)
|
| 391 |
# stage: 0.0 (์์) ~ 1.0 (์์ฑ)
|
|
@@ -446,10 +522,22 @@ def create_delta_animated(grid_size: int, stage: float,
|
|
| 446 |
|
| 447 |
|
| 448 |
def create_alluvial_fan_animated(grid_size: int, stage: float,
|
| 449 |
-
cone_angle: float = 90.0, max_height: float = 50.0
|
| 450 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 451 |
h, w = grid_size, grid_size
|
| 452 |
elevation = np.zeros((h, w))
|
|
|
|
| 453 |
|
| 454 |
apex_y = int(h * 0.15)
|
| 455 |
center_x = w // 2
|
|
@@ -469,19 +557,62 @@ def create_alluvial_fan_animated(grid_size: int, stage: float,
|
|
| 469 |
max_reach = int((h - apex_y) * stage)
|
| 470 |
half_angle = np.radians(cone_angle / 2) * (0.5 + 0.5 * stage)
|
| 471 |
|
| 472 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 473 |
dist = r - apex_y
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 474 |
for c in range(w):
|
| 475 |
dx = c - center_x
|
| 476 |
if abs(np.arctan2(dx, max(dist, 1))) < half_angle:
|
| 477 |
radial = np.sqrt(dx**2 + dist**2)
|
| 478 |
-
z = max_height * (1 - radial / (max_reach * 1.5 + 0.001)) * stage
|
| 479 |
lateral_decay = 1 - abs(dx) / (w // 2)
|
| 480 |
-
|
| 481 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 482 |
return elevation
|
| 483 |
|
| 484 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 485 |
def create_meander_animated(grid_size: int, stage: float,
|
| 486 |
amplitude: float = 0.3, num_bends: int = 3) -> np.ndarray:
|
| 487 |
"""๊ณก๋ฅ ํ์ฑ๊ณผ์ ์ ๋๋ฉ์ด์
(์ง์ โ ์ฌํ โ ์ฐ๊ฐํธ โ ํ์ค๋)
|
|
@@ -841,22 +972,41 @@ def create_barchan_animated(grid_size: int, stage: float,
|
|
| 841 |
# ============================================
|
| 842 |
|
| 843 |
def create_incised_meander(grid_size: int = 100, stage: float = 1.0,
|
| 844 |
-
valley_depth: float = 80.0, num_terraces: int = 3
|
|
|
|
| 845 |
"""
|
| 846 |
๊ฐ์
๊ณก๋ฅ (Incised Meander) + ํ์๋จ๊ตฌ (River Terraces)
|
| 847 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 848 |
์ต๊ธฐ ํ๊ฒฝ์์ ๊ณก๋ฅ๊ฐ ์๋ฐ์ ํ๊ณ ๋ค์ด๊ฐ๋ฉด์ ํ์ฑ
|
| 849 |
"""
|
| 850 |
h, w = grid_size, grid_size
|
| 851 |
elevation = np.zeros((h, w))
|
| 852 |
|
| 853 |
center_x = w // 2
|
| 854 |
-
|
| 855 |
-
wl = h / 3 # 3 bends
|
| 856 |
channel_width = max(3, w // 25)
|
| 857 |
|
| 858 |
-
# ๊ธฐ
|
| 859 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 860 |
|
| 861 |
# ๊ฐ์
๊ณก๋ฅ ํ๊ธฐ
|
| 862 |
for r in range(h):
|
|
@@ -866,121 +1016,295 @@ def create_incised_meander(grid_size: int = 100, stage: float = 1.0,
|
|
| 866 |
for c in range(w):
|
| 867 |
dist = abs(c - meander_x)
|
| 868 |
|
|
|
|
|
|
|
|
|
|
| 869 |
if dist < channel_width:
|
| 870 |
# ํ๋ (๊ฐ์ฅ ๊น์)
|
| 871 |
-
elevation[r, c] =
|
| 872 |
-
elif dist < channel_width *
|
| 873 |
-
#
|
| 874 |
-
t = (dist - channel_width) / channel_width
|
| 875 |
-
elevation[r, c] =
|
| 876 |
-
|
| 877 |
-
# ํ์๋จ๊ตฌ (๊ณ๋จ)
|
| 878 |
-
terrace_heights = [valley_depth * (0.3 + 0.2 * i) for i in range(num_terraces)]
|
| 879 |
|
| 880 |
-
|
| 881 |
-
|
| 882 |
-
|
| 883 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 884 |
|
| 885 |
-
for
|
| 886 |
-
|
| 887 |
-
|
| 888 |
-
|
| 889 |
-
|
| 890 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 891 |
return elevation
|
| 892 |
|
| 893 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 894 |
def create_free_meander(grid_size: int = 100, stage: float = 1.0,
|
| 895 |
-
num_bends: int = 4) -> np.ndarray:
|
| 896 |
"""
|
| 897 |
์์ ๊ณก๋ฅ (Free Meander) + ๋ฒ๋์ (Floodplain) + ์์ฐ์ ๋ฐฉ (Natural Levee)
|
| 898 |
|
| 899 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 900 |
"""
|
| 901 |
h, w = grid_size, grid_size
|
| 902 |
elevation = np.zeros((h, w))
|
| 903 |
|
| 904 |
# ๋ฒ๋์ ๊ธฐ๋ฐ
|
| 905 |
-
|
|
|
|
| 906 |
|
| 907 |
center_x = w // 2
|
| 908 |
-
amplitude = w * 0.3 * stage
|
| 909 |
-
wl = h / num_bends
|
| 910 |
channel_width = max(3, w // 20)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 911 |
|
| 912 |
for r in range(h):
|
| 913 |
theta = 2 * np.pi * r / wl
|
| 914 |
meander_x = center_x + amplitude * np.sin(theta)
|
| 915 |
|
|
|
|
|
|
|
|
|
|
| 916 |
for c in range(w):
|
| 917 |
-
dist =
|
|
|
|
| 918 |
|
| 919 |
-
if
|
| 920 |
-
# ํ๋
|
| 921 |
-
|
| 922 |
-
|
| 923 |
-
|
| 924 |
-
|
| 925 |
-
|
| 926 |
-
|
| 927 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 928 |
|
| 929 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 930 |
if stage > 0.7:
|
|
|
|
| 931 |
oxbow_y = h // 2
|
|
|
|
|
|
|
| 932 |
for dy in range(-int(wl/4), int(wl/4)):
|
| 933 |
r = oxbow_y + dy
|
| 934 |
if 0 <= r < h:
|
| 935 |
theta = 2 * np.pi * dy / (wl/2)
|
| 936 |
-
ox_x = center_x +
|
| 937 |
-
|
|
|
|
| 938 |
c = int(ox_x + dc)
|
| 939 |
if 0 <= c < w:
|
| 940 |
-
|
| 941 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 942 |
return elevation
|
| 943 |
|
| 944 |
|
| 945 |
-
def
|
| 946 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 947 |
h, w = grid_size, grid_size
|
| 948 |
elevation = np.zeros((h, w))
|
| 949 |
elevation[:, :] = -5.0 # ๋ฐ๋ค
|
| 950 |
|
| 951 |
-
apex_y = int(h * 0.
|
| 952 |
center_x = w // 2
|
| 953 |
|
| 954 |
-
#
|
| 955 |
-
|
| 956 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 957 |
|
| 958 |
for i in range(num_fingers):
|
| 959 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 960 |
|
| 961 |
for d in range(max_length):
|
| 962 |
r = apex_y + int(d * np.cos(angle))
|
| 963 |
c = center_x + int(d * np.sin(angle))
|
| 964 |
|
| 965 |
if 0 <= r < h and 0 <= c < w:
|
| 966 |
-
|
| 967 |
-
|
|
|
|
|
|
|
| 968 |
for dr in range(-2, 3):
|
| 969 |
nr, nc = r + dr, c + dc
|
| 970 |
if 0 <= nr < h and 0 <= nc < w:
|
| 971 |
dist = np.sqrt(dr**2 + dc**2)
|
| 972 |
-
z = 8.0 * (1 - d / max_length) * (1 - dist / 4) * stage
|
| 973 |
-
elevation[nr, nc] = max(elevation[nr, nc], z)
|
| 974 |
|
| 975 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 976 |
for r in range(apex_y):
|
| 977 |
-
for dc in range(-
|
| 978 |
if 0 <= center_x + dc < w:
|
| 979 |
-
|
| 980 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 981 |
return elevation
|
| 982 |
|
| 983 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 984 |
def create_arcuate_delta(grid_size: int = 100, stage: float = 1.0) -> np.ndarray:
|
| 985 |
"""ํธ์ ์ผ๊ฐ์ฃผ (Arcuate Delta) - ๋์ผ๊ฐํ"""
|
| 986 |
h, w = grid_size, grid_size
|
|
@@ -1050,18 +1374,49 @@ def create_cuspate_delta(grid_size: int = 100, stage: float = 1.0) -> np.ndarray
|
|
| 1050 |
|
| 1051 |
|
| 1052 |
def create_cirque(grid_size: int = 100, stage: float = 1.0,
|
| 1053 |
-
depth: float = 50.0) -> np.ndarray:
|
| 1054 |
-
"""๊ถ๊ณก (Cirque) - ๋นํ ์์์
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1055 |
h, w = grid_size, grid_size
|
| 1056 |
elevation = np.zeros((h, w))
|
| 1057 |
|
| 1058 |
# ์ฐ์
๋ฐฐ๊ฒฝ
|
| 1059 |
-
|
|
|
|
| 1060 |
|
| 1061 |
# ๊ถ๊ณก ์์น (์๋จ ์ค์)
|
| 1062 |
-
cirque_y = int(h * 0.
|
| 1063 |
cirque_x = w // 2
|
| 1064 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1065 |
|
| 1066 |
for r in range(h):
|
| 1067 |
for c in range(w):
|
|
@@ -1070,53 +1425,185 @@ def create_cirque(grid_size: int = 100, stage: float = 1.0,
|
|
| 1070 |
dist = np.sqrt(dy**2 + dx**2)
|
| 1071 |
|
| 1072 |
if dist < cirque_radius:
|
| 1073 |
-
#
|
| 1074 |
-
|
| 1075 |
-
|
| 1076 |
-
|
| 1077 |
-
|
| 1078 |
-
|
| 1079 |
-
|
| 1080 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1081 |
return elevation
|
| 1082 |
|
| 1083 |
|
| 1084 |
-
def
|
| 1085 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1086 |
h, w = grid_size, grid_size
|
| 1087 |
elevation = np.zeros((h, w))
|
| 1088 |
|
| 1089 |
center = (h // 2, w // 2)
|
| 1090 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1091 |
|
| 1092 |
-
|
| 1093 |
-
num_cirques = 4
|
| 1094 |
-
cirque_radius = int(w * 0.3)
|
| 1095 |
|
|
|
|
| 1096 |
for r in range(h):
|
| 1097 |
for c in range(w):
|
| 1098 |
dy = r - center[0]
|
| 1099 |
dx = c - center[1]
|
| 1100 |
dist = np.sqrt(dy**2 + dx**2)
|
| 1101 |
|
| 1102 |
-
# ๊ธฐ๋ณธ
|
| 1103 |
-
elevation[r, c] = peak_height * max(0, 1 - dist / (w
|
| 1104 |
-
|
| 1105 |
-
|
| 1106 |
-
|
| 1107 |
-
|
| 1108 |
-
|
| 1109 |
-
|
| 1110 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1111 |
cdist = np.sqrt((r - cy)**2 + (c - cx)**2)
|
|
|
|
| 1112 |
if cdist < cirque_radius * 0.6:
|
| 1113 |
-
# ๊ถ๊ณก ํ๊ธฐ
|
| 1114 |
-
|
| 1115 |
-
20.0 + 30.0 * (cdist / (cirque_radius * 0.6)))
|
| 1116 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1117 |
return elevation
|
| 1118 |
|
| 1119 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1120 |
def create_shield_volcano(grid_size: int = 100, stage: float = 1.0,
|
| 1121 |
max_height: float = 40.0) -> np.ndarray:
|
| 1122 |
"""์์ํ์ฐ (Shield Volcano) - ์๋งํ ๊ฒฝ์ฌ"""
|
|
@@ -1272,15 +1759,29 @@ def create_spit_lagoon(grid_size: int = 100, stage: float = 1.0) -> np.ndarray:
|
|
| 1272 |
# ์ถ๊ฐ ์งํ (Additional Landforms)
|
| 1273 |
# ============================================
|
| 1274 |
|
| 1275 |
-
def create_fjord(grid_size: int = 100, stage: float = 1.0
|
|
|
|
| 1276 |
"""ํผ์ค๋ฅด๋ (Fjord) - ๋นํ ํํด ํ ๋ฐ๋ค ์ ์
|
| 1277 |
|
| 1278 |
Stage 0.0~0.4: ๋นํ๊ฐ U์๊ณก์ ์ฑ์ (๋นํ๊ธฐ)
|
| 1279 |
-
|
| 1280 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1281 |
"""
|
| 1282 |
h, w = grid_size, grid_size
|
| 1283 |
elevation = np.zeros((h, w))
|
|
|
|
|
|
|
| 1284 |
|
| 1285 |
# ์ฐ์
์งํ (๋์ ์ฐ)
|
| 1286 |
elevation[:, :] = 100.0
|
|
@@ -1289,72 +1790,103 @@ def create_fjord(grid_size: int = 100, stage: float = 1.0) -> np.ndarray:
|
|
| 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 |
-
|
| 1300 |
-
|
|
|
|
| 1301 |
elif dx < valley_width + 15:
|
| 1302 |
-
# U์ ์ธก๋ฒฝ (
|
| 1303 |
t = (dx - valley_width) / 15
|
| 1304 |
-
|
|
|
|
| 1305 |
|
| 1306 |
# ๋นํ / ๋ฐ๋ค ์ํ
|
| 1307 |
if stage < 0.4:
|
| 1308 |
# ๋นํ๊ธฐ: U์๊ณก์ ๋นํ ์ฑ์
|
| 1309 |
-
glacier_extent = int(h * 0.
|
| 1310 |
-
glacier_thickness =
|
|
|
|
|
|
|
| 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 |
-
|
| 1319 |
|
| 1320 |
elif stage < 0.7:
|
| 1321 |
-
# ๋นํ ํํด ์ค
|
| 1322 |
retreat_factor = (stage - 0.4) / 0.3
|
| 1323 |
|
| 1324 |
# ๋นํ ์๋ฅ (์๋ฅ์๋ง)
|
| 1325 |
-
glacier_end = int(h * (0.
|
| 1326 |
-
glacier_thickness =
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 1334 |
|
| 1335 |
# ๋ฐ๋ค ์ ์
(ํ๋ฅ๋ถํฐ)
|
| 1336 |
-
|
| 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 |
-
|
| 1343 |
else:
|
| 1344 |
-
# ํผ์ค๋ฅด๋ ์์ฑ
|
| 1345 |
-
|
| 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 |
-
|
| 1353 |
-
|
| 1354 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1355 |
return elevation
|
| 1356 |
|
| 1357 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1358 |
def create_drumlin(grid_size: int = 100, stage: float = 1.0,
|
| 1359 |
num_drumlins: int = 5) -> np.ndarray:
|
| 1360 |
"""๋๋ผ๋ฆฐ (Drumlin) - ๋นํ ๋ฐฉํฅ ํ์ํ ์ธ๋"""
|
|
@@ -1475,83 +2007,121 @@ def create_braided_river(grid_size: int = 100, stage: float = 1.0) -> np.ndarray
|
|
| 1475 |
|
| 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
|
| 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 |
-
|
| 1493 |
-
|
| 1494 |
-
|
| 1495 |
-
fall_r = int(initial_fall -
|
| 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 =
|
|
|
|
| 1524 |
|
| 1525 |
for r in range(gorge_start, min(gorge_end, h)):
|
| 1526 |
-
for dc in range(-
|
| 1527 |
c = center + dc
|
| 1528 |
if 0 <= c < w:
|
| 1529 |
# V์ ํ๊ณก ๋จ๋ฉด
|
| 1530 |
-
depth = gorge_depth * (1 - abs(dc) /
|
| 1531 |
elevation[r, c] -= depth
|
| 1532 |
|
| 1533 |
# ํ์ฒ ์๋ก
|
|
|
|
| 1534 |
for r in range(h):
|
| 1535 |
-
for dc in range(-
|
| 1536 |
c = center + dc
|
| 1537 |
if 0 <= c < w:
|
| 1538 |
-
|
| 1539 |
-
|
| 1540 |
-
|
| 1541 |
-
|
| 1542 |
-
|
| 1543 |
-
|
| 1544 |
-
|
| 1545 |
-
|
| 1546 |
-
|
|
|
|
|
|
|
|
|
|
| 1547 |
dist = np.sqrt(dr**2 + dc**2)
|
| 1548 |
-
if dist <
|
| 1549 |
-
pool_effect = pool_depth * (1 - dist /
|
| 1550 |
-
elevation[
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1551 |
|
| 1552 |
return elevation
|
| 1553 |
|
| 1554 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1555 |
def create_karst_doline(grid_size: int = 100, stage: float = 1.0,
|
| 1556 |
num_dolines: int = 5) -> np.ndarray:
|
| 1557 |
"""๋๋ฆฌ๋ค (Doline/Sinkhole) - ์นด๋ฅด์คํธ ์งํ"""
|
|
|
|
| 332 |
return elevation
|
| 333 |
|
| 334 |
|
| 335 |
+
def create_coastal_cliff(grid_size: int = 100, stage: float = 1.0,
|
| 336 |
cliff_height: float = 30.0,
|
| 337 |
+
num_stacks: int = 2,
|
| 338 |
+
return_metadata: bool = False) -> np.ndarray:
|
| 339 |
"""
|
| 340 |
+
ํด์ ์ ๋ฒฝ (Coastal Cliff) + ํ์๋ + ์์คํ
|
| 341 |
|
| 342 |
+
Stage 0~0.3: ์ด๊ธฐ ํด์ (์ ๋ฒฝ ํ์ฑ ์์)
|
| 343 |
+
- ํ๋์ ์์์์ฉ(hydraulic action)
|
| 344 |
+
- ๋
ธ์น(notch) ํ์ฑ ์์
|
| 345 |
+
|
| 346 |
+
Stage 0.3~0.6: ์ ๋ฒฝ ๋ฐ๋ฌ
|
| 347 |
+
- ์ฐ๋ง์์ฉ(abrasion)์ผ๋ก ๋
ธ์น ํ๋
|
| 348 |
+
- ์ค๋ฒํ(overhang) ํ์ฑ
|
| 349 |
+
- ์ ๋ฒฝ ๋ถ๊ดด ์์
|
| 350 |
+
|
| 351 |
+
Stage 0.6~0.8: ์ ๋ฒฝ ํํด
|
| 352 |
+
- ๋ฐ๋ณต์ ๋ถ๊ดด๋ก ์ ๋ฒฝ์ด ์ก์ง์ชฝ์ผ๋ก ํํด
|
| 353 |
+
- ํ์๋(wave-cut platform) ํ์ฅ
|
| 354 |
+
|
| 355 |
+
Stage 0.8~1.0: ์์คํ/ํด์๋ ํ์ฑ
|
| 356 |
+
- ์ฐ์ฝ๋ถ ์ฐจ๋ณ์นจ์
|
| 357 |
+
- ํด์์์น โ ์์คํ ํ์ฑ
|
| 358 |
+
|
| 359 |
+
ํต์ฌ ๊ณผ์ :
|
| 360 |
+
- ์์์์ฉ: ํ๋ ์ถฉ๊ฒฉ โ ์์ ํ์ ์์ถ๊ณต๊ธฐ
|
| 361 |
+
- ์ฐ๋ง์์ฉ: ํด๋น ์๊ฐ/๋ชจ๋๊ฐ ์ ๋ฒฝ ๊น์
|
| 362 |
+
- ์ฉ์์์ฉ: ํด์์ ํํ์ ์ฉํด (์ํ์)
|
| 363 |
"""
|
| 364 |
h, w = grid_size, grid_size
|
| 365 |
elevation = np.zeros((h, w))
|
| 366 |
|
| 367 |
+
# ํด์์ ์์น (stage์ ๋ฐ๋ผ ์ก์ง์ชฝ์ผ๋ก ํํด)
|
| 368 |
+
initial_sea_line = int(h * 0.7)
|
| 369 |
+
retreat_amount = int(h * 0.2 * stage)
|
| 370 |
+
sea_line = initial_sea_line - retreat_amount
|
| 371 |
+
|
| 372 |
# ๋ฐ๋ค (ํ๋จ)
|
| 373 |
+
for r in range(sea_line, h):
|
| 374 |
+
elevation[r, :] = -5.0
|
| 375 |
+
|
| 376 |
+
# ์ ๋ฒฝ ๋์ด (stage์ ๋ฐ๋ผ ๋ฐ๋ฌ)
|
| 377 |
+
current_cliff_height = cliff_height * (0.5 + 0.5 * stage)
|
| 378 |
|
| 379 |
# ์ก์ง + ์ ๋ฒฝ
|
| 380 |
+
cliff_width = max(3, int(5 * stage))
|
| 381 |
for r in range(sea_line):
|
| 382 |
cliff_dist = sea_line - r
|
| 383 |
+
if cliff_dist < cliff_width:
|
| 384 |
+
# ์ ๋ฒฝ๋ฉด (์์ง์ ๊ฐ๊น์)
|
| 385 |
+
t = cliff_dist / cliff_width
|
| 386 |
+
elevation[r, :] = current_cliff_height * (t ** 0.7) # ์ค๋ชฉํ ํ๋กํ์ผ
|
| 387 |
else:
|
| 388 |
# ํํํ ์ก์ง
|
| 389 |
+
elevation[r, :] = current_cliff_height
|
| 390 |
+
|
| 391 |
+
# ๋
ธ์น (Notch) - stage > 0.3์์ ํ์ฑ
|
| 392 |
+
if stage > 0.3:
|
| 393 |
+
notch_depth = int(3 * (stage - 0.3) / 0.7)
|
| 394 |
+
notch_height = 2 # ํ๋๋ ๋์ด
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 395 |
|
| 396 |
+
for r in range(sea_line - notch_height, sea_line):
|
| 397 |
+
for c in range(w):
|
| 398 |
+
if 0 <= r < h:
|
| 399 |
+
# ๋
ธ์น ๊น์ด๋งํผ ํ์
|
| 400 |
+
elevation[r, c] = min(elevation[r, c],
|
| 401 |
+
elevation[r, c] - notch_depth * (1 - abs(r - (sea_line - 1)) / notch_height))
|
| 402 |
+
|
| 403 |
+
# ํ์๋ (Wave-cut Platform) - stage > 0.4์์ ํ์ฅ
|
| 404 |
+
platform_width = int(10 + 15 * max(0, (stage - 0.4) / 0.6))
|
| 405 |
+
for r in range(sea_line, min(sea_line + platform_width, h)):
|
| 406 |
+
platform_depth = -1.0 - (r - sea_line) * 0.3
|
| 407 |
+
elevation[r, :] = max(platform_depth, -5.0)
|
| 408 |
+
|
| 409 |
+
# ์์คํ (Sea Stacks) - stage > 0.7์์ ํ์ฑ
|
| 410 |
+
stacks_formed = []
|
| 411 |
+
if stage > 0.7:
|
| 412 |
+
stack_progress = (stage - 0.7) / 0.3
|
| 413 |
+
visible_stacks = int(num_stacks * stack_progress) + 1
|
| 414 |
|
| 415 |
+
for i in range(min(visible_stacks, num_stacks)):
|
| 416 |
+
sx = w // 4 + i * (w // 2)
|
| 417 |
+
sy = sea_line + 8 + i * 4
|
| 418 |
+
|
| 419 |
+
stack_height = current_cliff_height * 0.6 * stack_progress
|
| 420 |
+
stack_radius = 4
|
| 421 |
+
|
| 422 |
+
for dr in range(-stack_radius, stack_radius + 1):
|
| 423 |
+
for dc in range(-stack_radius, stack_radius + 1):
|
| 424 |
+
r_pos, c_pos = sy + dr, sx + dc
|
| 425 |
+
if 0 <= r_pos < h and 0 <= c_pos < w:
|
| 426 |
+
dist = np.sqrt(dr**2 + dc**2)
|
| 427 |
+
if dist < stack_radius:
|
| 428 |
+
z = stack_height * (1 - (dist / stack_radius) ** 2)
|
| 429 |
+
elevation[r_pos, c_pos] = max(elevation[r_pos, c_pos], z)
|
| 430 |
+
|
| 431 |
+
stacks_formed.append((sy, sx))
|
| 432 |
+
|
| 433 |
+
if return_metadata:
|
| 434 |
+
return elevation, {
|
| 435 |
+
'sea_line': sea_line,
|
| 436 |
+
'cliff_height': current_cliff_height,
|
| 437 |
+
'retreat_amount': retreat_amount,
|
| 438 |
+
'platform_width': platform_width,
|
| 439 |
+
'stacks_formed': stacks_formed,
|
| 440 |
+
'erosion_processes': {
|
| 441 |
+
'hydraulic_action': 'ํ๋ ์ถฉ๊ฒฉ โ ์์ ํ์ ์์ถ๊ณต๊ธฐ',
|
| 442 |
+
'abrasion': 'ํด๋น ์๊ฐ/๋ชจ๋๊ฐ ์ ๋ฒฝ ์ฐ๋ง',
|
| 443 |
+
'corrosion': 'ํด์์ ํํ์ ์ฉํด (์ํ์)'
|
| 444 |
+
},
|
| 445 |
+
'stage_description': _get_cliff_stage_desc(stage)
|
| 446 |
+
}
|
| 447 |
+
|
| 448 |
return elevation
|
| 449 |
|
| 450 |
|
| 451 |
+
def _get_cliff_stage_desc(stage: float) -> str:
|
| 452 |
+
"""ํด์์ ๋ฒฝ ๋จ๊ณ๋ณ ์ค๋ช
"""
|
| 453 |
+
if stage < 0.2:
|
| 454 |
+
return "๐ ์ด๊ธฐ ํด์: ํ๋ ์นจ์ ์์"
|
| 455 |
+
elif stage < 0.4:
|
| 456 |
+
return "โ๏ธ ๋
ธ์น ํ์ฑ: ์์์์ฉ์ผ๋ก ํ๋๋ ์นจ์"
|
| 457 |
+
elif stage < 0.6:
|
| 458 |
+
return "๐๏ธ ์ ๋ฒฝ ๋ฐ๋ฌ: ์ค๋ฒํ ํ์ฑ โ ๋ถ๊ดด"
|
| 459 |
+
elif stage < 0.8:
|
| 460 |
+
return "๐ ์ ๋ฒฝ ํํด: ํ์๋ ๋
ธ์ถ"
|
| 461 |
+
else:
|
| 462 |
+
return "๐ชจ ์์คํ ํ์ฑ: ์ฐจ๋ณ์นจ์์ผ๋ก ๊ณ ๋ฆฝ ์์"
|
| 463 |
+
|
| 464 |
+
|
| 465 |
# ============================================
|
| 466 |
# ์ ๋๋ฉ์ด์
์ฉ ํ์ฑ๊ณผ์ ํจ์ (Stage-based)
|
| 467 |
# stage: 0.0 (์์) ~ 1.0 (์์ฑ)
|
|
|
|
| 522 |
|
| 523 |
|
| 524 |
def create_alluvial_fan_animated(grid_size: int, stage: float,
|
| 525 |
+
cone_angle: float = 90.0, max_height: float = 50.0,
|
| 526 |
+
return_metadata: bool = False) -> np.ndarray:
|
| 527 |
+
"""์ ์์ง ํ์ฑ๊ณผ์ ์ ๋๋ฉ์ด์
|
| 528 |
+
|
| 529 |
+
Stage 0~0.3: ์ ์ (Apex) ํ์ฑ - ํ๊ณก ์ถ๊ตฌ, ์ญ ํด์
|
| 530 |
+
Stage 0.3~0.7: ์ ์(Mid-fan) ํ์ฅ - ๋ถ๊ธฐ ์๋ก, ์ฌ์ง ํด์
|
| 531 |
+
Stage 0.7~1.0: ์ ๋จ(Toe) ์์ฑ - ๋ง๋จ๋ถ, ๋์ง ํด์
|
| 532 |
+
|
| 533 |
+
์ธ๋ถ ๊ตฌ์กฐ:
|
| 534 |
+
- ์ ์ : ๊ฒฝ์ฌ 5-15ยฐ, ์ญ(Gravel) ํด์ , ๋จ์ผ ์ฃผ์๋ก
|
| 535 |
+
- ์ ์: ๊ฒฝ์ฌ 2-5ยฐ, ์ฌ(Sand) ํด์ , ๋ถ๊ธฐ ์๋ก
|
| 536 |
+
- ์ ๋จ: ๊ฒฝ์ฌ <2ยฐ, ๋(Silt) ํด์ , ๋ง์/์์ ์๋ก
|
| 537 |
+
"""
|
| 538 |
h, w = grid_size, grid_size
|
| 539 |
elevation = np.zeros((h, w))
|
| 540 |
+
zone_mask = np.zeros((h, w), dtype=int) # 0: ์์, 1: ์ ์ , 2: ์ ์, 3: ์ ๋จ
|
| 541 |
|
| 542 |
apex_y = int(h * 0.15)
|
| 543 |
center_x = w // 2
|
|
|
|
| 557 |
max_reach = int((h - apex_y) * stage)
|
| 558 |
half_angle = np.radians(cone_angle / 2) * (0.5 + 0.5 * stage)
|
| 559 |
|
| 560 |
+
# ์กด ๊ฒฝ๊ณ ๊ณ์ฐ
|
| 561 |
+
apex_end = apex_y + max(1, int(max_reach * 0.2)) # ์ ์ : 0~20%
|
| 562 |
+
mid_end = apex_y + max(1, int(max_reach * 0.6)) # ์ ์: 20~60%
|
| 563 |
+
# ์ ๋จ: 60~100%
|
| 564 |
+
|
| 565 |
+
for r in range(apex_y, min(apex_y + max_reach, h)):
|
| 566 |
dist = r - apex_y
|
| 567 |
+
|
| 568 |
+
# ์กด ๊ฒฐ์
|
| 569 |
+
if r < apex_end:
|
| 570 |
+
current_zone = 1 # ์ ์
|
| 571 |
+
elif r < mid_end:
|
| 572 |
+
current_zone = 2 # ์ ์
|
| 573 |
+
else:
|
| 574 |
+
current_zone = 3 # ์ ๋จ
|
| 575 |
+
|
| 576 |
for c in range(w):
|
| 577 |
dx = c - center_x
|
| 578 |
if abs(np.arctan2(dx, max(dist, 1))) < half_angle:
|
| 579 |
radial = np.sqrt(dx**2 + dist**2)
|
| 580 |
+
z = max_height * (1 - radial / (max_reach * 1.5 + 0.001)) * stage
|
| 581 |
lateral_decay = 1 - abs(dx) / (w // 2)
|
| 582 |
+
new_elevation = max(0, z * lateral_decay)
|
| 583 |
|
| 584 |
+
if new_elevation > 0:
|
| 585 |
+
elevation[r, c] = new_elevation
|
| 586 |
+
zone_mask[r, c] = current_zone
|
| 587 |
+
|
| 588 |
+
if return_metadata:
|
| 589 |
+
return elevation, {
|
| 590 |
+
'zone_mask': zone_mask,
|
| 591 |
+
'apex_boundary': apex_end,
|
| 592 |
+
'mid_boundary': mid_end,
|
| 593 |
+
'stage_description': _get_fan_stage_desc(stage),
|
| 594 |
+
'zone_info': {
|
| 595 |
+
1: {'name': '์ ์ (Apex)', 'slope': '5-15ยฐ', 'sediment': '์ญ (Gravel)'},
|
| 596 |
+
2: {'name': '์ ์ (Mid-fan)', 'slope': '2-5ยฐ', 'sediment': '์ฌ (Sand)'},
|
| 597 |
+
3: {'name': '์ ๋จ (Toe)', 'slope': '<2ยฐ', 'sediment': '๋ (Silt)'}
|
| 598 |
+
}
|
| 599 |
+
}
|
| 600 |
+
|
| 601 |
return elevation
|
| 602 |
|
| 603 |
|
| 604 |
+
def _get_fan_stage_desc(stage: float) -> str:
|
| 605 |
+
"""์ ์์ง ๋จ๊ณ๋ณ ์ค๋ช
"""
|
| 606 |
+
if stage < 0.3:
|
| 607 |
+
return "๐๏ธ ์ ์ ํ์ฑ: ํ๊ณก ์ถ๊ตฌ์์ ์ ์ ๊ธ๊ฐ, ์ญ ํด์ ์์"
|
| 608 |
+
elif stage < 0.6:
|
| 609 |
+
return "๐ ์ ์ ํ์ฅ: ์๋ก ๋ถ๊ธฐ, ์ฌ์ง ํด์ ๋ฌผ ํ์ฐ"
|
| 610 |
+
elif stage < 0.8:
|
| 611 |
+
return "๐ ์ ๋จ ๋ฐ๋ฌ: ์ธ๋ฆฝ์ง ํด์ , ๋ง๋จ๋ถ ์๋งํด์ง"
|
| 612 |
+
else:
|
| 613 |
+
return "โ
์ ์์ง ์์ฑ: ์ ์ -์ ์-์ ๋จ ๋ถํ ์๋ฃ"
|
| 614 |
+
|
| 615 |
+
|
| 616 |
def create_meander_animated(grid_size: int, stage: float,
|
| 617 |
amplitude: float = 0.3, num_bends: int = 3) -> np.ndarray:
|
| 618 |
"""๊ณก๋ฅ ํ์ฑ๊ณผ์ ์ ๋๋ฉ์ด์
(์ง์ โ ์ฌํ โ ์ฐ๊ฐํธ โ ํ์ค๋)
|
|
|
|
| 972 |
# ============================================
|
| 973 |
|
| 974 |
def create_incised_meander(grid_size: int = 100, stage: float = 1.0,
|
| 975 |
+
valley_depth: float = 80.0, num_terraces: int = 3,
|
| 976 |
+
return_metadata: bool = False) -> np.ndarray:
|
| 977 |
"""
|
| 978 |
๊ฐ์
๊ณก๋ฅ (Incised Meander) + ํ์๋จ๊ตฌ (River Terraces)
|
| 979 |
|
| 980 |
+
Stage 0~0.3: ์์ ๊ณก๋ฅ (๋ฒ๋์ ์, ์นจ์๊ธฐ์ค๋ฉด ๋์)
|
| 981 |
+
Stage 0.3~0.7: ์ต๊ธฐ ์์ โ ํ๋ฐฉ์นจ์ ๊ฐํ
|
| 982 |
+
Stage 0.7~1.0: ๊น์ ํ๊ณก + ํ์๋จ๊ตฌ ๋
ธ์ถ
|
| 983 |
+
|
| 984 |
์ต๊ธฐ ํ๊ฒฝ์์ ๊ณก๋ฅ๊ฐ ์๋ฐ์ ํ๊ณ ๋ค์ด๊ฐ๋ฉด์ ํ์ฑ
|
| 985 |
"""
|
| 986 |
h, w = grid_size, grid_size
|
| 987 |
elevation = np.zeros((h, w))
|
| 988 |
|
| 989 |
center_x = w // 2
|
| 990 |
+
wl = h / 3 # 3๊ฐ ๊ตฝ์ด
|
|
|
|
| 991 |
channel_width = max(3, w // 25)
|
| 992 |
|
| 993 |
+
# ์นจ์๊ธฐ์ค๋ฉด (stage์ ๋ฐ๋ผ ํ๊ฐ)
|
| 994 |
+
# Stage 0: ๊ธฐ์ค๋ฉด ๋์ (์์ ๊ณก๋ฅ)
|
| 995 |
+
# Stage 1: ๊ธฐ์ค๋ฉด ๋ฎ์ (๊ฐ์
)
|
| 996 |
+
base_level = valley_depth * (1 - stage * 0.9) # 90% ํ๊ฐ
|
| 997 |
+
|
| 998 |
+
# ๊ณก๋ฅ ์งํญ (stage์ ๋ฐ๋ผ ๊ณ ์ ํ)
|
| 999 |
+
if stage < 0.3:
|
| 1000 |
+
amplitude = w * 0.25 * (stage / 0.3) # ์ฌํ ๋ฐ๋ฌ
|
| 1001 |
+
else:
|
| 1002 |
+
amplitude = w * 0.25 # ๊ณก๋ฅ ํจํด ๊ณ ์
|
| 1003 |
+
|
| 1004 |
+
# ๊ธฐ๋ฐ ๊ณ ์ ๋์ด
|
| 1005 |
+
plateau_height = valley_depth
|
| 1006 |
+
elevation[:, :] = plateau_height
|
| 1007 |
+
|
| 1008 |
+
# ํ์ฌ ์นจ์ ๊น์ด (stage์ ๋ฐ๋ผ ์ฆ๊ฐ)
|
| 1009 |
+
current_depth = (plateau_height - base_level) * min(1.0, (stage - 0.2) / 0.8) if stage > 0.2 else 0
|
| 1010 |
|
| 1011 |
# ๊ฐ์
๊ณก๋ฅ ํ๊ธฐ
|
| 1012 |
for r in range(h):
|
|
|
|
| 1016 |
for c in range(w):
|
| 1017 |
dist = abs(c - meander_x)
|
| 1018 |
|
| 1019 |
+
# ํ๋ ๋ฐ๋ฅ (์นจ์๊ธฐ์ค๋ฉด๊น์ง)
|
| 1020 |
+
river_bottom = plateau_height - current_depth
|
| 1021 |
+
|
| 1022 |
if dist < channel_width:
|
| 1023 |
# ํ๋ (๊ฐ์ฅ ๊น์)
|
| 1024 |
+
elevation[r, c] = max(base_level, river_bottom)
|
| 1025 |
+
elif dist < channel_width * 3:
|
| 1026 |
+
# ํ๊ณก ์ธก๋ฒฝ (V์ํ)
|
| 1027 |
+
t = (dist - channel_width) / (channel_width * 2)
|
| 1028 |
+
elevation[r, c] = river_bottom + current_depth * t
|
|
|
|
|
|
|
|
|
|
| 1029 |
|
| 1030 |
+
# ํ์๋จ๊ตฌ (stage > 0.5์์ ํ์ฑ)
|
| 1031 |
+
if stage > 0.5:
|
| 1032 |
+
terrace_progress = (stage - 0.5) / 0.5
|
| 1033 |
+
num_visible_terraces = int(num_terraces * terrace_progress) + 1
|
| 1034 |
+
|
| 1035 |
+
for t_idx in range(min(num_visible_terraces, num_terraces)):
|
| 1036 |
+
terrace_height = plateau_height - current_depth * (0.3 + 0.25 * t_idx)
|
| 1037 |
+
terrace_width_start = channel_width * (3 + t_idx)
|
| 1038 |
+
terrace_width_end = channel_width * (4 + t_idx)
|
| 1039 |
|
| 1040 |
+
for r in range(h):
|
| 1041 |
+
theta = 2 * np.pi * r / wl
|
| 1042 |
+
meander_x = center_x + amplitude * np.sin(theta) * (0.9 - 0.1 * t_idx)
|
| 1043 |
+
|
| 1044 |
+
for c in range(w):
|
| 1045 |
+
dist = abs(c - meander_x)
|
| 1046 |
+
if terrace_width_start < dist < terrace_width_end:
|
| 1047 |
+
if elevation[r, c] > terrace_height:
|
| 1048 |
+
elevation[r, c] = terrace_height
|
| 1049 |
+
|
| 1050 |
+
if return_metadata:
|
| 1051 |
+
return elevation, {
|
| 1052 |
+
'base_level': base_level,
|
| 1053 |
+
'current_depth': current_depth,
|
| 1054 |
+
'stage_description': _get_incised_stage_desc(stage)
|
| 1055 |
+
}
|
| 1056 |
+
|
| 1057 |
return elevation
|
| 1058 |
|
| 1059 |
|
| 1060 |
+
def _get_incised_stage_desc(stage: float) -> str:
|
| 1061 |
+
"""๊ฐ์
๊ณก๋ฅ ๋จ๊ณ๋ณ ์ค๋ช
"""
|
| 1062 |
+
if stage < 0.3:
|
| 1063 |
+
return "๐ ์์ ๊ณก๋ฅ ๋จ๊ณ: ๋ฒ๋์ ์๋ฅผ ์์ ๋กญ๊ฒ ์ฌํ"
|
| 1064 |
+
elif stage < 0.5:
|
| 1065 |
+
return "โฌ๏ธ ์ต๊ธฐ ์์: ์นจ์๊ธฐ์ค๋ฉด ํ๊ฐ, ํ๋ฐฉ์นจ์ ์์"
|
| 1066 |
+
elif stage < 0.7:
|
| 1067 |
+
return "โ๏ธ ๊ฐ์
์งํ: ๊ณก๋ฅ ํจํด ๊ณ ์ , ํ๊ณก ๊น์ด์ง"
|
| 1068 |
+
else:
|
| 1069 |
+
return "๐๏ธ ๊ฐ์
๊ณก๋ฅ ์์ฑ: ํ์๋จ๊ตฌ ํ์ฑ, ๊ณผ๊ฑฐ ํ์ ๋
ธ์ถ"
|
| 1070 |
+
|
| 1071 |
+
|
| 1072 |
def create_free_meander(grid_size: int = 100, stage: float = 1.0,
|
| 1073 |
+
num_bends: int = 4, return_metadata: bool = False) -> np.ndarray:
|
| 1074 |
"""
|
| 1075 |
์์ ๊ณก๋ฅ (Free Meander) + ๋ฒ๋์ (Floodplain) + ์์ฐ์ ๋ฐฉ (Natural Levee)
|
| 1076 |
|
| 1077 |
+
Stage 0~0.2: ์ง์ ํ์ฒ (์ด๊ธฐ ํ๋)
|
| 1078 |
+
Stage 0.2~0.5: ์ฌํ ๋ฐ๋ฌ (ํฌ๋ฆฌ์ปฌ ํ๋ฆ์ ์ํ ๊ณต๊ฒฉ์ฌ๋ฉด ์นจ์)
|
| 1079 |
+
Stage 0.5~0.7: ๊ณก๋ฅ ์งํญ ์ฆ๊ฐ (์ฌํ๋ > 1.5)
|
| 1080 |
+
Stage 0.7~0.9: ๊ณก๋ฅ ๋ชฉ ์ ๋จ (Neck Cutoff) โ ์ฐ๊ฐํธ ํ์ฑ
|
| 1081 |
+
Stage 0.9~1.0: ์์ฐ์ ๋ฐฉ ์์ฑ + ๋ฐฐํ์ต์ง ๋ถํ
|
| 1082 |
+
|
| 1083 |
+
ํฌ๋ฆฌ์ปฌ ํ๋ฆ (Helical Flow):
|
| 1084 |
+
- ๊ณก๋ฅ๋ถ ์ธ์ธก: ์์ฌ๋ ฅ โ ์๋ฉด ์์น โ ๋ฐ๋ฅ์์ ๋ด์ธก์ผ๋ก ํก๋ฅ
|
| 1085 |
+
- ๊ณต๊ฒฉ์ฌ๋ฉด(Cut Bank): ์นจ์
|
| 1086 |
+
- ํ์ฃผ์ฌ๋ฉด(Point Bar): ํด์
|
| 1087 |
"""
|
| 1088 |
h, w = grid_size, grid_size
|
| 1089 |
elevation = np.zeros((h, w))
|
| 1090 |
|
| 1091 |
# ๋ฒ๋์ ๊ธฐ๋ฐ
|
| 1092 |
+
base_height = 10.0
|
| 1093 |
+
elevation[:, :] = base_height
|
| 1094 |
|
| 1095 |
center_x = w // 2
|
|
|
|
|
|
|
| 1096 |
channel_width = max(3, w // 20)
|
| 1097 |
+
wl = h / num_bends
|
| 1098 |
+
|
| 1099 |
+
# Stage์ ๋ฐ๋ฅธ ์ฌํ ์งํญ
|
| 1100 |
+
if stage < 0.2:
|
| 1101 |
+
amplitude = w * 0.05 # ๊ฑฐ์ ์ง์
|
| 1102 |
+
else:
|
| 1103 |
+
amplitude = w * 0.3 * min(1.0, (stage - 0.1) / 0.4)
|
| 1104 |
+
|
| 1105 |
+
# ์ฌํ๋ ๊ณ์ฐ
|
| 1106 |
+
sinuosity = 1.0 + amplitude / (h / num_bends) * 2
|
| 1107 |
+
|
| 1108 |
+
# ๊ณต๊ฒฉ์ฌ๋ฉด/ํ์ฃผ์ฌ๋ฉด ์์น ์ ์ฅ
|
| 1109 |
+
cutbank_positions = []
|
| 1110 |
+
pointbar_positions = []
|
| 1111 |
|
| 1112 |
for r in range(h):
|
| 1113 |
theta = 2 * np.pi * r / wl
|
| 1114 |
meander_x = center_x + amplitude * np.sin(theta)
|
| 1115 |
|
| 1116 |
+
# ๊ณก๋ฅ ๋ฐฉํฅ (๊ณต๊ฒฉ์ฌ๋ฉด ๊ฒฐ์ ์ฉ)
|
| 1117 |
+
curvature = np.cos(theta) # +: ์ค๋ฅธ์ชฝ ๊ณต๊ฒฉ์ฌ๋ฉด, -: ์ผ์ชฝ ๊ณต๊ฒฉ์ฌ๋ฉด
|
| 1118 |
+
|
| 1119 |
for c in range(w):
|
| 1120 |
+
dist = c - meander_x
|
| 1121 |
+
abs_dist = abs(dist)
|
| 1122 |
|
| 1123 |
+
if abs_dist < channel_width:
|
| 1124 |
+
# ํ๋ (๋น๋์นญ ๋จ๋ฉด - stage ํ๋ฐ์)
|
| 1125 |
+
if stage > 0.3:
|
| 1126 |
+
# ๊ณต๊ฒฉ์ฌ๋ฉด ์ชฝ์ ๋ ๊น์
|
| 1127 |
+
if (curvature > 0 and dist > 0) or (curvature < 0 and dist < 0):
|
| 1128 |
+
depth_factor = 1.2 # ๊ณต๊ฒฉ์ฌ๋ฉด
|
| 1129 |
+
if r % 20 == 0:
|
| 1130 |
+
cutbank_positions.append((r, c))
|
| 1131 |
+
else:
|
| 1132 |
+
depth_factor = 0.7 # ํ์ฃผ์ฌ๋ฉด
|
| 1133 |
+
if r % 20 == 0:
|
| 1134 |
+
pointbar_positions.append((r, c))
|
| 1135 |
+
else:
|
| 1136 |
+
depth_factor = 1.0
|
| 1137 |
+
elevation[r, c] = 5.0 - (channel_width - abs_dist) * 0.2 * depth_factor
|
| 1138 |
+
|
| 1139 |
+
elif abs_dist < channel_width * 2 and stage > 0.5:
|
| 1140 |
+
# ์์ฐ์ ๋ฐฉ (Levee) - stage ํ๋ฐ์ ๋ฐ๋ฌ
|
| 1141 |
+
levee_height = base_height + 1.5 * ((stage - 0.5) / 0.5)
|
| 1142 |
+
elevation[r, c] = levee_height
|
| 1143 |
|
| 1144 |
+
elif abs_dist < channel_width * 5 and stage > 0.7:
|
| 1145 |
+
# ๋ฐฐํ์ต์ง (Backswamp) - ์์ฐ์ ๋ฐฉ๋ณด๋ค ๋ฎ์
|
| 1146 |
+
elevation[r, c] = base_height - 0.5
|
| 1147 |
+
|
| 1148 |
+
# ์ฐ๊ฐํธ (Oxbow Lake) - Stage 0.7 ์ดํ
|
| 1149 |
+
oxbow_formed = False
|
| 1150 |
if stage > 0.7:
|
| 1151 |
+
oxbow_progress = (stage - 0.7) / 0.3
|
| 1152 |
oxbow_y = h // 2
|
| 1153 |
+
oxbow_amplitude = amplitude * 1.4
|
| 1154 |
+
|
| 1155 |
for dy in range(-int(wl/4), int(wl/4)):
|
| 1156 |
r = oxbow_y + dy
|
| 1157 |
if 0 <= r < h:
|
| 1158 |
theta = 2 * np.pi * dy / (wl/2)
|
| 1159 |
+
ox_x = center_x + oxbow_amplitude * np.sin(theta)
|
| 1160 |
+
|
| 1161 |
+
for dc in range(-channel_width-2, channel_width + 3):
|
| 1162 |
c = int(ox_x + dc)
|
| 1163 |
if 0 <= c < w:
|
| 1164 |
+
# ์ฐ๊ฐํธ (๊ณ ๋ฆฝ๋ ํธ์)
|
| 1165 |
+
elevation[r, c] = 4.0
|
| 1166 |
+
oxbow_formed = True
|
| 1167 |
+
|
| 1168 |
+
if return_metadata:
|
| 1169 |
+
return elevation, {
|
| 1170 |
+
'sinuosity': sinuosity,
|
| 1171 |
+
'amplitude': amplitude,
|
| 1172 |
+
'cutbank_positions': cutbank_positions[:5], # ์์ 5๊ฐ
|
| 1173 |
+
'pointbar_positions': pointbar_positions[:5],
|
| 1174 |
+
'oxbow_formed': oxbow_formed,
|
| 1175 |
+
'stage_description': _get_meander_stage_desc(stage)
|
| 1176 |
+
}
|
| 1177 |
+
|
| 1178 |
return elevation
|
| 1179 |
|
| 1180 |
|
| 1181 |
+
def _get_meander_stage_desc(stage: float) -> str:
|
| 1182 |
+
"""์์ ๊ณก๋ฅ ๋จ๊ณ๋ณ ์ค๋ช
"""
|
| 1183 |
+
if stage < 0.2:
|
| 1184 |
+
return "๐ ์ด๊ธฐ ํ๋: ๊ฑฐ์ ์ง์ ํ๋ฆ"
|
| 1185 |
+
elif stage < 0.4:
|
| 1186 |
+
return "๐ ์ฌํ ์์: ํฌ๋ฆฌ์ปฌ ํ๋ฆ์ผ๋ก ๊ณต๊ฒฉ์ฌ๋ฉด ์นจ์ ์์"
|
| 1187 |
+
elif stage < 0.6:
|
| 1188 |
+
return "๐ ๊ณก๋ฅ ๋ฐ๋ฌ: ์ฌํ๋ ์ฆ๊ฐ, ํ์ฃผ์ฌ๋ฉด ํด์ "
|
| 1189 |
+
elif stage < 0.8:
|
| 1190 |
+
return "โ๏ธ ๋ชฉ ์ ๋จ: ๊ณก๋ฅ ๋ชฉ ๊ทผ์ , ์ฐ๊ฐํธ ํ์ฑ ์์"
|
| 1191 |
+
else:
|
| 1192 |
+
return "๐๏ธ ์ฑ์ ๊ณก๋ฅ: ์์ฐ์ ๋ฐฉ + ๋ฐฐํ์ต์ง + ์ฐ๊ฐํธ ์์ฑ"
|
| 1193 |
+
|
| 1194 |
+
|
| 1195 |
+
def create_bird_foot_delta(grid_size: int = 100, stage: float = 1.0,
|
| 1196 |
+
return_metadata: bool = False) -> np.ndarray:
|
| 1197 |
+
"""์กฐ์กฑ์ ์ผ๊ฐ์ฃผ (Bird-foot Delta) - ๋ฏธ์์ํผ๊ฐํ
|
| 1198 |
+
|
| 1199 |
+
Stage 0~0.3: ์ฃผ ์๋ก ํ์ฑ
|
| 1200 |
+
- ๋จ์ผ ํ๋๊ฐ ๋ฐ๋ค๋ก ์ง์
|
| 1201 |
+
- ์ด๊ธฐ ํด์ ์์
|
| 1202 |
+
|
| 1203 |
+
Stage 0.3~0.6: ๋ถ๋ฐฐ์๋ก ๋ฐ๋ฌ
|
| 1204 |
+
- ์๋ก ๋ถ๊ธฐ ์์
|
| 1205 |
+
- ๊ฐ ์๋ก ์์์ ์์ฐ์ ๋ฐฉ ํ์ฑ
|
| 1206 |
+
|
| 1207 |
+
Stage 0.6~1.0: ์กฐ์กฑ์ ์์ฑ
|
| 1208 |
+
- ๋ค์์ ๋ถ๋ฐฐ์๋ก๊ฐ ์๋ฐ ๋ชจ์์ผ๋ก ๋์ถ
|
| 1209 |
+
- ๊ฐ finger ๋์์ ํด์ ํ๋ฐ
|
| 1210 |
+
|
| 1211 |
+
ํ์ฑ ์กฐ๊ฑด:
|
| 1212 |
+
- ํ๋ ์๋์ง ์ฝํจ (๋ง ๋๋ ๋ดํด)
|
| 1213 |
+
- ์กฐ์ ์ํฅ ์ ์
|
| 1214 |
+
- ํด์ ๋ฌผ ๊ณต๊ธ ํ๋ถ
|
| 1215 |
+
|
| 1216 |
+
๋ํ ์ฌ๋ก: ๋ฏธ์์ํผ๊ฐ ์ผ๊ฐ์ฃผ
|
| 1217 |
+
"""
|
| 1218 |
h, w = grid_size, grid_size
|
| 1219 |
elevation = np.zeros((h, w))
|
| 1220 |
elevation[:, :] = -5.0 # ๋ฐ๋ค
|
| 1221 |
|
| 1222 |
+
apex_y = int(h * 0.12)
|
| 1223 |
center_x = w // 2
|
| 1224 |
|
| 1225 |
+
# Stage์ ๋ฐ๋ฅธ ๋ถ๋ฐฐ์๋ก ๊ฐ์
|
| 1226 |
+
if stage < 0.3:
|
| 1227 |
+
num_fingers = 1
|
| 1228 |
+
elif stage < 0.5:
|
| 1229 |
+
num_fingers = 3
|
| 1230 |
+
else:
|
| 1231 |
+
num_fingers = min(7, 3 + int(4 * (stage - 0.5) / 0.5))
|
| 1232 |
+
|
| 1233 |
+
max_length = int((h - apex_y) * stage * 0.9)
|
| 1234 |
+
finger_width = max(3, int(4 * (1 - stage * 0.3))) # ์๊ฐ์ด ๊ฐ์๋ก ์ข์์ง
|
| 1235 |
+
|
| 1236 |
+
distributary_info = []
|
| 1237 |
|
| 1238 |
for i in range(num_fingers):
|
| 1239 |
+
# ๊ฐ๋ ๋ถํฌ (์ค์์์ ์์ชฝ์ผ๋ก)
|
| 1240 |
+
if num_fingers == 1:
|
| 1241 |
+
angle = 0
|
| 1242 |
+
else:
|
| 1243 |
+
angle = np.radians(-35 + 70 * i / (num_fingers - 1))
|
| 1244 |
+
|
| 1245 |
+
finger_length = 0
|
| 1246 |
|
| 1247 |
for d in range(max_length):
|
| 1248 |
r = apex_y + int(d * np.cos(angle))
|
| 1249 |
c = center_x + int(d * np.sin(angle))
|
| 1250 |
|
| 1251 |
if 0 <= r < h and 0 <= c < w:
|
| 1252 |
+
finger_length = d
|
| 1253 |
+
|
| 1254 |
+
# ๋ถ๋ฐฐ์๋ก + ์์ฐ์ ๋ฐฉ
|
| 1255 |
+
for dc in range(-finger_width, finger_width + 1):
|
| 1256 |
for dr in range(-2, 3):
|
| 1257 |
nr, nc = r + dr, c + dc
|
| 1258 |
if 0 <= nr < h and 0 <= nc < w:
|
| 1259 |
dist = np.sqrt(dr**2 + dc**2)
|
|
|
|
|
|
|
| 1260 |
|
| 1261 |
+
# ์ค์: ์๋ก (๋ฎ์), ์์ชฝ: ์์ฐ์ ๋ฐฉ (๋์)
|
| 1262 |
+
if abs(dc) < 2:
|
| 1263 |
+
# ์๋ก
|
| 1264 |
+
z = 2.0 * (1 - d / max_length) * stage
|
| 1265 |
+
else:
|
| 1266 |
+
# ์์ฐ์ ๋ฐฉ
|
| 1267 |
+
z = 6.0 * (1 - d / max_length) * (1 - (abs(dc) - 2) / finger_width) * stage
|
| 1268 |
+
|
| 1269 |
+
elevation[nr, nc] = max(elevation[nr, nc], z)
|
| 1270 |
+
|
| 1271 |
+
distributary_info.append({
|
| 1272 |
+
'angle_deg': np.degrees(angle),
|
| 1273 |
+
'length': finger_length
|
| 1274 |
+
})
|
| 1275 |
+
|
| 1276 |
+
# ์๋ฅ ํ์ฒ
|
| 1277 |
for r in range(apex_y):
|
| 1278 |
+
for dc in range(-4, 5):
|
| 1279 |
if 0 <= center_x + dc < w:
|
| 1280 |
+
channel_depth = 3.0 * (1 - abs(dc) / 5)
|
| 1281 |
+
elevation[r, center_x + dc] = 5.0 + channel_depth
|
| 1282 |
+
|
| 1283 |
+
if return_metadata:
|
| 1284 |
+
return elevation, {
|
| 1285 |
+
'num_distributaries': num_fingers,
|
| 1286 |
+
'max_length': max_length,
|
| 1287 |
+
'distributary_info': distributary_info,
|
| 1288 |
+
'stage_description': _get_bird_foot_stage_desc(stage)
|
| 1289 |
+
}
|
| 1290 |
+
|
| 1291 |
return elevation
|
| 1292 |
|
| 1293 |
|
| 1294 |
+
def _get_bird_foot_stage_desc(stage: float) -> str:
|
| 1295 |
+
"""์กฐ์กฑ์ ์ผ๊ฐ์ฃผ ๋จ๊ณ๋ณ ์ค๋ช
"""
|
| 1296 |
+
if stage < 0.2:
|
| 1297 |
+
return "๐๏ธ ์ด๊ธฐ: ๋จ์ผ ํ๋๊ฐ ๋ฐ๋ค๋ก ์ง์
"
|
| 1298 |
+
elif stage < 0.4:
|
| 1299 |
+
return "๐ ํด์ ์์: ํ๊ตฌ์์ ํด์ ๋ฌผ ์ถ์ "
|
| 1300 |
+
elif stage < 0.6:
|
| 1301 |
+
return "๐ ๋ถ๊ธฐ ๋ฐ์: ์๋ก๊ฐ ์ฌ๋ฌ ๊ฐ๋๋ก ๋๋จ"
|
| 1302 |
+
elif stage < 0.8:
|
| 1303 |
+
return "๐ฆถ ์กฐ์กฑ์ ๋ฐ๋ฌ: ๊ฐ finger์ ์์ฐ์ ๋ฐฉ ํ์ฑ"
|
| 1304 |
+
else:
|
| 1305 |
+
return "๐ฆ ์กฐ์กฑ์ ์์ฑ: ์๋ฐ ๋ชจ์ ์ผ๊ฐ์ฃผ"
|
| 1306 |
+
|
| 1307 |
+
|
| 1308 |
def create_arcuate_delta(grid_size: int = 100, stage: float = 1.0) -> np.ndarray:
|
| 1309 |
"""ํธ์ ์ผ๊ฐ์ฃผ (Arcuate Delta) - ๋์ผ๊ฐํ"""
|
| 1310 |
h, w = grid_size, grid_size
|
|
|
|
| 1374 |
|
| 1375 |
|
| 1376 |
def create_cirque(grid_size: int = 100, stage: float = 1.0,
|
| 1377 |
+
depth: float = 50.0, return_metadata: bool = False) -> np.ndarray:
|
| 1378 |
+
"""๊ถ๊ณก (Cirque) - ๋นํ ์์์
|
| 1379 |
+
|
| 1380 |
+
Stage 0~0.3: ๋๋ฐ ์นจ์ (Nivation)
|
| 1381 |
+
- ๋ง๋
์ค์ ๋๊ฒฐํํ๋ก ์์ ์ํน ํ์ฑ
|
| 1382 |
+
- ์์ค(rock debris) ์ถ์ ์์
|
| 1383 |
+
|
| 1384 |
+
Stage 0.3~0.6: ๋นํํ (Glacierization)
|
| 1385 |
+
- ํผ๋ฅธ(firn) โ ๋นํ ์ผ์ ์๋ฐ
|
| 1386 |
+
- ํ๋ฌํน(plucking)๊ณผ ๋ง์(abrasion) ์์
|
| 1387 |
+
- ํ๋ฒฝ(headwall) ๋ฐ๋ฌ
|
| 1388 |
+
|
| 1389 |
+
Stage 0.6~0.8: ๊ถ๊ณก ํ์ฅ
|
| 1390 |
+
- ๋ฒ ๋ฅด๊ทธ์๋ฐํธ(bergschrund) ๋๊ฒฐํํ
|
| 1391 |
+
- ๋ฐ๋ฅ๊ณผ ๋ฒฝ ์นจ์ ํ๋
|
| 1392 |
+
|
| 1393 |
+
Stage 0.8~1.0: ๋นํ ์๋ฉธ + ํด(Tarn) ํ์ฑ
|
| 1394 |
+
- ๋นํ ์ตํด
|
| 1395 |
+
- ๋นํํธ(tarn) ํ์ฑ
|
| 1396 |
+
"""
|
| 1397 |
h, w = grid_size, grid_size
|
| 1398 |
elevation = np.zeros((h, w))
|
| 1399 |
|
| 1400 |
# ์ฐ์
๋ฐฐ๊ฒฝ
|
| 1401 |
+
mountain_height = depth + 40.0
|
| 1402 |
+
elevation[:, :] = mountain_height
|
| 1403 |
|
| 1404 |
# ๊ถ๊ณก ์์น (์๋จ ์ค์)
|
| 1405 |
+
cirque_y = int(h * 0.35)
|
| 1406 |
cirque_x = w // 2
|
| 1407 |
+
|
| 1408 |
+
# Stage์ ๋ฐ๋ฅธ ๊ถ๊ณก ํฌ๊ธฐ
|
| 1409 |
+
if stage < 0.3:
|
| 1410 |
+
# ๋๋ฐ ์นจ์: ์์ ์ํน
|
| 1411 |
+
cirque_radius = int(w * 0.15 * (stage / 0.3))
|
| 1412 |
+
bowl_depth = depth * 0.3 * (stage / 0.3)
|
| 1413 |
+
else:
|
| 1414 |
+
# ๋นํํ ์ดํ: ๊ธ๊ฒฉํ ํ์ฅ
|
| 1415 |
+
cirque_radius = int(w * 0.15 + w * 0.15 * ((stage - 0.3) / 0.7))
|
| 1416 |
+
bowl_depth = depth * (0.3 + 0.7 * ((stage - 0.3) / 0.7))
|
| 1417 |
+
|
| 1418 |
+
# ํ๋ฒฝ ๊ฒฝ์ฌ๋ (stage์ ๋ฐ๋ผ ๊ธํด์ง)
|
| 1419 |
+
headwall_steepness = 0.3 + 0.7 * min(1.0, stage / 0.6)
|
| 1420 |
|
| 1421 |
for r in range(h):
|
| 1422 |
for c in range(w):
|
|
|
|
| 1425 |
dist = np.sqrt(dy**2 + dx**2)
|
| 1426 |
|
| 1427 |
if dist < cirque_radius:
|
| 1428 |
+
# ๋ฐฉํฅ์ ๋ฐ๋ฅธ ๋์ด ๋ณํ
|
| 1429 |
+
if dy < 0:
|
| 1430 |
+
# ํ๋ฒฝ (Headwall) - ๊ธ๊ฒฝ์ฌ
|
| 1431 |
+
wall_height = bowl_depth * headwall_steepness * (1 - dist / cirque_radius)
|
| 1432 |
+
elevation[r, c] = mountain_height - bowl_depth + wall_height
|
| 1433 |
+
else:
|
| 1434 |
+
# ๋ฐ๋ฅ - ํํํ๊ฑฐ๋ ์ฝ๊ฐ ์ค๋ชฉ
|
| 1435 |
+
floor_depth = bowl_depth * (1 - (dist / cirque_radius) ** 2) * 0.8
|
| 1436 |
+
elevation[r, c] = mountain_height - floor_depth
|
| 1437 |
+
|
| 1438 |
+
# ๋นํ ์ ์ถ๊ตฌ (ํ๋ฅ ๋ฐฉํฅ)
|
| 1439 |
+
if cirque_y < r < cirque_y + cirque_radius * 0.5:
|
| 1440 |
+
if abs(c - cirque_x) < cirque_radius * 0.3:
|
| 1441 |
+
outlet_depth = bowl_depth * 0.5 * (1 - (r - cirque_y) / (cirque_radius * 0.5))
|
| 1442 |
+
elevation[r, c] = min(elevation[r, c], mountain_height - outlet_depth)
|
| 1443 |
+
|
| 1444 |
+
# ํด(Tarn) ํธ์ - stage > 0.8
|
| 1445 |
+
tarn_present = False
|
| 1446 |
+
tarn_depth = 0
|
| 1447 |
+
if stage > 0.8:
|
| 1448 |
+
tarn_present = True
|
| 1449 |
+
tarn_progress = (stage - 0.8) / 0.2
|
| 1450 |
+
tarn_radius = int(cirque_radius * 0.5 * tarn_progress)
|
| 1451 |
+
tarn_depth = bowl_depth * 0.3 * tarn_progress
|
| 1452 |
+
|
| 1453 |
+
for r in range(cirque_y - tarn_radius, cirque_y + tarn_radius):
|
| 1454 |
+
for c in range(cirque_x - tarn_radius, cirque_x + tarn_radius):
|
| 1455 |
+
if 0 <= r < h and 0 <= c < w:
|
| 1456 |
+
dist = np.sqrt((r - cirque_y)**2 + (c - cirque_x)**2)
|
| 1457 |
+
if dist < tarn_radius:
|
| 1458 |
+
# ํธ์ ๋ฐ๋ฅ
|
| 1459 |
+
elevation[r, c] = mountain_height - bowl_depth - tarn_depth * (1 - dist / tarn_radius)
|
| 1460 |
+
|
| 1461 |
+
if return_metadata:
|
| 1462 |
+
return elevation, {
|
| 1463 |
+
'cirque_radius': cirque_radius,
|
| 1464 |
+
'bowl_depth': bowl_depth,
|
| 1465 |
+
'headwall_steepness': headwall_steepness,
|
| 1466 |
+
'tarn_present': tarn_present,
|
| 1467 |
+
'tarn_depth': tarn_depth if tarn_present else 0,
|
| 1468 |
+
'stage_description': _get_cirque_stage_desc(stage)
|
| 1469 |
+
}
|
| 1470 |
+
|
| 1471 |
return elevation
|
| 1472 |
|
| 1473 |
|
| 1474 |
+
def _get_cirque_stage_desc(stage: float) -> str:
|
| 1475 |
+
"""๊ถ๊ณก ๋จ๊ณ๋ณ ์ค๋ช
"""
|
| 1476 |
+
if stage < 0.2:
|
| 1477 |
+
return "โ๏ธ ๋๋ฐ ์ด๊ธฐ: ๋ง๋
์ค ์๋ ๋๊ฒฐํํ ์์"
|
| 1478 |
+
elif stage < 0.4:
|
| 1479 |
+
return "๐๏ธ ๋๋ฐ ํ๊ธฐ: ์์ ์ํน, ์์ค ์ถ์ "
|
| 1480 |
+
elif stage < 0.6:
|
| 1481 |
+
return "๐ง ๋นํํ: ํผ๋ฅธโ๋นํ, ํ๋ฌํน+๋ง์ ์์"
|
| 1482 |
+
elif stage < 0.8:
|
| 1483 |
+
return "โ๏ธ ๊ถ๊ณก ํ์ฅ: ๋ฒ ๋ฅด๊ทธ์๋ฐํธ ๋๊ฒฐํํ"
|
| 1484 |
+
else:
|
| 1485 |
+
return "๐ง ํด ํ์ฑ: ๋นํ ์๋ฉธ, ๋นํํธ ํ์ฑ"
|
| 1486 |
+
|
| 1487 |
+
|
| 1488 |
+
def create_horn(grid_size: int = 100, stage: float = 1.0,
|
| 1489 |
+
num_cirques: int = 4, return_metadata: bool = False) -> np.ndarray:
|
| 1490 |
+
"""ํธ๋ฅธ (Horn) - ํผ๋ผ๋ฏธ๋ํ ๋ด์ฐ๋ฆฌ
|
| 1491 |
+
|
| 1492 |
+
Stage 0~0.3: ์ด๊ธฐ ๊ถ๊ณก ํ์ฑ
|
| 1493 |
+
- ์ฌ๋ฌ ๋ฐฉํฅ์์ ๊ถ๊ณก ๋ฐ๋ฌ ์์
|
| 1494 |
+
- ๋ฅ์ ํํ ์ ์ง
|
| 1495 |
+
|
| 1496 |
+
Stage 0.3~0.6: ๊ถ๊ณก ํ์ฅ
|
| 1497 |
+
- ๋๋ถ์นจ์์ผ๋ก ๊ถ๊ณก ๊น์ด์ง
|
| 1498 |
+
- ์๋ ํธ(arรชte) ๋ฐ๋ฌ
|
| 1499 |
+
|
| 1500 |
+
Stage 0.6~0.9: ํธ๋ฅธ ํ์ฑ
|
| 1501 |
+
- ๊ถ๊ณก๋ค์ ๋ง๋จ
|
| 1502 |
+
- ํผ๋ผ๋ฏธ๋ํ ๋ด์ฐ๋ฆฌ ๋์ถ
|
| 1503 |
+
|
| 1504 |
+
Stage 0.9~1.0: ์ฑ์ ํธ๋ฅธ
|
| 1505 |
+
- ๋ ์นด๋ก์ด ์ ์
|
| 1506 |
+
- ๋งํฐํธ๋ฅธ ํํ
|
| 1507 |
+
|
| 1508 |
+
๋ํ ์ฌ๋ก: ๋งํฐํธ๋ฅธ (์ค์์ค), K2
|
| 1509 |
+
"""
|
| 1510 |
h, w = grid_size, grid_size
|
| 1511 |
elevation = np.zeros((h, w))
|
| 1512 |
|
| 1513 |
center = (h // 2, w // 2)
|
| 1514 |
+
max_peak_height = 120.0
|
| 1515 |
+
|
| 1516 |
+
# Stage์ ๋ฐ๋ฅธ ๋ด์ฐ๋ฆฌ ๋์ด์ ๊ถ๊ณก ๊น์ด
|
| 1517 |
+
if stage < 0.3:
|
| 1518 |
+
peak_height = max_peak_height * 0.6
|
| 1519 |
+
cirque_depth = 30.0 * (stage / 0.3)
|
| 1520 |
+
else:
|
| 1521 |
+
peak_height = max_peak_height * (0.6 + 0.4 * ((stage - 0.3) / 0.7))
|
| 1522 |
+
cirque_depth = 30.0 + 40.0 * ((stage - 0.3) / 0.7)
|
| 1523 |
|
| 1524 |
+
cirque_radius = int(w * 0.28 * (0.6 + 0.4 * stage))
|
|
|
|
|
|
|
| 1525 |
|
| 1526 |
+
# ๊ธฐ๋ณธ ์๋ฟํ ์ฐ์ฒด
|
| 1527 |
for r in range(h):
|
| 1528 |
for c in range(w):
|
| 1529 |
dy = r - center[0]
|
| 1530 |
dx = c - center[1]
|
| 1531 |
dist = np.sqrt(dy**2 + dx**2)
|
| 1532 |
|
| 1533 |
+
# ์๋ฟํ ๊ธฐ๋ณธ ํํ
|
| 1534 |
+
elevation[r, c] = peak_height * max(0, 1 - dist / (w * 0.45))
|
| 1535 |
+
|
| 1536 |
+
# ๋ค๋ฐฉํฅ ๊ถ๊ณก ํ๊ธฐ
|
| 1537 |
+
cirque_centers = []
|
| 1538 |
+
arete_count = 0
|
| 1539 |
+
|
| 1540 |
+
for i in range(num_cirques):
|
| 1541 |
+
angle = i * 2 * np.pi / num_cirques + np.pi / num_cirques # ์ฝ๊ฐ ํ์
|
| 1542 |
+
cx = center[1] + int(cirque_radius * 0.7 * np.cos(angle))
|
| 1543 |
+
cy = center[0] + int(cirque_radius * 0.7 * np.sin(angle))
|
| 1544 |
+
cirque_centers.append((cy, cx))
|
| 1545 |
+
|
| 1546 |
+
for r in range(h):
|
| 1547 |
+
for c in range(w):
|
| 1548 |
cdist = np.sqrt((r - cy)**2 + (c - cx)**2)
|
| 1549 |
+
|
| 1550 |
if cdist < cirque_radius * 0.6:
|
| 1551 |
+
# ๊ถ๊ณก ํ๊ธฐ (๋ฐ๊ทธ๋ฆ ํํ)
|
| 1552 |
+
floor_height = 20.0 + cirque_depth * (cdist / (cirque_radius * 0.6)) ** 0.5
|
|
|
|
| 1553 |
|
| 1554 |
+
# ํ๋ฒฝ ๋ฐฉํฅ (์ค์ฌ์ชฝ)์ผ๋ก ๋ ๊ธ๊ฒฝ์ฌ
|
| 1555 |
+
dir_to_center = np.sqrt((r - center[0])**2 + (c - center[1])**2)
|
| 1556 |
+
if dir_to_center < cirque_radius * 0.5:
|
| 1557 |
+
floor_height += 20.0 * (1 - dir_to_center / (cirque_radius * 0.5))
|
| 1558 |
+
|
| 1559 |
+
elevation[r, c] = min(elevation[r, c], floor_height)
|
| 1560 |
+
|
| 1561 |
+
# ์๋ ํธ ๊ฐํ (์ธ์ ๊ถ๊ณก ์ฌ์ด ๋ฅ์ )
|
| 1562 |
+
for i in range(num_cirques):
|
| 1563 |
+
next_i = (i + 1) % num_cirques
|
| 1564 |
+
cy1, cx1 = cirque_centers[i]
|
| 1565 |
+
cy2, cx2 = cirque_centers[next_i]
|
| 1566 |
+
|
| 1567 |
+
# ๋ ๊ถ๊ณก ์ค๊ฐ์
|
| 1568 |
+
mid_y, mid_x = (cy1 + cy2) // 2, (cx1 + cx2) // 2
|
| 1569 |
+
|
| 1570 |
+
for r in range(h):
|
| 1571 |
+
for c in range(w):
|
| 1572 |
+
# ๋ฅ์ ๋ฐฉํฅ์ ๊ฐ๊น์ด ํฝ์
์ ๋์ด ์ ์ง
|
| 1573 |
+
dist_to_mid = np.sqrt((r - mid_y)**2 + (c - mid_x)**2)
|
| 1574 |
+
if dist_to_mid < cirque_radius * 0.3:
|
| 1575 |
+
dist_to_center = np.sqrt((r - center[0])**2 + (c - center[1])**2)
|
| 1576 |
+
if dist_to_center < cirque_radius * 0.5:
|
| 1577 |
+
ridge_boost = 15.0 * stage * (1 - dist_to_mid / (cirque_radius * 0.3))
|
| 1578 |
+
elevation[r, c] = min(elevation[r, c] + ridge_boost, peak_height)
|
| 1579 |
+
|
| 1580 |
+
if return_metadata:
|
| 1581 |
+
return elevation, {
|
| 1582 |
+
'peak_height': peak_height,
|
| 1583 |
+
'num_cirques': num_cirques,
|
| 1584 |
+
'cirque_depth': cirque_depth,
|
| 1585 |
+
'cirque_radius': cirque_radius,
|
| 1586 |
+
'cirque_centers': cirque_centers,
|
| 1587 |
+
'stage_description': _get_horn_stage_desc(stage)
|
| 1588 |
+
}
|
| 1589 |
+
|
| 1590 |
return elevation
|
| 1591 |
|
| 1592 |
|
| 1593 |
+
def _get_horn_stage_desc(stage: float) -> str:
|
| 1594 |
+
"""ํธ๋ฅธ ๋จ๊ณ๋ณ ์ค๋ช
"""
|
| 1595 |
+
if stage < 0.2:
|
| 1596 |
+
return "๐๏ธ ์ด๊ธฐ ์ฐ์ฒด: ์๋ฟํ ์ฐ, ๊ถ๊ณก ํ์ฑ ์์"
|
| 1597 |
+
elif stage < 0.4:
|
| 1598 |
+
return "โ๏ธ ๊ถ๊ณก ๋ฐ๋ฌ: ์ฌ๋ฌ ๋ฐฉํฅ์์ ๋นํ ์นจ์"
|
| 1599 |
+
elif stage < 0.6:
|
| 1600 |
+
return "โ๏ธ ๋๋ถ์นจ์: ๊ถ๊ณก ๊น์ด์ง, ์๋ ํธ ํ์ฑ"
|
| 1601 |
+
elif stage < 0.8:
|
| 1602 |
+
return "๐ป ํธ๋ฅธ ํ์ฑ: ๊ถ๊ณก ๋ง๋จ, ํผ๋ผ๋ฏธ๋ ๋์ถ"
|
| 1603 |
+
else:
|
| 1604 |
+
return "โฐ๏ธ ์ฑ์ ํธ๋ฅธ: ๋ ์นด๋ก์ด ์ ์ (๋งํฐํธ๋ฅธํ)"
|
| 1605 |
+
|
| 1606 |
+
|
| 1607 |
def create_shield_volcano(grid_size: int = 100, stage: float = 1.0,
|
| 1608 |
max_height: float = 40.0) -> np.ndarray:
|
| 1609 |
"""์์ํ์ฐ (Shield Volcano) - ์๋งํ ๊ฒฝ์ฌ"""
|
|
|
|
| 1759 |
# ์ถ๊ฐ ์งํ (Additional Landforms)
|
| 1760 |
# ============================================
|
| 1761 |
|
| 1762 |
+
def create_fjord(grid_size: int = 100, stage: float = 1.0,
|
| 1763 |
+
return_metadata: bool = False) -> np.ndarray:
|
| 1764 |
"""ํผ์ค๋ฅด๋ (Fjord) - ๋นํ ํํด ํ ๋ฐ๋ค ์ ์
|
| 1765 |
|
| 1766 |
Stage 0.0~0.4: ๋นํ๊ฐ U์๊ณก์ ์ฑ์ (๋นํ๊ธฐ)
|
| 1767 |
+
- ๊ณ๊ณก๋นํ(Valley Glacier)๊ฐ U์๊ณก ์ ์
|
| 1768 |
+
- ํ๋ฌํน(Plucking)๊ณผ ๋ง์(Abrasion)์ผ๋ก ๋ฐ๋ฅ ์นจ์
|
| 1769 |
+
- ๋นํ ๋๊ป ์๋ฐฑm โ ํด์๋ฉด ์ดํ๊น์ง ์นจ์
|
| 1770 |
+
|
| 1771 |
+
Stage 0.4~0.7: ๋นํ ํํด ์์ (๊ฐ๋น๊ธฐ)
|
| 1772 |
+
- ๊ธฐํ ์จ๋ํ๋ก ๋นํ ์ตํด
|
| 1773 |
+
- ๋นํ ๋ง๋จ์ด ์๋ฅ๋ก ํํด
|
| 1774 |
+
- ํด์๊ฐ ๋นํ ๋ค๋ฅผ ๋ฐ๋ผ ์ ์
|
| 1775 |
+
|
| 1776 |
+
Stage 0.7~1.0: ํผ์ค๋ฅด๋ ์์ฑ
|
| 1777 |
+
- ๋นํ ์์ ์๋ฉธ
|
| 1778 |
+
- ๊น๊ณ ์ข์ ๋ง ํ์ฑ (์์ฌ ์ต๋ 1,300m)
|
| 1779 |
+
- ์๋ฅ๋ก ๊ฐ์๋ก ์์ฌ ๊ฐ์
|
| 1780 |
"""
|
| 1781 |
h, w = grid_size, grid_size
|
| 1782 |
elevation = np.zeros((h, w))
|
| 1783 |
+
glacier_surface = None
|
| 1784 |
+
sea_surface = None
|
| 1785 |
|
| 1786 |
# ์ฐ์
์งํ (๋์ ์ฐ)
|
| 1787 |
elevation[:, :] = 100.0
|
|
|
|
| 1790 |
valley_width = int(w * 0.25)
|
| 1791 |
valley_depth = 60.0
|
| 1792 |
|
| 1793 |
+
# U์๊ณก ํ์ฑ (๊ธฐ์ )
|
| 1794 |
for r in range(h):
|
| 1795 |
for c in range(w):
|
| 1796 |
dx = abs(c - center)
|
| 1797 |
|
| 1798 |
if dx < valley_width:
|
| 1799 |
+
# U์ ๋ฐ๋ฅ (๋นํ ์นจ์์ผ๋ก ๊น์ด์ง)
|
| 1800 |
+
# stage์ ๋ฐ๋ผ ์นจ์ ๊น์ด ์ฆ๊ฐ
|
| 1801 |
+
erosion_depth = 10.0 - 50.0 * min(stage / 0.7, 1.0)
|
| 1802 |
+
elevation[r, c] = erosion_depth
|
| 1803 |
elif dx < valley_width + 15:
|
| 1804 |
+
# U์ ์ธก๋ฒฝ (๊ธ๊ฒฝ์ฌ)
|
| 1805 |
t = (dx - valley_width) / 15
|
| 1806 |
+
base = 10.0 - 50.0 * min(stage / 0.7, 1.0) if dx == valley_width else 0
|
| 1807 |
+
elevation[r, c] = base + 100.0 * (t ** 0.5)
|
| 1808 |
|
| 1809 |
# ๋นํ / ๋ฐ๋ค ์ํ
|
| 1810 |
if stage < 0.4:
|
| 1811 |
# ๋นํ๊ธฐ: U์๊ณก์ ๋นํ ์ฑ์
|
| 1812 |
+
glacier_extent = int(h * (0.95 - stage * 0.5))
|
| 1813 |
+
glacier_thickness = 60.0 * (1 - stage * 0.3)
|
| 1814 |
+
|
| 1815 |
+
glacier_surface = np.full((h, w), np.nan)
|
| 1816 |
|
| 1817 |
for r in range(glacier_extent):
|
| 1818 |
for c in range(w):
|
| 1819 |
dx = abs(c - center)
|
| 1820 |
if dx < valley_width:
|
| 1821 |
+
# ๋นํ ํ๋ฉด (๋ณผ๋ก, ์ค์ ๋๊บผ์)
|
| 1822 |
cross_profile = glacier_thickness * (1 - (dx / valley_width) ** 2)
|
| 1823 |
+
glacier_surface[r, c] = elevation[r, c] + cross_profile
|
| 1824 |
|
| 1825 |
elif stage < 0.7:
|
| 1826 |
+
# ๋นํ ํํด ์ค
|
| 1827 |
retreat_factor = (stage - 0.4) / 0.3
|
| 1828 |
|
| 1829 |
# ๋นํ ์๋ฅ (์๋ฅ์๋ง)
|
| 1830 |
+
glacier_end = int(h * (0.8 - 0.6 * retreat_factor))
|
| 1831 |
+
glacier_thickness = 50.0 * (1 - retreat_factor * 0.7)
|
| 1832 |
+
|
| 1833 |
+
glacier_surface = np.full((h, w), np.nan)
|
| 1834 |
+
sea_surface = np.full((h, w), np.nan)
|
| 1835 |
|
| 1836 |
for r in range(glacier_end):
|
| 1837 |
for c in range(w):
|
| 1838 |
dx = abs(c - center)
|
| 1839 |
if dx < valley_width:
|
| 1840 |
cross_profile = glacier_thickness * (1 - (dx / valley_width) ** 2)
|
| 1841 |
+
glacier_surface[r, c] = elevation[r, c] + cross_profile
|
| 1842 |
|
| 1843 |
# ๋ฐ๋ค ์ ์
(ํ๋ฅ๋ถํฐ)
|
| 1844 |
+
for r in range(glacier_end, h):
|
|
|
|
| 1845 |
for c in range(w):
|
| 1846 |
dx = abs(c - center)
|
| 1847 |
if dx < valley_width:
|
| 1848 |
+
sea_surface[r, c] = 0 # ํด์๋ฉด
|
| 1849 |
+
|
| 1850 |
else:
|
| 1851 |
+
# ํผ์ค๋ฅด๋ ์์ฑ
|
| 1852 |
+
sea_surface = np.full((h, w), np.nan)
|
| 1853 |
|
| 1854 |
for r in range(h):
|
| 1855 |
for c in range(w):
|
| 1856 |
dx = abs(c - center)
|
| 1857 |
if dx < valley_width:
|
| 1858 |
+
sea_surface[r, c] = 0 # ํด์๋ฉด
|
| 1859 |
+
|
| 1860 |
+
if return_metadata:
|
| 1861 |
+
return elevation, {
|
| 1862 |
+
'glacier_surface': glacier_surface,
|
| 1863 |
+
'sea_surface': sea_surface,
|
| 1864 |
+
'stage_description': _get_fjord_stage_desc(stage),
|
| 1865 |
+
'glacier_extent': glacier_extent if stage < 0.4 else (glacier_end if stage < 0.7 else 0),
|
| 1866 |
+
'process_info': {
|
| 1867 |
+
'plucking': '๋นํ๊ฐ ๊ธฐ๋ฐ์์ ๋๊ฒฐ ๋ถ์ฐฉ ํ ๋ผ์ด๋',
|
| 1868 |
+
'abrasion': '๋นํ ๋ฐ๋ฅ์ ์์ ํํธ์ด ๊ธฐ๋ฐ์์ ๊น์',
|
| 1869 |
+
'overdeepening': '๋นํ ์นจ์์ด ํด์๋ฉด ์๋๊น์ง ์งํ'
|
| 1870 |
+
}
|
| 1871 |
+
}
|
| 1872 |
+
|
| 1873 |
return elevation
|
| 1874 |
|
| 1875 |
|
| 1876 |
+
def _get_fjord_stage_desc(stage: float) -> str:
|
| 1877 |
+
"""ํผ์ค๋ฅด๋ ๋จ๊ณ๋ณ ์ค๋ช
"""
|
| 1878 |
+
if stage < 0.2:
|
| 1879 |
+
return "โ๏ธ ๋นํ๊ธฐ ์ด๊ธฐ: ๋นํ๊ฐ U์๊ณก์ ์ฑ์ฐ๊ธฐ ์์"
|
| 1880 |
+
elif stage < 0.4:
|
| 1881 |
+
return "๐๏ธ ๋นํ๊ธฐ ์ต์ฑ๊ธฐ: ํ๋ฌํน๊ณผ ๋ง์์ผ๋ก ๋ฐ๋ฅ ๊น์ด์ง"
|
| 1882 |
+
elif stage < 0.55:
|
| 1883 |
+
return "๐ก๏ธ ๊ฐ๋น๊ธฐ ์์: ์จ๋ํ๋ก ๋นํ ์ตํด ์์"
|
| 1884 |
+
elif stage < 0.7:
|
| 1885 |
+
return "๐ ํด์นจ ์งํ: ๋นํ ํํด, ํด์ ์ ์
"
|
| 1886 |
+
else:
|
| 1887 |
+
return "๐
ํผ์ค๋ฅด๋ ์์ฑ: ๊น๊ณ ์ข์ ๋ง ํ์ฑ"
|
| 1888 |
+
|
| 1889 |
+
|
| 1890 |
def create_drumlin(grid_size: int = 100, stage: float = 1.0,
|
| 1891 |
num_drumlins: int = 5) -> np.ndarray:
|
| 1892 |
"""๋๋ผ๋ฆฐ (Drumlin) - ๋นํ ๋ฐฉํฅ ํ์ํ ์ธ๋"""
|
|
|
|
| 2007 |
|
| 2008 |
|
| 2009 |
def create_waterfall(grid_size: int = 100, stage: float = 1.0,
|
| 2010 |
+
drop_height: float = 50.0, return_metadata: bool = False) -> np.ndarray:
|
| 2011 |
"""ํญํฌ (Waterfall) - ๋๋ถ์นจ์์ผ๋ก ํํด
|
| 2012 |
|
| 2013 |
+
Stage 0.0~0.3: ํญํฌ ํ์ฑ (ํ๋ฅ์์ ์์)
|
| 2014 |
+
Stage 0.3~0.7: ๋๋ถ์นจ์ ์งํ (์๋ฅ๋ก ํํด)
|
| 2015 |
+
Stage 0.7~1.0: ํ๊ณก ๋ฐ๋ฌ (๊น์ ๊ณก์ )
|
| 2016 |
+
|
| 2017 |
+
์ฐจ๋ณ์นจ์ (Differential Erosion):
|
| 2018 |
+
- ๊ฒฝ์์ธต(hard rock): ์นจ์์ ๊ฐํจ โ ํญํฌ ์ ๋ฒฝ ํ์ฑ
|
| 2019 |
+
- ์ฐ์์ธต(soft rock): ์นจ์์ ์ฝํจ โ ์ธ๋์ปทํ
โ ๋ถ๊ดด
|
| 2020 |
+
|
| 2021 |
+
ํ๋ฐ์งํ(Plunge Pool):
|
| 2022 |
+
- ๋ํ์์ ์ถฉ๊ฒฉ์ผ๋ก ๋ฐ๋ฅ ์นจ์
|
| 2023 |
+
- ์๋ฅ(vortex)์ ์ํ ํฌํธํ ํ์ฑ
|
| 2024 |
"""
|
| 2025 |
h, w = grid_size, grid_size
|
| 2026 |
elevation = np.zeros((h, w))
|
| 2027 |
center = w // 2
|
| 2028 |
|
| 2029 |
# ํญํฌ ์์น (stage์ ๋ฐ๋ผ ์๋ฅ๋ก ํํด)
|
| 2030 |
+
initial_fall = int(h * 0.75)
|
| 2031 |
+
final_fall = int(h * 0.25)
|
| 2032 |
+
retreat_distance = (initial_fall - final_fall) * stage
|
| 2033 |
+
fall_r = int(initial_fall - retreat_distance)
|
| 2034 |
|
| 2035 |
+
# ์๋ฅ (๋์ ๊ณ ์ - ๊ฒฝ์์ธต)
|
| 2036 |
hard_rock_height = drop_height + 30.0
|
| 2037 |
+
|
| 2038 |
for r in range(fall_r):
|
| 2039 |
for c in range(w):
|
| 2040 |
# ์๋ฅ๋ก ๊ฐ์๋ก ๋์์ง
|
| 2041 |
upstream_rise = (fall_r - r) * 0.3
|
| 2042 |
elevation[r, c] = hard_rock_height + upstream_rise
|
| 2043 |
|
| 2044 |
+
# ํญํฌ ์ ๋ฒฝ (๊ฑฐ์ ์์ง)
|
| 2045 |
+
cliff_width = max(3, int(5 * stage))
|
| 2046 |
for r in range(fall_r, min(fall_r + cliff_width, h)):
|
| 2047 |
for c in range(w):
|
| 2048 |
t = (r - fall_r) / cliff_width
|
| 2049 |
+
# ์์ง์ ๊ฐ๊น์ด ๋ํ
|
| 2050 |
+
elevation[r, c] = hard_rock_height * (1 - t**0.5) + 10.0 * t**0.5
|
| 2051 |
|
| 2052 |
+
# ํ๋ฅ (์ฐ์์ธต - ์นจ์๋จ)
|
| 2053 |
for r in range(fall_r + cliff_width, h):
|
| 2054 |
for c in range(w):
|
| 2055 |
+
downstream_drop = (r - fall_r - cliff_width) * 0.15
|
|
|
|
| 2056 |
elevation[r, c] = 10.0 - downstream_drop
|
| 2057 |
|
| 2058 |
+
# ํ๊ณก (ํญํฌ ํํด ๊ฒฝ๋ก) - stage์ ๋ฐ๋ผ ๋ฐ๋ฌ
|
| 2059 |
gorge_start = fall_r + cliff_width
|
| 2060 |
+
gorge_end = initial_fall + 10
|
| 2061 |
+
gorge_depth = 10.0 * stage
|
| 2062 |
+
gorge_width = int(6 + 4 * stage)
|
| 2063 |
|
| 2064 |
for r in range(gorge_start, min(gorge_end, h)):
|
| 2065 |
+
for dc in range(-gorge_width, gorge_width + 1):
|
| 2066 |
c = center + dc
|
| 2067 |
if 0 <= c < w:
|
| 2068 |
# V์ ํ๊ณก ๋จ๋ฉด
|
| 2069 |
+
depth = gorge_depth * (1 - abs(dc) / gorge_width)
|
| 2070 |
elevation[r, c] -= depth
|
| 2071 |
|
| 2072 |
# ํ์ฒ ์๋ก
|
| 2073 |
+
channel_width = 4
|
| 2074 |
for r in range(h):
|
| 2075 |
+
for dc in range(-channel_width, channel_width + 1):
|
| 2076 |
c = center + dc
|
| 2077 |
if 0 <= c < w:
|
| 2078 |
+
channel_depth = 3.0 * (1 - abs(dc) / channel_width)
|
| 2079 |
+
elevation[r, c] -= channel_depth
|
| 2080 |
+
|
| 2081 |
+
# ํ๋ฐ์งํ (ํญํธ)
|
| 2082 |
+
pool_r = fall_r + cliff_width + 3
|
| 2083 |
+
pool_depth = 12.0 + 5.0 * stage
|
| 2084 |
+
pool_radius = 8
|
| 2085 |
+
|
| 2086 |
+
for dr in range(-pool_radius, pool_radius + 1):
|
| 2087 |
+
for dc in range(-pool_radius, pool_radius + 1):
|
| 2088 |
+
r_pos, c_pos = pool_r + dr, center + dc
|
| 2089 |
+
if 0 <= r_pos < h and 0 <= c_pos < w:
|
| 2090 |
dist = np.sqrt(dr**2 + dc**2)
|
| 2091 |
+
if dist < pool_radius:
|
| 2092 |
+
pool_effect = pool_depth * (1 - (dist / pool_radius)**2)
|
| 2093 |
+
elevation[r_pos, c_pos] = min(elevation[r_pos, c_pos], 5.0 - pool_effect)
|
| 2094 |
+
|
| 2095 |
+
if return_metadata:
|
| 2096 |
+
return elevation, {
|
| 2097 |
+
'waterfall_position': fall_r,
|
| 2098 |
+
'retreat_distance': retreat_distance,
|
| 2099 |
+
'gorge_length': gorge_end - gorge_start if stage > 0.3 else 0,
|
| 2100 |
+
'plunge_pool_depth': pool_depth,
|
| 2101 |
+
'layer_info': {
|
| 2102 |
+
'hard_rock': {'height': hard_rock_height, 'description': '๊ฒฝ์์ธต (์ ํญ์ฑ ๋์)'},
|
| 2103 |
+
'soft_rock': {'height': 20, 'description': '์ฐ์์ธต (์นจ์์ ์ฝํจ)'}
|
| 2104 |
+
},
|
| 2105 |
+
'stage_description': _get_waterfall_stage_desc(stage)
|
| 2106 |
+
}
|
| 2107 |
|
| 2108 |
return elevation
|
| 2109 |
|
| 2110 |
|
| 2111 |
+
def _get_waterfall_stage_desc(stage: float) -> str:
|
| 2112 |
+
"""ํญํฌ ๋จ๊ณ๋ณ ์ค๋ช
"""
|
| 2113 |
+
if stage < 0.2:
|
| 2114 |
+
return "๐๏ธ ํญํฌ ํ์ฑ: ๊ฒฝ์-์ฐ์ ๊ฒฝ๊ณ์์ ์ฐจ๋ณ์นจ์ ์์"
|
| 2115 |
+
elif stage < 0.4:
|
| 2116 |
+
return "๐ง ํ๋ฐ์งํ ๋ฐ๋ฌ: ๋ํ์ ์ถฉ๊ฒฉ์ผ๋ก ํญํธ ํ์ฑ"
|
| 2117 |
+
elif stage < 0.6:
|
| 2118 |
+
return "โ๏ธ ๋๋ถ์นจ์ ์งํ: ์ฐ์ ์ธ๋์ปทํ
โ ๊ฒฝ์ ๋ถ๊ดด"
|
| 2119 |
+
elif stage < 0.8:
|
| 2120 |
+
return "๐๏ธ ํญํฌ ํํด: ์๋ฅ๋ก ์ด๋, ํ๊ณก ์ฐ์ฅ"
|
| 2121 |
+
else:
|
| 2122 |
+
return "๐ป ์ฑ์ ํญํฌ: ๊น์ ํ๊ณก + ๋์ ํ๋ฐ์งํ"
|
| 2123 |
+
|
| 2124 |
+
|
| 2125 |
def create_karst_doline(grid_size: int = 100, stage: float = 1.0,
|
| 2126 |
num_dolines: int = 5) -> np.ndarray:
|
| 2127 |
"""๋๋ฆฌ๋ค (Doline/Sinkhole) - ์นด๋ฅด์คํธ ์งํ"""
|
pages/1_๐_Gallery.py
CHANGED
|
@@ -13,7 +13,7 @@ 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.
|
| 17 |
|
| 18 |
st.header("๐ ์ด์์ ์งํ ๊ฐค๋ฌ๋ฆฌ")
|
| 19 |
st.markdown("_๊ต๊ณผ์์ ์ธ ์งํ ํํ๋ฅผ ๊ธฐํํ์ ๋ชจ๋ธ๋ก ์๊ฐํํฉ๋๋ค._")
|
|
@@ -225,7 +225,75 @@ if landform_key in ANIMATED_LANDFORM_GENERATORS:
|
|
| 225 |
)
|
| 226 |
|
| 227 |
anim_func = ANIMATED_LANDFORM_GENERATORS[landform_key]
|
| 228 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 229 |
|
| 230 |
# ๋ฌผ ์์ฑ
|
| 231 |
stage_water = np.maximum(0, -stage_elev + 1.0)
|
|
|
|
| 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.components.renderer import render_terrain_plotly
|
| 17 |
|
| 18 |
st.header("๐ ์ด์์ ์งํ ๊ฐค๋ฌ๋ฆฌ")
|
| 19 |
st.markdown("_๊ต๊ณผ์์ ์ธ ์งํ ํํ๋ฅผ ๊ธฐํํ์ ๋ชจ๋ธ๋ก ์๊ฐํํฉ๋๋ค._")
|
|
|
|
| 225 |
)
|
| 226 |
|
| 227 |
anim_func = ANIMATED_LANDFORM_GENERATORS[landform_key]
|
| 228 |
+
|
| 229 |
+
# ๋ฉํ๋ฐ์ดํฐ ์ง์ ์งํ ํ์ธ
|
| 230 |
+
supported_metadata = [
|
| 231 |
+
'incised_meander', 'alluvial_fan', 'fjord', # ๊ธฐ์กด
|
| 232 |
+
'free_meander', 'waterfall', 'cirque', 'horn', 'coastal_cliff', # ์ ๊ท
|
| 233 |
+
'bird_foot_delta' # ์ถ๊ฐ
|
| 234 |
+
]
|
| 235 |
+
|
| 236 |
+
if landform_key in supported_metadata:
|
| 237 |
+
try:
|
| 238 |
+
stage_elev, metadata = anim_func(gallery_grid_size, stage_value, return_metadata=True)
|
| 239 |
+
# ๋จ๊ณ๋ณ ์ค๋ช
ํ์
|
| 240 |
+
st.success(metadata.get('stage_description', ''))
|
| 241 |
+
|
| 242 |
+
# ์ ์์ง ์กด ์ ๋ณด
|
| 243 |
+
if landform_key == 'alluvial_fan' and 'zone_info' in metadata:
|
| 244 |
+
with st.expander("๐ ์ธ๋ถ ๊ตฌ์กฐ ๋ณด๊ธฐ"):
|
| 245 |
+
for zone_id, info in metadata['zone_info'].items():
|
| 246 |
+
st.markdown(f"**{info['name']}**: ๊ฒฝ์ฌ {info['slope']}, {info['sediment']}")
|
| 247 |
+
|
| 248 |
+
# ํผ์ค๋ฅด๋ ํ๋ก์ธ์ค ์ ๋ณด
|
| 249 |
+
if landform_key == 'fjord' and 'process_info' in metadata:
|
| 250 |
+
with st.expander("๐ง ๋นํ ์์ฉ ๋ณด๊ธฐ"):
|
| 251 |
+
for process, desc in metadata['process_info'].items():
|
| 252 |
+
st.markdown(f"- **{process}**: {desc}")
|
| 253 |
+
|
| 254 |
+
# ์์ ๊ณก๋ฅ ์ ๋ณด
|
| 255 |
+
if landform_key == 'free_meander':
|
| 256 |
+
with st.expander("๐ ๊ณก๋ฅ ์ ๋ณด ๋ณด๊ธฐ"):
|
| 257 |
+
st.markdown(f"**์ฌํ๋**: {metadata.get('sinuosity', 1):.2f}")
|
| 258 |
+
st.markdown(f"**์ฐ๊ฐํธ ํ์ฑ**: {'โ
์' if metadata.get('oxbow_formed', False) else 'โ ์๋์ค'}")
|
| 259 |
+
|
| 260 |
+
# ํญํฌ ์ ๋ณด
|
| 261 |
+
if landform_key == 'waterfall' and 'layer_info' in metadata:
|
| 262 |
+
with st.expander("โฐ๏ธ ์ฐจ๋ณ์นจ์ ๋ณด๊ธฐ"):
|
| 263 |
+
for layer, info in metadata['layer_info'].items():
|
| 264 |
+
st.markdown(f"- **{layer}**: {info['description']}")
|
| 265 |
+
st.markdown(f"**ํํด ๊ฑฐ๋ฆฌ**: {metadata.get('retreat_distance', 0):.0f}m")
|
| 266 |
+
|
| 267 |
+
# ๊ถ๊ณก ์ ๋ณด
|
| 268 |
+
if landform_key == 'cirque':
|
| 269 |
+
with st.expander("โ๏ธ ๋นํ ์นจ์ ๋ณด๊ธฐ"):
|
| 270 |
+
st.markdown(f"**๊ถ๊ณก ๋ฐ๊ฒฝ**: {metadata.get('cirque_radius', 0)}m")
|
| 271 |
+
st.markdown(f"**ํด(ํธ์) ํ์ฑ**: {'โ
์' if metadata.get('tarn_present', False) else 'โ ์๋์ค'}")
|
| 272 |
+
|
| 273 |
+
# ํธ๋ฅธ ์ ๋ณด
|
| 274 |
+
if landform_key == 'horn':
|
| 275 |
+
with st.expander("๐ป ๋ค์ค ๊ถ๊ณก ๋ณด๊ธฐ"):
|
| 276 |
+
st.markdown(f"**๊ถ๊ณก ๊ฐ์**: {metadata.get('num_cirques', 0)}๊ฐ")
|
| 277 |
+
st.markdown(f"**์ ์ ๋์ด**: {metadata.get('peak_height', 0):.0f}m")
|
| 278 |
+
|
| 279 |
+
# ํด์์ ๋ฒฝ ์ ๋ณด
|
| 280 |
+
if landform_key == 'coastal_cliff' and 'erosion_processes' in metadata:
|
| 281 |
+
with st.expander("๐ ํ๋ ์นจ์ ๋ณด๊ธฐ"):
|
| 282 |
+
for process, desc in metadata['erosion_processes'].items():
|
| 283 |
+
st.markdown(f"- **{process}**: {desc}")
|
| 284 |
+
st.markdown(f"**ํํด๋**: {metadata.get('retreat_amount', 0)}m")
|
| 285 |
+
|
| 286 |
+
# ์กฐ์กฑ์ ์ผ๊ฐ์ฃผ ์ ๋ณด
|
| 287 |
+
if landform_key == 'bird_foot_delta':
|
| 288 |
+
with st.expander("๐ฆถ ๋ถ๋ฐฐ์๋ก ๋ณด๊ธฐ"):
|
| 289 |
+
st.markdown(f"**๋ถ๋ฐฐ์๋ก ๊ฐ์**: {metadata.get('num_distributaries', 0)}๊ฐ")
|
| 290 |
+
st.markdown(f"**์ต๋ ๊ธธ์ด**: {metadata.get('max_length', 0)}m")
|
| 291 |
+
|
| 292 |
+
except TypeError:
|
| 293 |
+
# return_metadata ์ง์ ์ ํ๋ ๊ฒฝ์ฐ
|
| 294 |
+
stage_elev = anim_func(gallery_grid_size, stage_value)
|
| 295 |
+
else:
|
| 296 |
+
stage_elev = anim_func(gallery_grid_size, stage_value)
|
| 297 |
|
| 298 |
# ๋ฌผ ์์ฑ
|
| 299 |
stage_water = np.maximum(0, -stage_elev + 1.0)
|