REMB / algorithms /backend /test_aesthetic.py
Cuong2004's picture
Beauti mode
3bd69a8
"""Tests for aesthetic optimization functions."""
import sys
import traceback
def test_shape_quality():
"""Test shape quality analysis functions."""
print("Testing shape quality functions...")
try:
from shapely.geometry import Polygon
from core.geometry.shape_quality import (
analyze_shape_quality,
get_dominant_edge_vector,
classify_lot_type,
get_obb_dimensions
)
# Test 1: Perfect rectangle should have high score and be valid
# Must be > 1000 m² to pass min area check
rect = Polygon([(0, 0), (50, 0), (50, 40), (0, 40)]) # 2000 m²
score, valid = analyze_shape_quality(rect)
print(f" Rectangle (2000m²): score={score:.3f}, valid={valid}")
assert valid, "Rectangle should be valid"
assert score > 0.8, "Rectangle should have high score"
# Test 2: Triangle should have low rectangularity and be invalid
tri = Polygon([(0, 0), (40, 0), (20, 30)])
score, valid = analyze_shape_quality(tri)
print(f" Triangle: score={score:.3f}, valid={valid}")
assert not valid, "Triangle should be invalid (low rectangularity)"
# Test 3: Very elongated shape should be invalid
elongated = Polygon([(0, 0), (200, 0), (200, 10), (0, 10)])
score, valid = analyze_shape_quality(elongated)
print(f" Elongated: score={score:.3f}, valid={valid}")
assert not valid, "Elongated shape should be invalid"
# Test 4: Small lot should be invalid
small = Polygon([(0, 0), (10, 0), (10, 10), (0, 10)]) # 100 m²
score, valid = analyze_shape_quality(small)
print(f" Small lot: score={score:.3f}, valid={valid}")
assert not valid, "Small lot should be invalid"
# Test 5: Dominant edge vector
vec = get_dominant_edge_vector(rect)
print(f" Dominant edge vector: {vec}")
assert vec is not None, "Should return vector"
# Test 6: OBB dimensions
w, l, angle = get_obb_dimensions(rect)
print(f" OBB dimensions: width={w:.1f}, length={l:.1f}, angle={angle:.1f}°")
assert abs(w - 40) < 1, "Width should be ~40"
assert abs(l - 50) < 1, "Length should be ~50"
# Test 7: Lot classification
lot_type = classify_lot_type(rect)
print(f" Rectangle type: {lot_type}")
assert lot_type == 'commercial', "Good lot should be commercial"
# Triangle must be large enough (>1000m²) to be green_space, otherwise it's unusable
large_tri = Polygon([(0, 0), (60, 0), (30, 50)]) # 1500 m²
lot_type = classify_lot_type(large_tri)
print(f" Triangle type: {lot_type}")
assert lot_type == 'green_space', "Bad lot (large enough) should be green_space"
print("✅ Shape quality tests passed")
return True
except Exception as e:
print(f"❌ Shape quality test failed: {e}")
traceback.print_exc()
return False
def test_orthogonal_slicer():
"""Test orthogonal slicing functions."""
print("\nTesting orthogonal slicer functions...")
try:
from shapely.geometry import Polygon
from core.geometry.orthogonal_slicer import (
orthogonal_slice,
subdivide_with_uniform_widths
)
# Test 1: Subdivide a rectangular block
block = Polygon([(0, 0), (100, 0), (100, 50), (0, 50)])
widths = [25, 25, 25, 25]
lots = orthogonal_slice(block, widths)
print(f" Orthogonal slice: {len(lots)} lots from 4 widths")
assert len(lots) == 4, "Should create 4 lots"
# Test 2: Uniform width subdivision
lots2, actual_widths = subdivide_with_uniform_widths(block, target_width=20)
print(f" Uniform subdivision: {len(lots2)} lots, widths={[round(w,1) for w in actual_widths]}")
assert len(lots2) > 0, "Should create lots"
print("✅ Orthogonal slicer tests passed")
return True
except Exception as e:
print(f"❌ Orthogonal slicer test failed: {e}")
traceback.print_exc()
return False
def test_settings():
"""Test aesthetic settings in config."""
print("\nTesting aesthetic settings...")
try:
from core.config.settings import (
DEFAULT_SETTINGS,
MIN_RECTANGULARITY,
MAX_ASPECT_RATIO,
MIN_LOT_AREA,
DEVIATION_PENALTY_WEIGHT,
ENABLE_LEFTOVER_MANAGEMENT
)
print(f" MIN_RECTANGULARITY = {MIN_RECTANGULARITY}")
print(f" MAX_ASPECT_RATIO = {MAX_ASPECT_RATIO}")
print(f" MIN_LOT_AREA = {MIN_LOT_AREA}")
print(f" DEVIATION_PENALTY_WEIGHT = {DEVIATION_PENALTY_WEIGHT}")
print(f" ENABLE_LEFTOVER_MANAGEMENT = {ENABLE_LEFTOVER_MANAGEMENT}")
assert MIN_RECTANGULARITY == 0.75, "Should match Beauti_mode spec"
assert MAX_ASPECT_RATIO == 4.0, "Should match Beauti_mode spec"
assert MIN_LOT_AREA == 1000.0, "Should match Beauti_mode spec"
print("✅ Settings tests passed")
return True
except Exception as e:
print(f"❌ Settings test failed: {e}")
traceback.print_exc()
return False
if __name__ == "__main__":
print("=" * 50)
print("Aesthetic Optimization Test Suite")
print("=" * 50)
results = []
results.append(("Shape Quality", test_shape_quality()))
results.append(("Orthogonal Slicer", test_orthogonal_slicer()))
results.append(("Settings", test_settings()))
print("\n" + "=" * 50)
print("Test Results:")
print("=" * 50)
for name, passed in results:
status = "✅ PASS" if passed else "❌ FAIL"
print(f"{status} - {name}")
all_passed = all(r[1] for r in results)
print("\n" + "=" * 50)
if all_passed:
print("✅ ALL AESTHETIC TESTS PASSED")
sys.exit(0)
else:
print("❌ SOME TESTS FAILED")
sys.exit(1)