Cookbook: draft docs
This commit is contained in:
parent
8628ac164b
commit
73230989ad
42 changed files with 3352 additions and 0 deletions
148
docs/cookbook/procgen/08_heightmap_thresholds.py
Normal file
148
docs/cookbook/procgen/08_heightmap_thresholds.py
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
"""HeightMap Thresholds and ColorLayer Integration Demo
|
||||
|
||||
Demonstrates: threshold, threshold_binary, inverse, count_in_range
|
||||
Also: ColorLayer.apply_ranges for multi-threshold coloring
|
||||
Shows terrain classification and visualization techniques.
|
||||
"""
|
||||
import mcrfpy
|
||||
from mcrfpy import automation
|
||||
|
||||
GRID_WIDTH, GRID_HEIGHT = 64, 48
|
||||
CELL_SIZE = 16
|
||||
|
||||
def run_demo(runtime):
|
||||
panel_w = GRID_WIDTH // 3
|
||||
panel_h = GRID_HEIGHT // 2
|
||||
|
||||
# Create source terrain
|
||||
noise = mcrfpy.NoiseSource(dimensions=2, algorithm='simplex', seed=42)
|
||||
source = mcrfpy.HeightMap((panel_w, panel_h), fill=0.0)
|
||||
source.add_noise(noise, world_size=(10, 10), mode='fbm', octaves=5)
|
||||
source.add_hill((panel_w // 2, panel_h // 2), 8, 0.3)
|
||||
source.normalize(0.0, 1.0)
|
||||
|
||||
# Create derived heightmaps
|
||||
water_mask = source.threshold((0.0, 0.3)) # Returns NEW heightmap with values only in range
|
||||
land_binary = source.threshold_binary((0.3, 1.0), value=1.0) # Binary mask
|
||||
inverted = source.inverse() # Inverted values
|
||||
|
||||
# Count cells in ranges for classification stats
|
||||
water_count = source.count_in_range((0.0, 0.3))
|
||||
land_count = source.count_in_range((0.3, 0.7))
|
||||
mountain_count = source.count_in_range((0.7, 1.0))
|
||||
|
||||
# IMPORTANT: Render apply_ranges FIRST since it affects the whole layer
|
||||
# Panel 6: Using ColorLayer.apply_ranges (bottom-right)
|
||||
# Create a full-size heightmap and copy source data to correct position
|
||||
panel6_hmap = mcrfpy.HeightMap((GRID_WIDTH, GRID_HEIGHT), fill=-1.0) # -1 won't match any range
|
||||
for y in range(panel_h):
|
||||
for x in range(panel_w):
|
||||
h = source.get((x, y))
|
||||
panel6_hmap.fill(h, pos=(panel_w * 2 + x, panel_h + y), size=(1, 1))
|
||||
|
||||
# apply_ranges colors cells based on height ranges
|
||||
# Cells with -1.0 won't match any range and stay unchanged
|
||||
color_layer.apply_ranges(panel6_hmap, [
|
||||
((0.0, 0.2), (30, 80, 160)), # Deep water
|
||||
((0.2, 0.3), ((60, 120, 180), (120, 160, 140))), # Gradient: shallow to shore
|
||||
((0.3, 0.5), (80, 150, 60)), # Lowland
|
||||
((0.5, 0.7), ((60, 120, 40), (100, 100, 80))), # Gradient: forest to hills
|
||||
((0.7, 0.85), (130, 120, 110)), # Rock
|
||||
((0.85, 1.0), ((180, 180, 190), (250, 250, 255))), # Gradient: rock to snow
|
||||
])
|
||||
|
||||
# Now render the other 5 panels (they will overwrite only their regions)
|
||||
|
||||
# Panel 1 (top-left): Original grayscale
|
||||
for y in range(panel_h):
|
||||
for x in range(panel_w):
|
||||
h = source.get((x, y))
|
||||
v = int(h * 255)
|
||||
color_layer.set(((x, y)), mcrfpy.Color(v, v, v))
|
||||
|
||||
# Panel 2 (top-middle): threshold() - shows only values in range 0.0-0.3
|
||||
for y in range(panel_h):
|
||||
for x in range(panel_w):
|
||||
h = water_mask.get((x, y))
|
||||
if h > 0:
|
||||
# Values were preserved in 0.0-0.3 range
|
||||
t = h / 0.3
|
||||
color_layer.set(((panel_w + x, y)), mcrfpy.Color(
|
||||
int(30 + t * 40), int(60 + t * 60), int(150 + t * 50)))
|
||||
else:
|
||||
# Outside threshold range - dark
|
||||
color_layer.set(((panel_w + x, y)), mcrfpy.Color(20, 20, 30))
|
||||
|
||||
# Panel 3 (top-right): threshold_binary() - land mask
|
||||
for y in range(panel_h):
|
||||
for x in range(panel_w):
|
||||
h = land_binary.get((x, y))
|
||||
if h > 0:
|
||||
color_layer.set(((panel_w * 2 + x, y)), mcrfpy.Color(80, 140, 60)) # Land
|
||||
else:
|
||||
color_layer.set(((panel_w * 2 + x, y)), mcrfpy.Color(40, 80, 150)) # Water
|
||||
|
||||
# Panel 4 (bottom-left): inverse()
|
||||
for y in range(panel_h):
|
||||
for x in range(panel_w):
|
||||
h = inverted.get((x, y))
|
||||
v = int(h * 255)
|
||||
color_layer.set(((x, panel_h + y)), mcrfpy.Color(v, int(v * 0.8), int(v * 0.6)))
|
||||
|
||||
# Panel 5 (bottom-middle): Classification using count_in_range results
|
||||
for y in range(panel_h):
|
||||
for x in range(panel_w):
|
||||
h = source.get((x, y))
|
||||
if h < 0.3:
|
||||
color_layer.set(((panel_w + x, panel_h + y)), mcrfpy.Color(50, 100, 180)) # Water
|
||||
elif h < 0.7:
|
||||
color_layer.set(((panel_w + x, panel_h + y)), mcrfpy.Color(70, 140, 50)) # Land
|
||||
else:
|
||||
color_layer.set(((panel_w + x, panel_h + y)), mcrfpy.Color(140, 130, 120)) # Mountain
|
||||
|
||||
# Labels
|
||||
labels = [
|
||||
("Original (grayscale)", 5, 5),
|
||||
("threshold(0-0.3)", panel_w * CELL_SIZE + 5, 5),
|
||||
("threshold_binary(land)", panel_w * 2 * CELL_SIZE + 5, 5),
|
||||
("inverse()", 5, panel_h * CELL_SIZE + 5),
|
||||
(f"Classified (W:{water_count} L:{land_count} M:{mountain_count})", panel_w * CELL_SIZE + 5, panel_h * CELL_SIZE + 5),
|
||||
("apply_ranges (biome)", panel_w * 2 * CELL_SIZE + 5, panel_h * CELL_SIZE + 5),
|
||||
]
|
||||
|
||||
for text, x, y in labels:
|
||||
label = mcrfpy.Caption(text=text, pos=(x, y))
|
||||
label.fill_color = mcrfpy.Color(255, 255, 255)
|
||||
label.outline = 1
|
||||
label.outline_color = mcrfpy.Color(0, 0, 0)
|
||||
scene.children.append(label)
|
||||
|
||||
# Grid divider lines
|
||||
for y in range(GRID_HEIGHT):
|
||||
color_layer.set(((panel_w - 1, y)), mcrfpy.Color(80, 80, 80))
|
||||
color_layer.set(((panel_w * 2 - 1, y)), mcrfpy.Color(80, 80, 80))
|
||||
for x in range(GRID_WIDTH):
|
||||
color_layer.set(((x, panel_h - 1)), mcrfpy.Color(80, 80, 80))
|
||||
|
||||
|
||||
# Setup
|
||||
scene = mcrfpy.Scene("thresholds_demo")
|
||||
|
||||
grid = mcrfpy.Grid(
|
||||
grid_size=(GRID_WIDTH, GRID_HEIGHT),
|
||||
pos=(0, 0),
|
||||
size=(GRID_WIDTH * CELL_SIZE, GRID_HEIGHT * CELL_SIZE),
|
||||
layers={}
|
||||
)
|
||||
grid.fill_color = mcrfpy.Color(0, 0, 0)
|
||||
color_layer = grid.add_layer("color", z_index=-1)
|
||||
scene.children.append(grid)
|
||||
|
||||
scene.activate()
|
||||
|
||||
# Run the demo
|
||||
run_demo(0)
|
||||
|
||||
# Take screenshot
|
||||
automation.screenshot("procgen_08_heightmap_thresholds.png")
|
||||
print("Screenshot saved: procgen_08_heightmap_thresholds.png")
|
||||
Loading…
Add table
Add a link
Reference in a new issue