File size: 6,139 Bytes
3bd69a8 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | """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)
|