DiscreteMap class - mask for operations or uint8 tile data
This commit is contained in:
parent
001cc6efd6
commit
d8fec5fea0
7 changed files with 2817 additions and 0 deletions
226
tests/unit/discretemap_arithmetic_test.py
Normal file
226
tests/unit/discretemap_arithmetic_test.py
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Unit tests for DiscreteMap arithmetic and bitwise operations."""
|
||||
|
||||
import mcrfpy
|
||||
import sys
|
||||
|
||||
def test_add_scalar():
|
||||
"""Test adding a scalar value."""
|
||||
dmap = mcrfpy.DiscreteMap((10, 10), fill=50)
|
||||
dmap.add(25)
|
||||
assert dmap[5, 5] == 75, f"Expected 75, got {dmap[5, 5]}"
|
||||
|
||||
# Test saturation at 255
|
||||
dmap.fill(250)
|
||||
dmap.add(20) # 250 + 20 = 270 -> saturates to 255
|
||||
assert dmap[0, 0] == 255, f"Expected 255 (saturated), got {dmap[0, 0]}"
|
||||
|
||||
print(" [PASS] Add scalar")
|
||||
|
||||
def test_add_map():
|
||||
"""Test adding another DiscreteMap."""
|
||||
dmap1 = mcrfpy.DiscreteMap((10, 10), fill=50)
|
||||
dmap2 = mcrfpy.DiscreteMap((10, 10), fill=30)
|
||||
|
||||
dmap1.add(dmap2)
|
||||
assert dmap1[5, 5] == 80, f"Expected 80, got {dmap1[5, 5]}"
|
||||
|
||||
# Test saturation
|
||||
dmap1.fill(200)
|
||||
dmap2.fill(100)
|
||||
dmap1.add(dmap2)
|
||||
assert dmap1[0, 0] == 255, f"Expected 255 (saturated), got {dmap1[0, 0]}"
|
||||
|
||||
print(" [PASS] Add map")
|
||||
|
||||
def test_subtract_scalar():
|
||||
"""Test subtracting a scalar value."""
|
||||
dmap = mcrfpy.DiscreteMap((10, 10), fill=100)
|
||||
dmap.subtract(25)
|
||||
assert dmap[5, 5] == 75, f"Expected 75, got {dmap[5, 5]}"
|
||||
|
||||
# Test saturation at 0
|
||||
dmap.fill(10)
|
||||
dmap.subtract(20) # 10 - 20 = -10 -> saturates to 0
|
||||
assert dmap[0, 0] == 0, f"Expected 0 (saturated), got {dmap[0, 0]}"
|
||||
|
||||
print(" [PASS] Subtract scalar")
|
||||
|
||||
def test_subtract_map():
|
||||
"""Test subtracting another DiscreteMap."""
|
||||
dmap1 = mcrfpy.DiscreteMap((10, 10), fill=100)
|
||||
dmap2 = mcrfpy.DiscreteMap((10, 10), fill=30)
|
||||
|
||||
dmap1.subtract(dmap2)
|
||||
assert dmap1[5, 5] == 70, f"Expected 70, got {dmap1[5, 5]}"
|
||||
|
||||
# Test saturation
|
||||
dmap1.fill(50)
|
||||
dmap2.fill(100)
|
||||
dmap1.subtract(dmap2)
|
||||
assert dmap1[0, 0] == 0, f"Expected 0 (saturated), got {dmap1[0, 0]}"
|
||||
|
||||
print(" [PASS] Subtract map")
|
||||
|
||||
def test_multiply():
|
||||
"""Test scalar multiplication."""
|
||||
dmap = mcrfpy.DiscreteMap((10, 10), fill=50)
|
||||
dmap.multiply(2.0)
|
||||
assert dmap[5, 5] == 100, f"Expected 100, got {dmap[5, 5]}"
|
||||
|
||||
# Test saturation
|
||||
dmap.fill(100)
|
||||
dmap.multiply(3.0) # 100 * 3 = 300 -> saturates to 255
|
||||
assert dmap[0, 0] == 255, f"Expected 255 (saturated), got {dmap[0, 0]}"
|
||||
|
||||
# Test fractional
|
||||
dmap.fill(100)
|
||||
dmap.multiply(0.5)
|
||||
assert dmap[0, 0] == 50, f"Expected 50, got {dmap[0, 0]}"
|
||||
|
||||
print(" [PASS] Multiply")
|
||||
|
||||
def test_copy_from():
|
||||
"""Test copy_from operation."""
|
||||
dmap1 = mcrfpy.DiscreteMap((10, 10), fill=0)
|
||||
dmap2 = mcrfpy.DiscreteMap((5, 5), fill=99)
|
||||
|
||||
dmap1.copy_from(dmap2, pos=(2, 2))
|
||||
|
||||
assert dmap1[2, 2] == 99, f"Expected 99 at (2,2), got {dmap1[2, 2]}"
|
||||
assert dmap1[6, 6] == 99, f"Expected 99 at (6,6), got {dmap1[6, 6]}"
|
||||
assert dmap1[0, 0] == 0, f"Expected 0 at (0,0), got {dmap1[0, 0]}"
|
||||
assert dmap1[7, 7] == 0, f"Expected 0 at (7,7), got {dmap1[7, 7]}"
|
||||
|
||||
print(" [PASS] Copy from")
|
||||
|
||||
def test_max():
|
||||
"""Test element-wise max."""
|
||||
dmap1 = mcrfpy.DiscreteMap((10, 10), fill=50)
|
||||
dmap2 = mcrfpy.DiscreteMap((10, 10), fill=70)
|
||||
|
||||
# Set some values in dmap1 higher
|
||||
dmap1[3, 3] = 100
|
||||
|
||||
dmap1.max(dmap2)
|
||||
|
||||
assert dmap1[0, 0] == 70, f"Expected 70 at (0,0), got {dmap1[0, 0]}"
|
||||
assert dmap1[3, 3] == 100, f"Expected 100 at (3,3), got {dmap1[3, 3]}"
|
||||
|
||||
print(" [PASS] Max")
|
||||
|
||||
def test_min():
|
||||
"""Test element-wise min."""
|
||||
dmap1 = mcrfpy.DiscreteMap((10, 10), fill=50)
|
||||
dmap2 = mcrfpy.DiscreteMap((10, 10), fill=30)
|
||||
|
||||
# Set some values in dmap1 lower
|
||||
dmap1[3, 3] = 10
|
||||
|
||||
dmap1.min(dmap2)
|
||||
|
||||
assert dmap1[0, 0] == 30, f"Expected 30 at (0,0), got {dmap1[0, 0]}"
|
||||
assert dmap1[3, 3] == 10, f"Expected 10 at (3,3), got {dmap1[3, 3]}"
|
||||
|
||||
print(" [PASS] Min")
|
||||
|
||||
def test_bitwise_and():
|
||||
"""Test bitwise AND."""
|
||||
dmap1 = mcrfpy.DiscreteMap((10, 10), fill=0xFF) # 11111111
|
||||
dmap2 = mcrfpy.DiscreteMap((10, 10), fill=0x0F) # 00001111
|
||||
|
||||
dmap1.bitwise_and(dmap2)
|
||||
assert dmap1[0, 0] == 0x0F, f"Expected 0x0F, got {hex(dmap1[0, 0])}"
|
||||
|
||||
# Test specific pattern
|
||||
dmap1.fill(0b10101010)
|
||||
dmap2.fill(0b11110000)
|
||||
dmap1.bitwise_and(dmap2)
|
||||
assert dmap1[0, 0] == 0b10100000, f"Expected 0b10100000, got {bin(dmap1[0, 0])}"
|
||||
|
||||
print(" [PASS] Bitwise AND")
|
||||
|
||||
def test_bitwise_or():
|
||||
"""Test bitwise OR."""
|
||||
dmap1 = mcrfpy.DiscreteMap((10, 10), fill=0x0F) # 00001111
|
||||
dmap2 = mcrfpy.DiscreteMap((10, 10), fill=0xF0) # 11110000
|
||||
|
||||
dmap1.bitwise_or(dmap2)
|
||||
assert dmap1[0, 0] == 0xFF, f"Expected 0xFF, got {hex(dmap1[0, 0])}"
|
||||
|
||||
print(" [PASS] Bitwise OR")
|
||||
|
||||
def test_bitwise_xor():
|
||||
"""Test bitwise XOR."""
|
||||
dmap1 = mcrfpy.DiscreteMap((10, 10), fill=0xFF)
|
||||
dmap2 = mcrfpy.DiscreteMap((10, 10), fill=0xFF)
|
||||
|
||||
dmap1.bitwise_xor(dmap2)
|
||||
assert dmap1[0, 0] == 0x00, f"Expected 0x00, got {hex(dmap1[0, 0])}"
|
||||
|
||||
dmap1.fill(0b10101010)
|
||||
dmap2.fill(0b11110000)
|
||||
dmap1.bitwise_xor(dmap2)
|
||||
assert dmap1[0, 0] == 0b01011010, f"Expected 0b01011010, got {bin(dmap1[0, 0])}"
|
||||
|
||||
print(" [PASS] Bitwise XOR")
|
||||
|
||||
def test_invert():
|
||||
"""Test invert (returns new map)."""
|
||||
dmap = mcrfpy.DiscreteMap((10, 10), fill=100)
|
||||
result = dmap.invert()
|
||||
|
||||
# Original unchanged
|
||||
assert dmap[0, 0] == 100, f"Original should be unchanged, got {dmap[0, 0]}"
|
||||
|
||||
# Result is inverted
|
||||
assert result[0, 0] == 155, f"Expected 155 (255-100), got {result[0, 0]}"
|
||||
|
||||
# Test edge cases
|
||||
dmap.fill(0)
|
||||
result = dmap.invert()
|
||||
assert result[0, 0] == 255, f"Expected 255, got {result[0, 0]}"
|
||||
|
||||
dmap.fill(255)
|
||||
result = dmap.invert()
|
||||
assert result[0, 0] == 0, f"Expected 0, got {result[0, 0]}"
|
||||
|
||||
print(" [PASS] Invert")
|
||||
|
||||
def test_region_operations():
|
||||
"""Test operations with region parameters."""
|
||||
dmap1 = mcrfpy.DiscreteMap((20, 20), fill=10)
|
||||
dmap2 = mcrfpy.DiscreteMap((20, 20), fill=5)
|
||||
|
||||
# Add only in a region
|
||||
dmap1.add(dmap2, pos=(5, 5), source_pos=(0, 0), size=(5, 5))
|
||||
|
||||
assert dmap1[5, 5] == 15, f"Expected 15 in region, got {dmap1[5, 5]}"
|
||||
assert dmap1[9, 9] == 15, f"Expected 15 in region, got {dmap1[9, 9]}"
|
||||
assert dmap1[0, 0] == 10, f"Expected 10 outside region, got {dmap1[0, 0]}"
|
||||
assert dmap1[10, 10] == 10, f"Expected 10 outside region, got {dmap1[10, 10]}"
|
||||
|
||||
print(" [PASS] Region operations")
|
||||
|
||||
def main():
|
||||
print("Running DiscreteMap arithmetic tests...")
|
||||
|
||||
test_add_scalar()
|
||||
test_add_map()
|
||||
test_subtract_scalar()
|
||||
test_subtract_map()
|
||||
test_multiply()
|
||||
test_copy_from()
|
||||
test_max()
|
||||
test_min()
|
||||
test_bitwise_and()
|
||||
test_bitwise_or()
|
||||
test_bitwise_xor()
|
||||
test_invert()
|
||||
test_region_operations()
|
||||
|
||||
print("All DiscreteMap arithmetic tests PASSED!")
|
||||
sys.exit(0)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
173
tests/unit/discretemap_basic_test.py
Normal file
173
tests/unit/discretemap_basic_test.py
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Unit tests for DiscreteMap basic operations."""
|
||||
|
||||
import mcrfpy
|
||||
import sys
|
||||
|
||||
def test_construction():
|
||||
"""Test basic construction."""
|
||||
# Default construction
|
||||
dmap = mcrfpy.DiscreteMap((100, 100))
|
||||
assert dmap.size == (100, 100), f"Expected (100, 100), got {dmap.size}"
|
||||
|
||||
# With fill value
|
||||
dmap2 = mcrfpy.DiscreteMap((50, 50), fill=42)
|
||||
assert dmap2[0, 0] == 42, f"Expected 42, got {dmap2[0, 0]}"
|
||||
assert dmap2[25, 25] == 42, f"Expected 42, got {dmap2[25, 25]}"
|
||||
|
||||
print(" [PASS] Construction")
|
||||
|
||||
def test_size_property():
|
||||
"""Test size property."""
|
||||
dmap = mcrfpy.DiscreteMap((123, 456))
|
||||
w, h = dmap.size
|
||||
assert w == 123, f"Expected width 123, got {w}"
|
||||
assert h == 456, f"Expected height 456, got {h}"
|
||||
print(" [PASS] Size property")
|
||||
|
||||
def test_get_set():
|
||||
"""Test get/set methods."""
|
||||
dmap = mcrfpy.DiscreteMap((10, 10))
|
||||
|
||||
# Test set/get
|
||||
dmap.set(5, 5, 100)
|
||||
assert dmap.get(5, 5) == 100, f"Expected 100, got {dmap.get(5, 5)}"
|
||||
|
||||
# Test subscript
|
||||
dmap[3, 7] = 200
|
||||
assert dmap[3, 7] == 200, f"Expected 200, got {dmap[3, 7]}"
|
||||
|
||||
# Test tuple subscript
|
||||
dmap[(1, 2)] = 150
|
||||
assert dmap[(1, 2)] == 150, f"Expected 150, got {dmap[(1, 2)]}"
|
||||
|
||||
print(" [PASS] Get/set methods")
|
||||
|
||||
def test_bounds_checking():
|
||||
"""Test that out-of-bounds access raises IndexError."""
|
||||
dmap = mcrfpy.DiscreteMap((10, 10))
|
||||
|
||||
# Test out of bounds get
|
||||
try:
|
||||
_ = dmap[10, 10]
|
||||
print(" [FAIL] Should have raised IndexError for (10, 10)")
|
||||
return False
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
try:
|
||||
_ = dmap[-1, 0]
|
||||
print(" [FAIL] Should have raised IndexError for (-1, 0)")
|
||||
return False
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
# Test out of bounds set
|
||||
try:
|
||||
dmap[100, 100] = 5
|
||||
print(" [FAIL] Should have raised IndexError for set")
|
||||
return False
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
print(" [PASS] Bounds checking")
|
||||
return True
|
||||
|
||||
def test_value_range():
|
||||
"""Test that values are clamped to 0-255."""
|
||||
dmap = mcrfpy.DiscreteMap((10, 10))
|
||||
|
||||
# Test valid range
|
||||
dmap[0, 0] = 0
|
||||
dmap[0, 1] = 255
|
||||
assert dmap[0, 0] == 0
|
||||
assert dmap[0, 1] == 255
|
||||
|
||||
# Test invalid values
|
||||
try:
|
||||
dmap[0, 0] = -1
|
||||
print(" [FAIL] Should have raised ValueError for -1")
|
||||
return False
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
try:
|
||||
dmap[0, 0] = 256
|
||||
print(" [FAIL] Should have raised ValueError for 256")
|
||||
return False
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
print(" [PASS] Value range")
|
||||
return True
|
||||
|
||||
def test_fill():
|
||||
"""Test fill operation."""
|
||||
dmap = mcrfpy.DiscreteMap((10, 10))
|
||||
|
||||
# Fill entire map
|
||||
dmap.fill(77)
|
||||
for y in range(10):
|
||||
for x in range(10):
|
||||
assert dmap[x, y] == 77, f"Expected 77 at ({x}, {y}), got {dmap[x, y]}"
|
||||
|
||||
# Fill region
|
||||
dmap.fill(88, pos=(2, 2), size=(3, 3))
|
||||
assert dmap[2, 2] == 88, "Region fill failed at start"
|
||||
assert dmap[4, 4] == 88, "Region fill failed at end"
|
||||
assert dmap[1, 1] == 77, "Region fill affected outside area"
|
||||
assert dmap[5, 5] == 77, "Region fill affected outside area"
|
||||
|
||||
print(" [PASS] Fill operation")
|
||||
|
||||
def test_clear():
|
||||
"""Test clear operation."""
|
||||
dmap = mcrfpy.DiscreteMap((10, 10), fill=100)
|
||||
dmap.clear()
|
||||
|
||||
for y in range(10):
|
||||
for x in range(10):
|
||||
assert dmap[x, y] == 0, f"Expected 0 at ({x}, {y}), got {dmap[x, y]}"
|
||||
|
||||
print(" [PASS] Clear operation")
|
||||
|
||||
def test_repr():
|
||||
"""Test repr output."""
|
||||
dmap = mcrfpy.DiscreteMap((100, 50))
|
||||
r = repr(dmap)
|
||||
assert "DiscreteMap" in r, f"Expected 'DiscreteMap' in repr, got {r}"
|
||||
assert "100" in r, f"Expected '100' in repr, got {r}"
|
||||
assert "50" in r, f"Expected '50' in repr, got {r}"
|
||||
print(" [PASS] Repr")
|
||||
|
||||
def test_chaining():
|
||||
"""Test method chaining."""
|
||||
dmap = mcrfpy.DiscreteMap((10, 10))
|
||||
|
||||
# Methods should return self
|
||||
result = dmap.fill(50).clear().fill(100)
|
||||
assert result is dmap, "Method chaining should return self"
|
||||
assert dmap[5, 5] == 100, "Chained operations should work"
|
||||
|
||||
print(" [PASS] Method chaining")
|
||||
|
||||
def main():
|
||||
print("Running DiscreteMap basic tests...")
|
||||
|
||||
test_construction()
|
||||
test_size_property()
|
||||
test_get_set()
|
||||
if not test_bounds_checking():
|
||||
sys.exit(1)
|
||||
if not test_value_range():
|
||||
sys.exit(1)
|
||||
test_fill()
|
||||
test_clear()
|
||||
test_repr()
|
||||
test_chaining()
|
||||
|
||||
print("All DiscreteMap basic tests PASSED!")
|
||||
sys.exit(0)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
280
tests/unit/discretemap_heightmap_test.py
Normal file
280
tests/unit/discretemap_heightmap_test.py
Normal file
|
|
@ -0,0 +1,280 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Unit tests for DiscreteMap <-> HeightMap integration."""
|
||||
|
||||
import mcrfpy
|
||||
import sys
|
||||
from enum import IntEnum
|
||||
|
||||
class Terrain(IntEnum):
|
||||
WATER = 0
|
||||
SAND = 1
|
||||
GRASS = 2
|
||||
FOREST = 3
|
||||
MOUNTAIN = 4
|
||||
|
||||
def test_from_heightmap_basic():
|
||||
"""Test basic HeightMap to DiscreteMap conversion."""
|
||||
# Create a simple heightmap
|
||||
hmap = mcrfpy.HeightMap((10, 10), fill=0.5)
|
||||
|
||||
# Create a simple mapping
|
||||
mapping = [
|
||||
((0.0, 0.3), 0),
|
||||
((0.3, 0.6), 1),
|
||||
((0.6, 1.0), 2),
|
||||
]
|
||||
|
||||
dmap = mcrfpy.DiscreteMap.from_heightmap(hmap, mapping)
|
||||
|
||||
# 0.5 should map to category 1
|
||||
assert dmap[5, 5] == 1, f"Expected 1, got {dmap[5, 5]}"
|
||||
|
||||
print(" [PASS] from_heightmap basic")
|
||||
|
||||
def test_from_heightmap_full_range():
|
||||
"""Test conversion with values spanning the full range."""
|
||||
hmap = mcrfpy.HeightMap((100, 1))
|
||||
|
||||
# Create gradient
|
||||
for x in range(100):
|
||||
hmap[x, 0] = x / 100.0 # 0.0 to 0.99
|
||||
|
||||
mapping = [
|
||||
((0.0, 0.25), Terrain.WATER),
|
||||
((0.25, 0.5), Terrain.SAND),
|
||||
((0.5, 0.75), Terrain.GRASS),
|
||||
((0.75, 1.0), Terrain.FOREST),
|
||||
]
|
||||
|
||||
dmap = mcrfpy.DiscreteMap.from_heightmap(hmap, mapping)
|
||||
|
||||
# Check values at key positions
|
||||
assert dmap[10, 0] == Terrain.WATER, f"Expected WATER at 10, got {dmap[10, 0]}"
|
||||
assert dmap[30, 0] == Terrain.SAND, f"Expected SAND at 30, got {dmap[30, 0]}"
|
||||
assert dmap[60, 0] == Terrain.GRASS, f"Expected GRASS at 60, got {dmap[60, 0]}"
|
||||
assert dmap[80, 0] == Terrain.FOREST, f"Expected FOREST at 80, got {dmap[80, 0]}"
|
||||
|
||||
print(" [PASS] from_heightmap full range")
|
||||
|
||||
def test_from_heightmap_with_enum():
|
||||
"""Test from_heightmap with enum parameter."""
|
||||
hmap = mcrfpy.HeightMap((10, 10), fill=0.5)
|
||||
|
||||
mapping = [
|
||||
((0.0, 0.3), Terrain.WATER),
|
||||
((0.3, 0.7), Terrain.GRASS),
|
||||
((0.7, 1.0), Terrain.MOUNTAIN),
|
||||
]
|
||||
|
||||
dmap = mcrfpy.DiscreteMap.from_heightmap(hmap, mapping, enum=Terrain)
|
||||
|
||||
# Value should be returned as enum member
|
||||
val = dmap[5, 5]
|
||||
assert val == Terrain.GRASS, f"Expected Terrain.GRASS, got {val}"
|
||||
assert isinstance(val, Terrain), f"Expected Terrain type, got {type(val)}"
|
||||
|
||||
print(" [PASS] from_heightmap with enum")
|
||||
|
||||
def test_to_heightmap_basic():
|
||||
"""Test basic DiscreteMap to HeightMap conversion."""
|
||||
dmap = mcrfpy.DiscreteMap((10, 10), fill=100)
|
||||
|
||||
hmap = dmap.to_heightmap()
|
||||
|
||||
# Direct conversion: uint8 -> float
|
||||
assert abs(hmap[5, 5] - 100.0) < 0.001, f"Expected 100.0, got {hmap[5, 5]}"
|
||||
|
||||
print(" [PASS] to_heightmap basic")
|
||||
|
||||
def test_to_heightmap_with_mapping():
|
||||
"""Test to_heightmap with value mapping."""
|
||||
dmap = mcrfpy.DiscreteMap((10, 10))
|
||||
|
||||
# Create pattern
|
||||
dmap.fill(0, pos=(0, 0), size=(5, 10)) # Left half = 0
|
||||
dmap.fill(1, pos=(5, 0), size=(5, 10)) # Right half = 1
|
||||
|
||||
# Map discrete values to heights
|
||||
mapping = {
|
||||
0: 0.2,
|
||||
1: 0.8,
|
||||
}
|
||||
|
||||
hmap = dmap.to_heightmap(mapping)
|
||||
|
||||
assert abs(hmap[2, 5] - 0.2) < 0.001, f"Expected 0.2, got {hmap[2, 5]}"
|
||||
assert abs(hmap[7, 5] - 0.8) < 0.001, f"Expected 0.8, got {hmap[7, 5]}"
|
||||
|
||||
print(" [PASS] to_heightmap with mapping")
|
||||
|
||||
def test_roundtrip():
|
||||
"""Test HeightMap -> DiscreteMap -> HeightMap roundtrip."""
|
||||
# Create original heightmap
|
||||
original = mcrfpy.HeightMap((50, 50))
|
||||
for y in range(50):
|
||||
for x in range(50):
|
||||
original[x, y] = (x + y) / 100.0 # Gradient 0.0 to 0.98
|
||||
|
||||
# Convert to discrete with specific ranges
|
||||
mapping = [
|
||||
((0.0, 0.33), 0),
|
||||
((0.33, 0.66), 1),
|
||||
((0.66, 1.0), 2),
|
||||
]
|
||||
dmap = mcrfpy.DiscreteMap.from_heightmap(original, mapping)
|
||||
|
||||
# Convert back with value mapping
|
||||
reverse_mapping = {
|
||||
0: 0.15, # Midpoint of first range
|
||||
1: 0.5, # Midpoint of second range
|
||||
2: 0.85, # Midpoint of third range
|
||||
}
|
||||
restored = dmap.to_heightmap(reverse_mapping)
|
||||
|
||||
# Verify approximate restoration
|
||||
assert abs(restored[0, 0] - 0.15) < 0.01, f"Expected ~0.15 at (0,0), got {restored[0, 0]}"
|
||||
assert abs(restored[25, 25] - 0.5) < 0.01, f"Expected ~0.5 at (25,25), got {restored[25, 25]}"
|
||||
|
||||
print(" [PASS] Roundtrip conversion")
|
||||
|
||||
def test_query_methods():
|
||||
"""Test count, count_range, min_max, histogram."""
|
||||
dmap = mcrfpy.DiscreteMap((10, 10))
|
||||
|
||||
# Create pattern with different values
|
||||
dmap.fill(0, pos=(0, 0), size=(5, 5)) # 25 cells with 0
|
||||
dmap.fill(1, pos=(5, 0), size=(5, 5)) # 25 cells with 1
|
||||
dmap.fill(2, pos=(0, 5), size=(5, 5)) # 25 cells with 2
|
||||
dmap.fill(3, pos=(5, 5), size=(5, 5)) # 25 cells with 3
|
||||
|
||||
# Test count
|
||||
assert dmap.count(0) == 25, f"Expected 25 zeros, got {dmap.count(0)}"
|
||||
assert dmap.count(1) == 25, f"Expected 25 ones, got {dmap.count(1)}"
|
||||
assert dmap.count(4) == 0, f"Expected 0 fours, got {dmap.count(4)}"
|
||||
|
||||
# Test count_range
|
||||
assert dmap.count_range(0, 1) == 50, f"Expected 50 in range 0-1, got {dmap.count_range(0, 1)}"
|
||||
assert dmap.count_range(0, 3) == 100, f"Expected 100 in range 0-3, got {dmap.count_range(0, 3)}"
|
||||
|
||||
# Test min_max
|
||||
min_val, max_val = dmap.min_max()
|
||||
assert min_val == 0, f"Expected min 0, got {min_val}"
|
||||
assert max_val == 3, f"Expected max 3, got {max_val}"
|
||||
|
||||
# Test histogram
|
||||
hist = dmap.histogram()
|
||||
assert hist[0] == 25, f"Expected 25 for value 0, got {hist.get(0)}"
|
||||
assert hist[1] == 25, f"Expected 25 for value 1, got {hist.get(1)}"
|
||||
assert hist[2] == 25, f"Expected 25 for value 2, got {hist.get(2)}"
|
||||
assert hist[3] == 25, f"Expected 25 for value 3, got {hist.get(3)}"
|
||||
assert 4 not in hist, "Value 4 should not be in histogram"
|
||||
|
||||
print(" [PASS] Query methods")
|
||||
|
||||
def test_bool_int():
|
||||
"""Test bool() with integer condition."""
|
||||
dmap = mcrfpy.DiscreteMap((10, 10), fill=0)
|
||||
dmap.fill(1, pos=(2, 2), size=(3, 3))
|
||||
|
||||
mask = dmap.bool(1)
|
||||
|
||||
# Should be 1 where original is 1, 0 elsewhere
|
||||
assert mask[0, 0] == 0, f"Expected 0 outside region, got {mask[0, 0]}"
|
||||
assert mask[3, 3] == 1, f"Expected 1 inside region, got {mask[3, 3]}"
|
||||
assert mask.count(1) == 9, f"Expected 9 ones, got {mask.count(1)}"
|
||||
|
||||
print(" [PASS] bool() with int")
|
||||
|
||||
def test_bool_set():
|
||||
"""Test bool() with set condition."""
|
||||
dmap = mcrfpy.DiscreteMap((10, 10))
|
||||
dmap.fill(0, pos=(0, 0), size=(5, 5))
|
||||
dmap.fill(1, pos=(5, 0), size=(5, 5))
|
||||
dmap.fill(2, pos=(0, 5), size=(5, 5))
|
||||
dmap.fill(3, pos=(5, 5), size=(5, 5))
|
||||
|
||||
# Match 0 or 2
|
||||
mask = dmap.bool({0, 2})
|
||||
|
||||
assert mask[2, 2] == 1, "Expected 1 where value is 0"
|
||||
assert mask[7, 2] == 0, "Expected 0 where value is 1"
|
||||
assert mask[2, 7] == 1, "Expected 1 where value is 2"
|
||||
assert mask[7, 7] == 0, "Expected 0 where value is 3"
|
||||
assert mask.count(1) == 50, f"Expected 50 ones, got {mask.count(1)}"
|
||||
|
||||
print(" [PASS] bool() with set")
|
||||
|
||||
def test_bool_callable():
|
||||
"""Test bool() with callable condition."""
|
||||
dmap = mcrfpy.DiscreteMap((10, 10), fill=0)
|
||||
for y in range(10):
|
||||
for x in range(10):
|
||||
dmap[x, y] = x + y # Values 0-18
|
||||
|
||||
# Match where value > 10
|
||||
mask = dmap.bool(lambda v: v > 10)
|
||||
|
||||
assert mask[5, 5] == 0, "Expected 0 where value is 10"
|
||||
assert mask[6, 6] == 1, "Expected 1 where value is 12"
|
||||
assert mask[9, 9] == 1, "Expected 1 where value is 18"
|
||||
|
||||
print(" [PASS] bool() with callable")
|
||||
|
||||
def test_mask_memoryview():
|
||||
"""Test mask() returns working memoryview."""
|
||||
dmap = mcrfpy.DiscreteMap((10, 10), fill=42)
|
||||
|
||||
mv = dmap.mask()
|
||||
|
||||
assert len(mv) == 100, f"Expected 100 bytes, got {len(mv)}"
|
||||
assert mv[0] == 42, f"Expected 42, got {mv[0]}"
|
||||
|
||||
# Test writing through memoryview
|
||||
mv[50] = 99
|
||||
assert dmap[0, 5] == 99, f"Expected 99, got {dmap[0, 5]}"
|
||||
|
||||
print(" [PASS] mask() memoryview")
|
||||
|
||||
def test_enum_type_property():
|
||||
"""Test enum_type property getter/setter."""
|
||||
dmap = mcrfpy.DiscreteMap((10, 10), fill=1)
|
||||
|
||||
# Initially no enum
|
||||
assert dmap.enum_type is None, "Expected None initially"
|
||||
|
||||
# Set enum type
|
||||
dmap.enum_type = Terrain
|
||||
assert dmap.enum_type is Terrain, "Expected Terrain enum"
|
||||
|
||||
# Value should now return enum member
|
||||
val = dmap[5, 5]
|
||||
assert val == Terrain.SAND, f"Expected Terrain.SAND, got {val}"
|
||||
|
||||
# Clear enum type
|
||||
dmap.enum_type = None
|
||||
val = dmap[5, 5]
|
||||
assert isinstance(val, int), f"Expected int after clearing enum, got {type(val)}"
|
||||
|
||||
print(" [PASS] enum_type property")
|
||||
|
||||
def main():
|
||||
print("Running DiscreteMap HeightMap integration tests...")
|
||||
|
||||
test_from_heightmap_basic()
|
||||
test_from_heightmap_full_range()
|
||||
test_from_heightmap_with_enum()
|
||||
test_to_heightmap_basic()
|
||||
test_to_heightmap_with_mapping()
|
||||
test_roundtrip()
|
||||
test_query_methods()
|
||||
test_bool_int()
|
||||
test_bool_set()
|
||||
test_bool_callable()
|
||||
test_mask_memoryview()
|
||||
test_enum_type_property()
|
||||
|
||||
print("All DiscreteMap HeightMap integration tests PASSED!")
|
||||
sys.exit(0)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue