buildinves commited on
Commit
24482a2
·
verified ·
1 Parent(s): 54bdcdf

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +519 -213
app.py CHANGED
@@ -16,8 +16,8 @@ import tempfile
16
 
17
  class AdvancedGridOptimizer:
18
  def __init__(self):
19
- # Standard lot widths and their typical depths
20
- self.lot_specifications = {
21
  8.5: {"depths": [21, 25, 28], "type": "SLHC", "squares": "11-16"},
22
  10.5: {"depths": [21, 25, 28, 32, 35], "type": "SLHC", "squares": "13-21.5"},
23
  12.5: {"depths": [21, 25, 28, 30, 32], "type": "Standard", "squares": "16-24"},
@@ -31,17 +31,39 @@ class AdvancedGridOptimizer:
31
  16.8: {"depths": [30, 32], "type": "Corner-Premium", "squares": "26-32"}
32
  }
33
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  self.slhc_widths = [8.5, 10.5]
35
  self.standard_widths = [12.5, 14.0]
36
  self.premium_widths = [16.0, 18.0]
37
  self.corner_specific = [11.0, 13.3, 14.8, 16.8]
38
 
 
 
 
 
39
  # Define corner_widths as all widths suitable for corners
40
  self.corner_widths = self.corner_specific + [14.0, 16.0, 18.0]
41
 
42
  # Enhanced color palette with RPM brand colors
43
  self.color_schemes = {
44
  'rpm_primary': {
 
45
  8.5: '#802B2B', # Burgundy for SLHC
46
  10.5: '#AB3838', # Burgundy 75%
47
  12.5: '#216767', # Teal
@@ -51,9 +73,16 @@ class AdvancedGridOptimizer:
51
  11.0: '#4F8585', # Teal 75%
52
  13.3: '#545D51', # RPM Green 75%
53
  14.8: '#697687', # Blue 75%
54
- 16.8: '#FFCF6D' # Yellow 75%
 
 
 
 
 
 
55
  },
56
  'rpm_contrast': {
 
57
  8.5: '#D69C9C', # Burgundy 50%
58
  10.5: '#E2C1B7', # Burgundy 25%
59
  12.5: '#95B5B5', # Teal 50%
@@ -63,9 +92,16 @@ class AdvancedGridOptimizer:
63
  11.0: '#D6E3E3', # Teal 25%
64
  13.3: '#B6B8B2', # RPM Green 25%
65
  14.8: '#CCD7E4', # Blue 25%
66
- 16.8: '#FFEFCE' # Yellow 25%
 
 
 
 
 
 
67
  },
68
  'rpm_monochrome': {
 
69
  8.5: '#2E3E2F', # RPM Green 100%
70
  10.5: '#545D51', # RPM Green 75%
71
  12.5: '#80857B', # RPM Green 50%
@@ -75,16 +111,40 @@ class AdvancedGridOptimizer:
75
  11.0: '#D1D3D4', # Black 25%
76
  13.3: '#216767', # Teal (accent)
77
  14.8: '#415B6E', # Blue (accent)
78
- 16.8: '#FF8E3C' # Yellow (accent)
 
 
 
 
 
 
79
  }
80
  }
81
 
82
  self.current_scheme = 'rpm_primary'
83
  self.current_solution = None # Store current AI solution
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
 
85
  def create_enhanced_visualization(self, solution, stage_width, stage_depth=32, title="Premium Grid Layout", show_variance=None):
86
- """Create a clean 2D visualization with corner splays"""
87
- fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(18, 12), gridspec_kw={'height_ratios': [3, 1]},
 
 
88
  facecolor='#2E3E2F')
89
 
90
  # Main visualization
@@ -95,20 +155,27 @@ class AdvancedGridOptimizer:
95
 
96
  # Set up main plot with RPM green background
97
  ax1.set_xlim(-5, stage_width + 5)
98
- ax1.set_ylim(-10, 50)
 
 
 
 
99
  ax1.set_facecolor('#2E3E2F')
100
 
101
  # Add title with variance if provided
102
  if show_variance is not None:
103
  variance_color = '#216767' if abs(show_variance) < 0.001 else '#802B2B'
104
- title_text = f"{title}\nGrid Variance: {show_variance:+.1f}m"
 
 
105
  ax1.set_title(title_text, fontsize=28, fontweight='bold', pad=25, color='white')
106
  else:
107
  ax1.set_title(title, fontsize=28, fontweight='bold', pad=25, color='white')
108
 
109
  # Add subtle gradient background
110
  gradient = np.linspace(0.3, 0.1, 100).reshape(1, -1)
111
- ax1.imshow(gradient, extent=[-5, stage_width + 5, -10, 50], aspect='auto',
 
112
  cmap='Greys', alpha=0.3, zorder=0)
113
 
114
  # Add street with label
@@ -119,17 +186,45 @@ class AdvancedGridOptimizer:
119
  ax1.text(stage_width/2, -2, 'STREET', ha='center', va='center',
120
  fontsize=20, color='white', fontweight='bold')
121
 
122
- # Draw lots with corner splays - FIXED ALIGNMENT
123
  splay_size = 3 # 3m corner splay
124
- lot_height = 28 # UNIFORM HEIGHT FOR ALL LOTS
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
 
126
  for i, (width, lot_type) in enumerate(solution):
127
  # Get base color
128
  if width in colors:
129
  base_color = colors[width]
130
  else:
131
- closest_width = min(colors.keys(), key=lambda x: abs(x - width))
132
- base_color = colors[closest_width]
 
 
 
 
 
 
 
133
 
134
  # Check position
135
  is_corner = (i == 0 or i == len(solution) - 1)
@@ -139,14 +234,14 @@ class AdvancedGridOptimizer:
139
  edge_color = 'white'
140
  linewidth = 4.0 if is_corner else 3.0
141
 
142
- # Create lot shape with SAME HEIGHT for all lots
143
- if is_corner:
144
- # Corner lot with splay - using same height
145
  if i == 0: # First corner
146
  vertices = [
147
  (x_pos + splay_size, 8), # Start after splay
148
  (x_pos + width, 8),
149
- (x_pos + width, 8 + lot_height), # SAME HEIGHT
150
  (x_pos, 8 + lot_height), # Straight rear
151
  (x_pos, 8 + splay_size) # Splay corner
152
  ]
@@ -155,7 +250,7 @@ class AdvancedGridOptimizer:
155
  (x_pos, 8),
156
  (x_pos + width - splay_size, 8),
157
  (x_pos + width, 8 + splay_size), # Splay corner
158
- (x_pos + width, 8 + lot_height), # SAME HEIGHT
159
  (x_pos, 8 + lot_height)
160
  ]
161
 
@@ -175,7 +270,7 @@ class AdvancedGridOptimizer:
175
  ax1.plot([x_pos + width - splay_size, x_pos + width],
176
  [8, 8 + splay_size], 'white', linewidth=2, alpha=0.8)
177
  else:
178
- # Regular lot with SAME HEIGHT
179
  lot = FancyBboxPatch((x_pos, 8), width, lot_height,
180
  boxstyle="round,pad=0.1",
181
  facecolor=face_color,
@@ -184,7 +279,7 @@ class AdvancedGridOptimizer:
184
  zorder=3)
185
  ax1.add_patch(lot)
186
 
187
- # Add subtle glow (same for all)
188
  glow = FancyBboxPatch((x_pos - 0.2, 7.8), width + 0.4, lot_height + 0.4,
189
  boxstyle="round,pad=0.15",
190
  facecolor='none',
@@ -194,16 +289,13 @@ class AdvancedGridOptimizer:
194
  zorder=2)
195
  ax1.add_patch(glow)
196
 
197
- # Add rear alignment line to emphasize equal depth
198
- rear_y = 8 + lot_height
199
- ax1.plot([x_pos, x_pos + width], [rear_y, rear_y],
200
- color=edge_color, linewidth=1, alpha=0.3, linestyle='--')
201
-
202
  # Add lot information (positioned consistently)
203
- ax1.text(x_pos + width/2, 40, f'L{lot_num}',
 
 
204
  ha='center', va='center', fontsize=16, fontweight='bold', color='white')
205
 
206
- ax1.text(x_pos + width/2, 35, f'{width:.1f}m',
207
  ha='center', va='center', fontsize=14, fontweight='bold', color='white')
208
 
209
  # Lot type
@@ -218,10 +310,14 @@ class AdvancedGridOptimizer:
218
  spec = {**spec, 'type': 'Custom'}
219
 
220
  lot_type_text = spec['type']
221
- if is_corner:
222
  lot_type_text = "CORNER"
223
 
224
- ax1.text(x_pos + width/2, 23, lot_type_text,
 
 
 
 
225
  ha='center', va='center', fontsize=11,
226
  bbox=dict(boxstyle="round,pad=0.3", facecolor='#545D51',
227
  edgecolor='white', alpha=0.9), color='white')
@@ -231,16 +327,27 @@ class AdvancedGridOptimizer:
231
  ax1.plot([x_pos, x_pos], [10, 14], 'w-', linewidth=1, alpha=0.3)
232
  ax1.plot([x_pos + width, x_pos + width], [10, 14], 'w-', linewidth=1, alpha=0.3)
233
 
 
 
 
 
 
 
 
 
 
234
  x_pos += width
235
  lot_num += 1
236
 
237
  # Add rear alignment line across all lots
238
- ax1.plot([0, stage_width], [8 + lot_height, 8 + lot_height],
239
- '#216767', linewidth=2, alpha=0.8, linestyle='-')
240
- ax1.text(stage_width/2, 8 + lot_height + 1, 'REAR ALIGNMENT LINE',
241
- ha='center', va='bottom', fontsize=12, color='#216767', alpha=0.8,
242
- bbox=dict(boxstyle="round,pad=0.3", facecolor='#2E3E2F',
243
- edgecolor='#216767', alpha=0.8))
 
 
244
 
245
  # Add stage dimensions
246
  arrow_props = dict(arrowstyle='<->', color='white', lw=3)
@@ -263,33 +370,48 @@ class AdvancedGridOptimizer:
263
  unique_widths = len(set(w for w, _ in solution))
264
  diversity_score = unique_widths / len(set(self.lot_specifications.keys()))
265
 
266
- slhc_count = sum(1 for w, _ in solution if w <= 10.5)
267
- standard_count = sum(1 for w, _ in solution if 10.5 < w <= 14)
268
- premium_count = sum(1 for w, _ in solution if w > 14)
269
-
270
- # SLHC pairs
271
- slhc_pairs = 0
272
- for i in range(len(solution) - 1):
273
- if solution[i][0] <= 10.5 and solution[i+1][0] <= 10.5:
274
- slhc_pairs += 1
 
 
 
 
 
 
 
275
 
276
  # Calculate actual total width and variance
277
  total_width = sum(w for w, _ in solution)
278
  variance = total_width - stage_width
279
  efficiency = "100%" if abs(variance) < 0.001 else f"{(total_width/stage_width)*100:.1f}%"
280
 
 
 
 
 
 
 
 
 
281
  metrics_lines = [
282
  f"📊 TOTAL LOTS: {total_lots}",
283
  f"📐 LAND EFFICIENCY: {efficiency}",
284
  f"🎯 DIVERSITY: {diversity_score:.0%} ({unique_widths} types)",
285
  f"📏 GRID VARIANCE: {variance:+.2f}m",
286
  "",
287
- f"SLHC (≤10.5m): {slhc_count} lots",
288
- f"Standard (11-14m): {standard_count} lots",
289
- f"Premium (>14m): {premium_count} lots",
290
  "",
291
- f"🚗 SLHC Pairs: {slhc_pairs}",
292
- f"💰 Revenue: ${total_lots * 0.5:.1f}M - ${total_lots * 1.2:.1f}M"
293
  ]
294
 
295
  col1_text = '\n'.join(metrics_lines[:5])
@@ -358,12 +480,14 @@ class AdvancedGridOptimizer:
358
  feedback = f"⚠️ Grid is {-variance:.2f}m too narrow. Add {-variance:.2f}m total width."
359
 
360
  # Add suggestions if not perfect
 
 
361
  if abs(variance) > 0.001:
362
  if variance > 0:
363
  # Suggest which lots could be reduced
364
  suggestions = []
365
  for i, w in enumerate(widths):
366
- if w - variance >= 8.5: # Minimum viable width
367
  suggestions.append(f"L{i+1}: reduce from {w:.1f}m to {w-variance:.1f}m")
368
  if suggestions:
369
  feedback += f"\n\nSuggestions:\n" + "\n".join(suggestions[:3])
@@ -381,54 +505,19 @@ class AdvancedGridOptimizer:
381
  return ""
382
  return ", ".join([f"{w:.1f}" for w, _ in solution])
383
 
384
- def parse_manual_input(self, manual_text):
385
- """Parse manual input into structured data"""
386
- try:
387
- if not manual_text:
388
- return {}
389
-
390
- # Try JSON format first
391
- if manual_text.strip().startswith('{'):
392
- return json.loads(manual_text)
393
-
394
- # Otherwise parse line by line
395
- result = {}
396
- for line in manual_text.strip().split('\n'):
397
- line = line.strip()
398
- if not line:
399
- continue
400
-
401
- if '=' in line:
402
- parts = line.split('=')
403
- width_str = parts[0].strip().replace('m', '')
404
- count_str = parts[1].strip()
405
- try:
406
- width_val = float(width_str)
407
- result[width_val] = int(count_str)
408
- except:
409
- pass
410
- elif ':' in line:
411
- parts = line.split(':')
412
- width_str = parts[0].strip().replace('m', '')
413
- count_str = parts[1].strip()
414
- try:
415
- width_val = float(width_str)
416
- result[width_val] = int(count_str)
417
- except:
418
- pass
419
- return result
420
- except Exception as e:
421
- print(f"Error parsing manual input: {e}")
422
- return {}
423
-
424
  def find_optimal_custom_corners(self, stage_width, internal_widths, base_corner_width, tolerance=0.5):
425
  """Find optimal corner widths that can vary slightly from base width"""
426
  best_solution = None
427
  best_fitness = -float('inf')
428
 
429
- # Ensure corners are at least as wide as smallest internal lot
430
- min_internal = min(internal_widths) if internal_widths else 8.5
431
- min_corner_width = max(base_corner_width - tolerance, min_internal)
 
 
 
 
 
432
 
433
  # Try variations of corner widths within tolerance
434
  variations = np.arange(min_corner_width,
@@ -446,10 +535,11 @@ class AdvancedGridOptimizer:
446
  internal_solution = self.find_exact_solution_with_diversity(internal_width, internal_widths)
447
 
448
  if internal_solution:
449
- # Verify no internal lot is wider than corners
450
- max_internal = max(internal_solution) if internal_solution else 0
451
- if max_internal > min(corner1, corner2):
452
- continue
 
453
 
454
  # Build complete solution
455
  solution = [(round(corner1, 1), 'corner')]
@@ -484,8 +574,14 @@ class AdvancedGridOptimizer:
484
 
485
  # Strategy 2: Try flexible corners if enabled
486
  if allow_custom_corners and standard_internal:
487
- # Try variations around each corner-suitable width
488
- for base_width in [11.0, 13.3, 14.8, 16.8, 14.0, 16.0]:
 
 
 
 
 
 
489
  if any(abs(w - base_width) < 2 for w in enabled_widths):
490
  custom_solution = self.find_optimal_custom_corners(
491
  stage_width, standard_internal, base_width, tolerance=0.5
@@ -503,10 +599,15 @@ class AdvancedGridOptimizer:
503
 
504
  # Separate widths by size
505
  all_widths = sorted(enabled_widths)
506
- min_internal_width = min(all_widths)
507
 
508
- # Corner lots must be at least as wide as smallest internal lot
509
- corner_options = [w for w in enabled_widths if w >= max(11.0, min_internal_width)]
 
 
 
 
 
 
510
 
511
  best_solution = None
512
  best_fitness = -float('inf')
@@ -528,10 +629,11 @@ class AdvancedGridOptimizer:
528
  )
529
 
530
  for internal_widths in internal_solutions:
531
- # Verify no internal lot is wider than corners
532
- max_internal = max(internal_widths) if internal_widths else 0
533
- if max_internal > min(corner1, corner2):
534
- continue # Skip if internal lots are wider than corners
 
535
 
536
  # Build complete solution
537
  solution = [(corner1, 'corner')]
@@ -539,31 +641,34 @@ class AdvancedGridOptimizer:
539
  solution.append((corner2, 'corner'))
540
 
541
  # Optimize arrangement
542
- optimized = self.optimize_slhc_grouping(solution)
543
  fitness = self.evaluate_solution_with_diversity(optimized, stage_width)
544
 
545
  if fitness > best_fitness:
546
  best_fitness = fitness
547
  best_solution = optimized
548
 
549
- # If no good solution, try without strict corner rules but maintain size hierarchy
550
  if not best_solution:
551
  all_solutions = []
552
  self.find_all_combinations_recursive(stage_width, sorted(enabled_widths),
553
  [], all_solutions, 20)
554
 
555
  for widths in all_solutions[:50]:
556
- # Ensure corners are among the largest lots
557
- sorted_widths = sorted(widths)
558
- if len(sorted_widths) >= 2:
559
- # Put two largest widths at corners
560
- solution = [(sorted_widths[-1], 'corner')] # Largest
561
- solution.extend([(w, 'standard') for w in sorted_widths[:-2]])
562
- solution.append((sorted_widths[-2], 'corner')) # Second largest
563
- else:
564
  solution = [(w, 'standard') for w in widths]
 
 
 
 
 
 
 
 
 
565
 
566
- optimized = self.optimize_slhc_grouping(solution)
567
  fitness = self.evaluate_solution_with_diversity(optimized, stage_width)
568
 
569
  if fitness > best_fitness:
@@ -572,6 +677,52 @@ class AdvancedGridOptimizer:
572
 
573
  return best_solution
574
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
575
  def find_diverse_combinations(self, target_width, available_widths, max_solutions=20):
576
  """Find combinations that maximize diversity"""
577
  all_solutions = []
@@ -678,7 +829,7 @@ class AdvancedGridOptimizer:
678
  current.pop()
679
 
680
  def optimize_slhc_grouping(self, lots):
681
- """Optimize lot arrangement with sophisticated rules"""
682
  if not lots or len(lots) <= 1:
683
  return lots
684
 
@@ -812,43 +963,56 @@ class AdvancedGridOptimizer:
812
  fitness -= max_repetition * 500 # Penalty for too many of same width
813
  fitness += diversity_ratio * 3000 # Bonus for good diversity ratio
814
 
815
- # Corner evaluation
816
- if len(solution) >= 2:
817
- first_width = solution[0][0]
818
- last_width = solution[-1][0]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
819
 
820
- # Penalty for SLHC on corners
821
- if first_width <= 10.5:
822
- fitness -= 2000
823
- if last_width <= 10.5:
824
- fitness -= 2000
825
 
826
- # Bonus for good corners
827
- if first_width >= 11.0:
828
- fitness += 1000
829
- if last_width >= 11.0:
830
- fitness += 1000
 
 
 
 
 
 
831
 
832
- # Balance bonus
833
- corner_diff = abs(first_width - last_width)
834
- if corner_diff < 0.1:
835
- fitness += 1500 # Perfect match
836
- elif corner_diff <= 1.0:
837
- fitness += 1000 # Very good
838
- elif corner_diff <= 2.0:
839
- fitness += 500 # Good
840
- else:
841
- fitness -= 500 # Poor balance
842
-
843
- # SLHC grouping bonus
844
- for i in range(len(solution) - 1):
845
- if solution[i][0] <= 10.5 and solution[i+1][0] <= 10.5:
846
- fitness += 300 # Adjacent SLHC bonus
847
-
848
- # Penalize corner-specific widths used internally
849
- for i in range(1, len(solution) - 1):
850
- if solution[i][0] in self.corner_specific:
851
- fitness -= 200
852
 
853
  return fitness
854
 
@@ -873,11 +1037,16 @@ class AdvancedGridOptimizer:
873
  total_width = sum(w for w, _ in solution)
874
  variance = total_width - stage_width
875
 
 
 
 
 
876
  report = f"""
877
- # SUBDIVISION OPTIMIZATION REPORT
878
  ## Project Analysis for {stage_width}m × {stage_depth}m Stage
879
 
880
  ### EXECUTIVE SUMMARY
 
881
  - **Total Lots**: {len(solution)}
882
  - **Unique Lot Types**: {unique_widths}
883
  - **Land Efficiency**: {"100%" if abs(variance) < 0.001 else f"{(total_width/stage_width)*100:.1f}%"}
@@ -885,32 +1054,49 @@ class AdvancedGridOptimizer:
885
  - **Stage Dimensions**: {stage_width}m × {stage_depth}m
886
  - **Total Area**: {stage_width * stage_depth}m²
887
  {f"- **Custom Widths Used**: {', '.join(custom_widths)}" if custom_widths else ""}
888
-
889
- ### LOT DIVERSITY ANALYSIS
890
  """
891
 
 
 
 
 
 
 
 
 
 
892
  # Sort by count to show distribution
893
  sorted_widths = sorted(width_counts.items(), key=lambda x: x[1], reverse=True)
894
  for width, count in sorted_widths:
895
  percentage = (count / len(solution)) * 100
896
  if width in self.lot_specifications:
897
  spec = self.lot_specifications[width]
898
- report += f"- **{width:.1f}m** × {count} ({percentage:.1f}%): {spec['type']}\n"
 
899
  else:
900
  report += f"- **{width:.1f}m** × {count} ({percentage:.1f}%): Custom Width\n"
901
 
902
- # Corner analysis
903
- if len(solution) >= 2:
904
  report += f"\n### CORNER ANALYSIS\n"
905
  report += f"- **Front Corner**: {solution[0][0]:.1f}m with 3m × 3m splay\n"
906
  report += f"- **Rear Corner**: {solution[-1][0]:.1f}m with 3m × 3m splay\n"
907
  report += f"- **Balance**: {abs(solution[0][0] - solution[-1][0]):.1f}m difference\n"
908
 
909
  report += f"\n### DESIGN FEATURES\n"
910
- report += f"- Corner splays provide safe sight lines at intersections\n"
911
- report += f"- All lots have identical rear alignment for visual consistency\n"
912
- report += f"- Diverse lot mix ensures varied streetscape\n"
913
- report += f"- SLHC lots grouped for efficient garbage collection\n"
 
 
 
 
 
 
 
 
 
914
 
915
  report += f"\n---\n*Report generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}*"
916
 
@@ -929,30 +1115,68 @@ class AdvancedGridOptimizer:
929
  def create_advanced_app():
930
  optimizer = AdvancedGridOptimizer()
931
 
 
 
 
 
 
 
 
 
 
 
 
 
 
932
  def optimize_grid(
933
  stage_width,
934
  stage_depth,
 
 
 
935
  enable_8_5, enable_10_5, enable_12_5, enable_14, enable_16, enable_18,
936
  enable_corners, enable_11, enable_13_3, enable_14_8, enable_16_8,
 
 
 
 
937
  allow_custom_corners, color_scheme
938
  ):
 
 
 
 
 
 
939
  # Update color scheme
940
  optimizer.current_scheme = color_scheme
941
 
942
- # Collect enabled widths
943
  enabled_widths = []
944
- if enable_8_5: enabled_widths.append(8.5)
945
- if enable_10_5: enabled_widths.append(10.5)
946
- if enable_12_5: enabled_widths.append(12.5)
947
- if enable_14: enabled_widths.append(14.0)
948
- if enable_16: enabled_widths.append(16.0)
949
- if enable_18: enabled_widths.append(18.0)
950
-
951
- if enable_corners:
952
- if enable_11: enabled_widths.append(11.0)
953
- if enable_13_3: enabled_widths.append(13.3)
954
- if enable_14_8: enabled_widths.append(14.8)
955
- if enable_16_8: enabled_widths.append(16.8)
 
 
 
 
 
 
 
 
 
 
 
 
956
 
957
  if not enabled_widths:
958
  return None, None, pd.DataFrame(), "Please select at least one lot width!", "", ""
@@ -974,23 +1198,33 @@ def create_advanced_app():
974
 
975
  # Verify solution
976
  if not optimized_solution or abs(sum(w for w, _ in optimized_solution) - stage_width) > 0.001:
977
- # Provide suggestions
 
 
 
 
 
 
 
978
  return None, pd.DataFrame(), f"""
979
  ### ❌ Cannot achieve 100% usage with selected widths
980
 
981
  **Stage Width**: {stage_width}m
 
982
  **Available Widths**: {', '.join([f"{w}m" for w in sorted(enabled_widths)])}
983
 
984
  **Try:**
985
  1. Enable more lot types for flexibility
986
  2. Enable "Custom Corners" option
987
- 3. Try common stage widths: 84m, 105m, 126m
 
988
  """, "", ""
989
 
990
  # Create visualizations with variance indicator
 
991
  fig_2d = optimizer.create_enhanced_visualization(
992
  optimized_solution, stage_width, stage_depth,
993
- "RPM Grid Cut Optimization",
994
  show_variance=variance
995
  )
996
 
@@ -1017,19 +1251,23 @@ def create_advanced_app():
1017
  'count': 1,
1018
  'type': spec.get('type', 'Custom'),
1019
  'squares': spec.get('squares', 'N/A'),
1020
- 'area': width * stage_depth
 
1021
  }
1022
 
1023
  results_data = []
1024
  for width, info in sorted(width_counts.items()):
1025
- results_data.append({
1026
  'Lot Width': width,
1027
  'Count': info['count'],
1028
  'Type': info['type'],
1029
  'Area Each': f"{info['area']:.0f}m²",
1030
  'Total Width': f"{float(width[:-1]) * info['count']:.1f}m",
1031
  'Total Area': f"{info['area'] * info['count']:.0f}m²"
1032
- })
 
 
 
1033
 
1034
  results_df = pd.DataFrame(results_data)
1035
 
@@ -1040,31 +1278,30 @@ def create_advanced_app():
1040
  total_lots = len(optimized_solution)
1041
  unique_widths = len(set(w for w, _ in optimized_solution))
1042
 
1043
- # Count SLHC pairs
1044
- slhc_pairs = sum(1 for i in range(len(optimized_solution) - 1)
1045
- if optimized_solution[i][0] <= 10.5 and optimized_solution[i+1][0] <= 10.5)
1046
-
1047
- # Analyze corners
1048
- corner_info = "N/A"
1049
- if len(optimized_solution) >= 2:
1050
- first = optimized_solution[0][0]
1051
- last = optimized_solution[-1][0]
1052
- diff = abs(first - last)
1053
 
1054
- if diff < 0.1:
1055
- corner_info = f"✨ PERFECT ({first:.1f}m × 2)"
1056
- elif diff <= 1.0:
1057
- corner_info = f"✅ Excellent ({first:.1f}m + {last:.1f}m)"
1058
- elif diff <= 2.0:
1059
- corner_info = f"👍 Good ({first:.1f}m + {last:.1f}m)"
1060
- else:
1061
- corner_info = f"⚠️ Unbalanced ({first:.1f}m + {last:.1f}m)"
1062
-
1063
- summary = f"""
1064
  **Stage**: {stage_width}m × {stage_depth}m = {stage_width * stage_depth}m²
 
1065
  **Total Lots**: {total_lots}
 
1066
  **Unique Lot Types**: {unique_widths}
1067
  **Grid Variance**: {variance:+.2f}m {"✅" if abs(variance) < 0.001 else "⚠️"}
 
 
 
 
 
 
 
 
 
 
 
 
1068
  """
1069
 
1070
  # Convert solution to string for manual editing
@@ -1072,8 +1309,14 @@ def create_advanced_app():
1072
 
1073
  return fig_2d, results_df, summary, report, manual_edit_string
1074
 
1075
- def update_manual_adjustment(manual_widths_text, stage_width, stage_depth, color_scheme):
1076
  """Update visualization based on manual adjustment"""
 
 
 
 
 
 
1077
  optimizer.current_scheme = color_scheme
1078
 
1079
  # Parse manual widths
@@ -1160,7 +1403,23 @@ def create_advanced_app():
1160
  with gr.Row():
1161
  with gr.Column(scale=1):
1162
  with gr.Group():
1163
- gr.Markdown("### 📐 Stage Dimensions")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1164
  stage_width = gr.Number(
1165
  label="Stage Width (m)",
1166
  value=105.0,
@@ -1174,7 +1433,8 @@ def create_advanced_app():
1174
 
1175
  gr.Markdown("### 📏 Lot Width Options")
1176
 
1177
- with gr.Group():
 
1178
  gr.Markdown("**Standard Widths**")
1179
  with gr.Row():
1180
  enable_8_5 = gr.Checkbox(label="8.5m SLHC", value=True)
@@ -1184,8 +1444,7 @@ def create_advanced_app():
1184
  enable_14 = gr.Checkbox(label="14.0m", value=True)
1185
  enable_16 = gr.Checkbox(label="16.0m", value=True)
1186
  enable_18 = gr.Checkbox(label="18.0m", value=False)
1187
-
1188
- with gr.Group():
1189
  enable_corners = gr.Checkbox(
1190
  label="Enable Corner-Specific Widths",
1191
  value=True,
@@ -1197,6 +1456,21 @@ def create_advanced_app():
1197
  with gr.Row():
1198
  enable_14_8 = gr.Checkbox(label="14.8m", value=True)
1199
  enable_16_8 = gr.Checkbox(label="16.8m", value=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1200
 
1201
  with gr.Column(scale=1):
1202
  gr.Markdown("### ⚙️ Settings")
@@ -1223,14 +1497,14 @@ def create_advanced_app():
1223
 
1224
  gr.Markdown("""
1225
  ### 💡 Quick Tips:
1226
- - **Corner Lots**: Always wider than internals
 
 
1227
  - **Grid Variance**: Shows if layout is perfect (0.0m)
1228
- - **Manual Adjust**: Edit the result below after optimization
1229
- - **Diversity Focus**: Maximizes lot variety
1230
  """)
1231
 
1232
  with gr.Row():
1233
- plot_2d = gr.Plot(label="2D Layout with Corner Splays")
1234
 
1235
  # Manual adjustment section
1236
  gr.Markdown("### ✏️ Fine-Tune Result")
@@ -1258,14 +1532,46 @@ def create_advanced_app():
1258
  with gr.Column():
1259
  report_output = gr.Markdown(label="Professional Report")
1260
 
1261
- # Wire up the buttons
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1262
  optimize_btn.click(
1263
  optimize_grid,
1264
  inputs=[
1265
  stage_width,
1266
  stage_depth,
 
 
 
1267
  enable_8_5, enable_10_5, enable_12_5, enable_14, enable_16, enable_18,
1268
  enable_corners, enable_11, enable_13_3, enable_14_8, enable_16_8,
 
 
 
 
1269
  allow_custom_corners, color_scheme
1270
  ],
1271
  outputs=[plot_2d, results_table, summary_output, report_output, manual_widths]
@@ -1273,7 +1579,7 @@ def create_advanced_app():
1273
 
1274
  update_btn.click(
1275
  update_manual_adjustment,
1276
- inputs=[manual_widths, stage_width, stage_depth, color_scheme],
1277
  outputs=[plot_2d, adjustment_feedback]
1278
  )
1279
 
 
16
 
17
  class AdvancedGridOptimizer:
18
  def __init__(self):
19
+ # Conventional lot widths and their typical depths
20
+ self.conventional_lot_specifications = {
21
  8.5: {"depths": [21, 25, 28], "type": "SLHC", "squares": "11-16"},
22
  10.5: {"depths": [21, 25, 28, 32, 35], "type": "SLHC", "squares": "13-21.5"},
23
  12.5: {"depths": [21, 25, 28, 30, 32], "type": "Standard", "squares": "16-24"},
 
31
  16.8: {"depths": [30, 32], "type": "Corner-Premium", "squares": "26-32"}
32
  }
33
 
34
+ # Medium Density lot specifications
35
+ self.md_rear_loaded_specifications = {
36
+ 4.5: {"depths": [19, 25, 28], "type": "MD-Rear Load", "squares": "85.5-126", "build": "2/2/1"},
37
+ 6.0: {"depths": [19, 25, 28], "type": "MD-Rear Load", "squares": "114-168", "build": "3/2/2"},
38
+ 7.5: {"depths": [25, 28], "type": "MD-Rear Load", "squares": "187.5-210", "build": "3-4/2/2"}
39
+ }
40
+
41
+ self.md_front_loaded_specifications = {
42
+ 7.0: {"depths": [21], "type": "MD-Front Load", "squares": "147", "build": "3/2/1"},
43
+ 8.0: {"depths": [21], "type": "MD-Front Load", "squares": "168", "build": "3-4/2/2"},
44
+ 8.5: {"depths": [16], "type": "MD-Front Load", "squares": "136", "build": "3/2/1"},
45
+ 10.5: {"depths": [16], "type": "MD-Front Load", "squares": "168", "build": "3-4/2/2"}
46
+ }
47
+
48
+ # Set initial lot specifications to conventional
49
+ self.lot_specifications = self.conventional_lot_specifications
50
+
51
  self.slhc_widths = [8.5, 10.5]
52
  self.standard_widths = [12.5, 14.0]
53
  self.premium_widths = [16.0, 18.0]
54
  self.corner_specific = [11.0, 13.3, 14.8, 16.8]
55
 
56
+ # Medium density categories
57
+ self.md_rear_widths = [4.5, 6.0, 7.5]
58
+ self.md_front_widths = [7.0, 8.0, 8.5, 10.5]
59
+
60
  # Define corner_widths as all widths suitable for corners
61
  self.corner_widths = self.corner_specific + [14.0, 16.0, 18.0]
62
 
63
  # Enhanced color palette with RPM brand colors
64
  self.color_schemes = {
65
  'rpm_primary': {
66
+ # Conventional colors
67
  8.5: '#802B2B', # Burgundy for SLHC
68
  10.5: '#AB3838', # Burgundy 75%
69
  12.5: '#216767', # Teal
 
73
  11.0: '#4F8585', # Teal 75%
74
  13.3: '#545D51', # RPM Green 75%
75
  14.8: '#697687', # Blue 75%
76
+ 16.8: '#FFCF6D', # Yellow 75%
77
+ # Medium Density colors
78
+ 4.5: '#6B4C8A', # Purple for MD
79
+ 6.0: '#8A6BB3', # Purple 75%
80
+ 7.5: '#9F85C7', # Purple 50%
81
+ 7.0: '#4A7C7E', # Teal-Blue for MD Front
82
+ 8.0: '#5A9A9C' # Teal-Blue 75%
83
  },
84
  'rpm_contrast': {
85
+ # Conventional colors
86
  8.5: '#D69C9C', # Burgundy 50%
87
  10.5: '#E2C1B7', # Burgundy 25%
88
  12.5: '#95B5B5', # Teal 50%
 
92
  11.0: '#D6E3E3', # Teal 25%
93
  13.3: '#B6B8B2', # RPM Green 25%
94
  14.8: '#CCD7E4', # Blue 25%
95
+ 16.8: '#FFEFCE', # Yellow 25%
96
+ # Medium Density colors
97
+ 4.5: '#B5A6C5', # Purple 50%
98
+ 6.0: '#C7BDD6', # Purple 25%
99
+ 7.5: '#DDD6E8', # Purple 15%
100
+ 7.0: '#8FB8BA', # Teal-Blue 50%
101
+ 8.0: '#B3D0D2' # Teal-Blue 25%
102
  },
103
  'rpm_monochrome': {
104
+ # All widths use grayscale
105
  8.5: '#2E3E2F', # RPM Green 100%
106
  10.5: '#545D51', # RPM Green 75%
107
  12.5: '#80857B', # RPM Green 50%
 
111
  11.0: '#D1D3D4', # Black 25%
112
  13.3: '#216767', # Teal (accent)
113
  14.8: '#415B6E', # Blue (accent)
114
+ 16.8: '#FF8E3C', # Yellow (accent)
115
+ # Medium Density
116
+ 4.5: '#4A4B4D', # Dark gray
117
+ 6.0: '#6B6C6E', # Medium gray
118
+ 7.5: '#8C8D8F', # Light gray
119
+ 7.0: '#5C5D5F', # Gray
120
+ 8.0: '#7D7E80' # Light gray
121
  }
122
  }
123
 
124
  self.current_scheme = 'rpm_primary'
125
  self.current_solution = None # Store current AI solution
126
+ self.development_mode = 'conventional' # conventional or medium_density
127
+ self.md_load_type = 'front' # front or rear
128
+
129
+ def set_development_mode(self, mode, load_type=None):
130
+ """Set the development mode and update lot specifications"""
131
+ self.development_mode = mode
132
+ if mode == 'medium_density':
133
+ if load_type == 'rear':
134
+ self.lot_specifications = self.md_rear_loaded_specifications
135
+ self.md_load_type = 'rear'
136
+ else:
137
+ self.lot_specifications = self.md_front_loaded_specifications
138
+ self.md_load_type = 'front'
139
+ else:
140
+ self.lot_specifications = self.conventional_lot_specifications
141
+ self.md_load_type = None
142
 
143
  def create_enhanced_visualization(self, solution, stage_width, stage_depth=32, title="Premium Grid Layout", show_variance=None):
144
+ """Create a clean 2D visualization with corner splays and optional laneway"""
145
+ # Adjust figure size for laneway if needed
146
+ fig_height = 14 if self.md_load_type == 'rear' else 12
147
+ fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(18, fig_height), gridspec_kw={'height_ratios': [3, 1]},
148
  facecolor='#2E3E2F')
149
 
150
  # Main visualization
 
155
 
156
  # Set up main plot with RPM green background
157
  ax1.set_xlim(-5, stage_width + 5)
158
+ # Adjust y-limits for rear laneway
159
+ if self.md_load_type == 'rear':
160
+ ax1.set_ylim(-10, 60) # Extended for laneway
161
+ else:
162
+ ax1.set_ylim(-10, 50)
163
  ax1.set_facecolor('#2E3E2F')
164
 
165
  # Add title with variance if provided
166
  if show_variance is not None:
167
  variance_color = '#216767' if abs(show_variance) < 0.001 else '#802B2B'
168
+ mode_text = "MD " if self.development_mode == 'medium_density' else ""
169
+ load_text = f"({self.md_load_type.title()} Loaded) " if self.md_load_type else ""
170
+ title_text = f"{mode_text}{load_text}{title}\nGrid Variance: {show_variance:+.1f}m"
171
  ax1.set_title(title_text, fontsize=28, fontweight='bold', pad=25, color='white')
172
  else:
173
  ax1.set_title(title, fontsize=28, fontweight='bold', pad=25, color='white')
174
 
175
  # Add subtle gradient background
176
  gradient = np.linspace(0.3, 0.1, 100).reshape(1, -1)
177
+ y_max = 60 if self.md_load_type == 'rear' else 50
178
+ ax1.imshow(gradient, extent=[-5, stage_width + 5, -10, y_max], aspect='auto',
179
  cmap='Greys', alpha=0.3, zorder=0)
180
 
181
  # Add street with label
 
186
  ax1.text(stage_width/2, -2, 'STREET', ha='center', va='center',
187
  fontsize=20, color='white', fontweight='bold')
188
 
189
+ # Draw lots with corner splays
190
  splay_size = 3 # 3m corner splay
191
+
192
+ # Get appropriate depth for current mode
193
+ if self.development_mode == 'medium_density':
194
+ # Use first available depth for MD lots
195
+ lot_height = 28 # Default
196
+ for width, _ in solution:
197
+ if width in self.lot_specifications:
198
+ lot_height = self.lot_specifications[width]['depths'][0]
199
+ break
200
+ else:
201
+ lot_height = 28 # Standard height for conventional
202
+
203
+ # Add rear laneway if rear loaded MD
204
+ if self.md_load_type == 'rear':
205
+ laneway_y = 8 + lot_height
206
+ laneway = Rectangle((-5, laneway_y), stage_width + 10, 7,
207
+ facecolor='#3A3A3A', alpha=0.9, zorder=1,
208
+ edgecolor='#FFCF6D', linewidth=2, linestyle='--')
209
+ ax1.add_patch(laneway)
210
+ ax1.text(stage_width/2, laneway_y + 3.5, 'REAR LANEWAY (7m)',
211
+ ha='center', va='center', fontsize=16, color='#FFCF6D',
212
+ fontweight='bold', alpha=0.9)
213
 
214
  for i, (width, lot_type) in enumerate(solution):
215
  # Get base color
216
  if width in colors:
217
  base_color = colors[width]
218
  else:
219
+ # Use MD colors if in MD mode
220
+ if self.development_mode == 'medium_density':
221
+ if width <= 6.0:
222
+ base_color = colors.get(4.5, '#6B4C8A')
223
+ else:
224
+ base_color = colors.get(7.0, '#4A7C7E')
225
+ else:
226
+ closest_width = min(colors.keys(), key=lambda x: abs(x - width))
227
+ base_color = colors[closest_width]
228
 
229
  # Check position
230
  is_corner = (i == 0 or i == len(solution) - 1)
 
234
  edge_color = 'white'
235
  linewidth = 4.0 if is_corner else 3.0
236
 
237
+ # Create lot shape with appropriate height
238
+ if is_corner and self.development_mode == 'conventional':
239
+ # Corner lot with splay for conventional only
240
  if i == 0: # First corner
241
  vertices = [
242
  (x_pos + splay_size, 8), # Start after splay
243
  (x_pos + width, 8),
244
+ (x_pos + width, 8 + lot_height),
245
  (x_pos, 8 + lot_height), # Straight rear
246
  (x_pos, 8 + splay_size) # Splay corner
247
  ]
 
250
  (x_pos, 8),
251
  (x_pos + width - splay_size, 8),
252
  (x_pos + width, 8 + splay_size), # Splay corner
253
+ (x_pos + width, 8 + lot_height),
254
  (x_pos, 8 + lot_height)
255
  ]
256
 
 
270
  ax1.plot([x_pos + width - splay_size, x_pos + width],
271
  [8, 8 + splay_size], 'white', linewidth=2, alpha=0.8)
272
  else:
273
+ # Regular lot (or MD corner without splay)
274
  lot = FancyBboxPatch((x_pos, 8), width, lot_height,
275
  boxstyle="round,pad=0.1",
276
  facecolor=face_color,
 
279
  zorder=3)
280
  ax1.add_patch(lot)
281
 
282
+ # Add subtle glow
283
  glow = FancyBboxPatch((x_pos - 0.2, 7.8), width + 0.4, lot_height + 0.4,
284
  boxstyle="round,pad=0.15",
285
  facecolor='none',
 
289
  zorder=2)
290
  ax1.add_patch(glow)
291
 
 
 
 
 
 
292
  # Add lot information (positioned consistently)
293
+ info_y_base = 48 if self.md_load_type == 'rear' else 40
294
+
295
+ ax1.text(x_pos + width/2, info_y_base, f'L{lot_num}',
296
  ha='center', va='center', fontsize=16, fontweight='bold', color='white')
297
 
298
+ ax1.text(x_pos + width/2, info_y_base - 5, f'{width:.1f}m',
299
  ha='center', va='center', fontsize=14, fontweight='bold', color='white')
300
 
301
  # Lot type
 
310
  spec = {**spec, 'type': 'Custom'}
311
 
312
  lot_type_text = spec['type']
313
+ if is_corner and self.development_mode == 'conventional':
314
  lot_type_text = "CORNER"
315
 
316
+ # Add build type for MD
317
+ if self.development_mode == 'medium_density' and 'build' in spec:
318
+ lot_type_text += f"\n{spec['build']}"
319
+
320
+ ax1.text(x_pos + width/2, info_y_base - 17, lot_type_text,
321
  ha='center', va='center', fontsize=11,
322
  bbox=dict(boxstyle="round,pad=0.3", facecolor='#545D51',
323
  edgecolor='white', alpha=0.9), color='white')
 
327
  ax1.plot([x_pos, x_pos], [10, 14], 'w-', linewidth=1, alpha=0.3)
328
  ax1.plot([x_pos + width, x_pos + width], [10, 14], 'w-', linewidth=1, alpha=0.3)
329
 
330
+ # Add garage indicators for rear loaded
331
+ if self.md_load_type == 'rear':
332
+ # Small garage icon at rear
333
+ garage_y = 8 + lot_height - 6
334
+ garage = Rectangle((x_pos + width/2 - 1.5, garage_y), 3, 5,
335
+ facecolor='#636466', edgecolor='white',
336
+ linewidth=1, alpha=0.8, zorder=4)
337
+ ax1.add_patch(garage)
338
+
339
  x_pos += width
340
  lot_num += 1
341
 
342
  # Add rear alignment line across all lots
343
+ rear_y = 8 + lot_height
344
+ if self.md_load_type != 'rear': # Don't show if laneway present
345
+ ax1.plot([0, stage_width], [rear_y, rear_y],
346
+ '#216767', linewidth=2, alpha=0.8, linestyle='-')
347
+ ax1.text(stage_width/2, rear_y + 1, 'REAR ALIGNMENT LINE',
348
+ ha='center', va='bottom', fontsize=12, color='#216767', alpha=0.8,
349
+ bbox=dict(boxstyle="round,pad=0.3", facecolor='#2E3E2F',
350
+ edgecolor='#216767', alpha=0.8))
351
 
352
  # Add stage dimensions
353
  arrow_props = dict(arrowstyle='<->', color='white', lw=3)
 
370
  unique_widths = len(set(w for w, _ in solution))
371
  diversity_score = unique_widths / len(set(self.lot_specifications.keys()))
372
 
373
+ if self.development_mode == 'conventional':
374
+ slhc_count = sum(1 for w, _ in solution if w <= 10.5)
375
+ standard_count = sum(1 for w, _ in solution if 10.5 < w <= 14)
376
+ premium_count = sum(1 for w, _ in solution if w > 14)
377
+
378
+ # SLHC pairs
379
+ slhc_pairs = 0
380
+ for i in range(len(solution) - 1):
381
+ if solution[i][0] <= 10.5 and solution[i+1][0] <= 10.5:
382
+ slhc_pairs += 1
383
+ else:
384
+ # MD metrics
385
+ narrow_count = sum(1 for w, _ in solution if w <= 6.0)
386
+ standard_count = sum(1 for w, _ in solution if 6.0 < w <= 8.0)
387
+ wide_count = sum(1 for w, _ in solution if w > 8.0)
388
+ slhc_pairs = 0 # Not applicable for MD
389
 
390
  # Calculate actual total width and variance
391
  total_width = sum(w for w, _ in solution)
392
  variance = total_width - stage_width
393
  efficiency = "100%" if abs(variance) < 0.001 else f"{(total_width/stage_width)*100:.1f}%"
394
 
395
+ # Calculate yield
396
+ if self.development_mode == 'medium_density':
397
+ # Assume potential for duplex on lots ≥ 7m
398
+ potential_dwellings = sum(2 if w >= 7.0 else 1 for w, _ in solution)
399
+ yield_text = f"🏘️ Potential Dwellings: {potential_dwellings}"
400
+ else:
401
+ yield_text = f"💰 Revenue: ${total_lots * 0.5:.1f}M - ${total_lots * 1.2:.1f}M"
402
+
403
  metrics_lines = [
404
  f"📊 TOTAL LOTS: {total_lots}",
405
  f"📐 LAND EFFICIENCY: {efficiency}",
406
  f"🎯 DIVERSITY: {diversity_score:.0%} ({unique_widths} types)",
407
  f"📏 GRID VARIANCE: {variance:+.2f}m",
408
  "",
409
+ f"{'Narrow (≤6m)' if self.development_mode == 'medium_density' else 'SLHC (≤10.5m)'}: {narrow_count if self.development_mode == 'medium_density' else slhc_count} lots",
410
+ f"{'Standard (6-8m)' if self.development_mode == 'medium_density' else 'Standard (11-14m)'}: {standard_count} lots",
411
+ f"{'Wide (>8m)' if self.development_mode == 'medium_density' else 'Premium (>14m)'}: {wide_count if self.development_mode == 'medium_density' else premium_count} lots",
412
  "",
413
+ f"{'🚗 Access: ' + ('Rear Laneway' if self.md_load_type == 'rear' else 'Front Loaded') if self.development_mode == 'medium_density' else f'🚗 SLHC Pairs: {slhc_pairs}'}",
414
+ yield_text
415
  ]
416
 
417
  col1_text = '\n'.join(metrics_lines[:5])
 
480
  feedback = f"⚠️ Grid is {-variance:.2f}m too narrow. Add {-variance:.2f}m total width."
481
 
482
  # Add suggestions if not perfect
483
+ min_width = 4.5 if self.development_mode == 'medium_density' else 8.5
484
+
485
  if abs(variance) > 0.001:
486
  if variance > 0:
487
  # Suggest which lots could be reduced
488
  suggestions = []
489
  for i, w in enumerate(widths):
490
+ if w - variance >= min_width: # Minimum viable width
491
  suggestions.append(f"L{i+1}: reduce from {w:.1f}m to {w-variance:.1f}m")
492
  if suggestions:
493
  feedback += f"\n\nSuggestions:\n" + "\n".join(suggestions[:3])
 
505
  return ""
506
  return ", ".join([f"{w:.1f}" for w, _ in solution])
507
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
508
  def find_optimal_custom_corners(self, stage_width, internal_widths, base_corner_width, tolerance=0.5):
509
  """Find optimal corner widths that can vary slightly from base width"""
510
  best_solution = None
511
  best_fitness = -float('inf')
512
 
513
+ # For MD, corners don't need to be wider than internal
514
+ if self.development_mode == 'medium_density':
515
+ min_internal = min(internal_widths) if internal_widths else 4.5
516
+ min_corner_width = base_corner_width - tolerance
517
+ else:
518
+ # Ensure corners are at least as wide as smallest internal lot
519
+ min_internal = min(internal_widths) if internal_widths else 8.5
520
+ min_corner_width = max(base_corner_width - tolerance, min_internal)
521
 
522
  # Try variations of corner widths within tolerance
523
  variations = np.arange(min_corner_width,
 
535
  internal_solution = self.find_exact_solution_with_diversity(internal_width, internal_widths)
536
 
537
  if internal_solution:
538
+ # For conventional, verify no internal lot is wider than corners
539
+ if self.development_mode == 'conventional':
540
+ max_internal = max(internal_solution) if internal_solution else 0
541
+ if max_internal > min(corner1, corner2):
542
+ continue
543
 
544
  # Build complete solution
545
  solution = [(round(corner1, 1), 'corner')]
 
574
 
575
  # Strategy 2: Try flexible corners if enabled
576
  if allow_custom_corners and standard_internal:
577
+ # For MD, try all widths as potential corners
578
+ if self.development_mode == 'medium_density':
579
+ corner_bases = list(enabled_widths)
580
+ else:
581
+ # For conventional, use traditional corner widths
582
+ corner_bases = [11.0, 13.3, 14.8, 16.8, 14.0, 16.0]
583
+
584
+ for base_width in corner_bases:
585
  if any(abs(w - base_width) < 2 for w in enabled_widths):
586
  custom_solution = self.find_optimal_custom_corners(
587
  stage_width, standard_internal, base_width, tolerance=0.5
 
599
 
600
  # Separate widths by size
601
  all_widths = sorted(enabled_widths)
 
602
 
603
+ if self.development_mode == 'medium_density':
604
+ # For MD, any width can be a corner
605
+ corner_options = all_widths
606
+ min_internal_width = min(all_widths) if all_widths else 4.5
607
+ else:
608
+ # For conventional, maintain hierarchy
609
+ min_internal_width = min(all_widths)
610
+ corner_options = [w for w in enabled_widths if w >= max(11.0, min_internal_width)]
611
 
612
  best_solution = None
613
  best_fitness = -float('inf')
 
629
  )
630
 
631
  for internal_widths in internal_solutions:
632
+ # For conventional, verify no internal lot is wider than corners
633
+ if self.development_mode == 'conventional':
634
+ max_internal = max(internal_widths) if internal_widths else 0
635
+ if max_internal > min(corner1, corner2):
636
+ continue
637
 
638
  # Build complete solution
639
  solution = [(corner1, 'corner')]
 
641
  solution.append((corner2, 'corner'))
642
 
643
  # Optimize arrangement
644
+ optimized = self.optimize_lot_grouping(solution)
645
  fitness = self.evaluate_solution_with_diversity(optimized, stage_width)
646
 
647
  if fitness > best_fitness:
648
  best_fitness = fitness
649
  best_solution = optimized
650
 
651
+ # If no good solution, try without strict corner rules
652
  if not best_solution:
653
  all_solutions = []
654
  self.find_all_combinations_recursive(stage_width, sorted(enabled_widths),
655
  [], all_solutions, 20)
656
 
657
  for widths in all_solutions[:50]:
658
+ # For MD, just use the solution as-is
659
+ if self.development_mode == 'medium_density':
 
 
 
 
 
 
660
  solution = [(w, 'standard') for w in widths]
661
+ else:
662
+ # For conventional, ensure corners are among the largest lots
663
+ sorted_widths = sorted(widths)
664
+ if len(sorted_widths) >= 2:
665
+ solution = [(sorted_widths[-1], 'corner')]
666
+ solution.extend([(w, 'standard') for w in sorted_widths[:-2]])
667
+ solution.append((sorted_widths[-2], 'corner'))
668
+ else:
669
+ solution = [(w, 'standard') for w in widths]
670
 
671
+ optimized = self.optimize_lot_grouping(solution)
672
  fitness = self.evaluate_solution_with_diversity(optimized, stage_width)
673
 
674
  if fitness > best_fitness:
 
677
 
678
  return best_solution
679
 
680
+ def optimize_lot_grouping(self, lots):
681
+ """Optimize lot arrangement based on development mode"""
682
+ if self.development_mode == 'medium_density':
683
+ return self.optimize_md_grouping(lots)
684
+ else:
685
+ return self.optimize_slhc_grouping(lots)
686
+
687
+ def optimize_md_grouping(self, lots):
688
+ """Optimize lot arrangement for medium density"""
689
+ if not lots or len(lots) <= 1:
690
+ return lots
691
+
692
+ # Separate lots by width
693
+ narrow_lots = [] # 4.5-6m
694
+ medium_lots = [] # 7-8m
695
+ wide_lots = [] # >8m
696
+
697
+ for width, lot_type in lots:
698
+ if width <= 6.0:
699
+ narrow_lots.append((width, lot_type))
700
+ elif width <= 8.0:
701
+ medium_lots.append((width, lot_type))
702
+ else:
703
+ wide_lots.append((width, lot_type))
704
+
705
+ # Build optimized layout
706
+ optimized = []
707
+
708
+ # For rear loaded, group similar widths for efficient laneway access
709
+ if self.md_load_type == 'rear':
710
+ # Group narrow lots together
711
+ optimized.extend(narrow_lots)
712
+ optimized.extend(medium_lots)
713
+ optimized.extend(wide_lots)
714
+ else:
715
+ # For front loaded, alternate sizes for variety
716
+ while narrow_lots or medium_lots or wide_lots:
717
+ if wide_lots:
718
+ optimized.append(wide_lots.pop(0))
719
+ if narrow_lots:
720
+ optimized.append(narrow_lots.pop(0))
721
+ if medium_lots:
722
+ optimized.append(medium_lots.pop(0))
723
+
724
+ return optimized
725
+
726
  def find_diverse_combinations(self, target_width, available_widths, max_solutions=20):
727
  """Find combinations that maximize diversity"""
728
  all_solutions = []
 
829
  current.pop()
830
 
831
  def optimize_slhc_grouping(self, lots):
832
+ """Optimize lot arrangement with sophisticated rules for conventional"""
833
  if not lots or len(lots) <= 1:
834
  return lots
835
 
 
963
  fitness -= max_repetition * 500 # Penalty for too many of same width
964
  fitness += diversity_ratio * 3000 # Bonus for good diversity ratio
965
 
966
+ # Mode-specific evaluation
967
+ if self.development_mode == 'conventional':
968
+ # Corner evaluation for conventional
969
+ if len(solution) >= 2:
970
+ first_width = solution[0][0]
971
+ last_width = solution[-1][0]
972
+
973
+ # Penalty for SLHC on corners
974
+ if first_width <= 10.5:
975
+ fitness -= 2000
976
+ if last_width <= 10.5:
977
+ fitness -= 2000
978
+
979
+ # Bonus for good corners
980
+ if first_width >= 11.0:
981
+ fitness += 1000
982
+ if last_width >= 11.0:
983
+ fitness += 1000
984
+
985
+ # Balance bonus
986
+ corner_diff = abs(first_width - last_width)
987
+ if corner_diff < 0.1:
988
+ fitness += 1500 # Perfect match
989
+ elif corner_diff <= 1.0:
990
+ fitness += 1000 # Very good
991
+ elif corner_diff <= 2.0:
992
+ fitness += 500 # Good
993
+ else:
994
+ fitness -= 500 # Poor balance
995
 
996
+ # SLHC grouping bonus
997
+ for i in range(len(solution) - 1):
998
+ if solution[i][0] <= 10.5 and solution[i+1][0] <= 10.5:
999
+ fitness += 300 # Adjacent SLHC bonus
 
1000
 
1001
+ # Penalize corner-specific widths used internally
1002
+ for i in range(1, len(solution) - 1):
1003
+ if solution[i][0] in self.corner_specific:
1004
+ fitness -= 200
1005
+ else:
1006
+ # MD-specific bonuses
1007
+ if self.md_load_type == 'rear':
1008
+ # Bonus for grouping similar widths (efficient laneway access)
1009
+ for i in range(len(solution) - 1):
1010
+ if abs(solution[i][0] - solution[i+1][0]) < 1.5:
1011
+ fitness += 200
1012
 
1013
+ # Bonus for potential duplex lots (≥7m)
1014
+ duplex_count = sum(1 for w, _ in solution if w >= 7.0)
1015
+ fitness += duplex_count * 500
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1016
 
1017
  return fitness
1018
 
 
1037
  total_width = sum(w for w, _ in solution)
1038
  variance = total_width - stage_width
1039
 
1040
+ # Mode-specific title
1041
+ mode_text = "MEDIUM DENSITY " if self.development_mode == 'medium_density' else ""
1042
+ load_text = f"({self.md_load_type.upper()} LOADED) " if self.md_load_type else ""
1043
+
1044
  report = f"""
1045
+ # {mode_text}{load_text}SUBDIVISION OPTIMIZATION REPORT
1046
  ## Project Analysis for {stage_width}m × {stage_depth}m Stage
1047
 
1048
  ### EXECUTIVE SUMMARY
1049
+ - **Development Type**: {self.development_mode.replace('_', ' ').title()}
1050
  - **Total Lots**: {len(solution)}
1051
  - **Unique Lot Types**: {unique_widths}
1052
  - **Land Efficiency**: {"100%" if abs(variance) < 0.001 else f"{(total_width/stage_width)*100:.1f}%"}
 
1054
  - **Stage Dimensions**: {stage_width}m × {stage_depth}m
1055
  - **Total Area**: {stage_width * stage_depth}m²
1056
  {f"- **Custom Widths Used**: {', '.join(custom_widths)}" if custom_widths else ""}
 
 
1057
  """
1058
 
1059
+ # Add MD-specific info
1060
+ if self.development_mode == 'medium_density':
1061
+ potential_dwellings = sum(2 if w >= 7.0 else 1 for w, _ in solution)
1062
+ density = potential_dwellings / (stage_width * stage_depth / 10000) # per hectare
1063
+ report += f"- **Potential Dwellings**: {potential_dwellings} ({density:.0f} dwellings/ha)\n"
1064
+ report += f"- **Access Type**: {'Rear Laneway (7m)' if self.md_load_type == 'rear' else 'Front Loaded'}\n"
1065
+
1066
+ report += f"\n### LOT DIVERSITY ANALYSIS\n"
1067
+
1068
  # Sort by count to show distribution
1069
  sorted_widths = sorted(width_counts.items(), key=lambda x: x[1], reverse=True)
1070
  for width, count in sorted_widths:
1071
  percentage = (count / len(solution)) * 100
1072
  if width in self.lot_specifications:
1073
  spec = self.lot_specifications[width]
1074
+ build_info = f" [{spec.get('build', 'N/A')}]" if 'build' in spec else ""
1075
+ report += f"- **{width:.1f}m** × {count} ({percentage:.1f}%): {spec['type']}{build_info}\n"
1076
  else:
1077
  report += f"- **{width:.1f}m** × {count} ({percentage:.1f}%): Custom Width\n"
1078
 
1079
+ # Corner analysis (only for conventional)
1080
+ if self.development_mode == 'conventional' and len(solution) >= 2:
1081
  report += f"\n### CORNER ANALYSIS\n"
1082
  report += f"- **Front Corner**: {solution[0][0]:.1f}m with 3m × 3m splay\n"
1083
  report += f"- **Rear Corner**: {solution[-1][0]:.1f}m with 3m × 3m splay\n"
1084
  report += f"- **Balance**: {abs(solution[0][0] - solution[-1][0]):.1f}m difference\n"
1085
 
1086
  report += f"\n### DESIGN FEATURES\n"
1087
+ if self.development_mode == 'medium_density':
1088
+ if self.md_load_type == 'rear':
1089
+ report += f"- 7m rear laneway provides vehicle access and services\n"
1090
+ report += f"- Garages positioned at rear for better street presentation\n"
1091
+ else:
1092
+ report += f"- Front loaded design with integrated garages\n"
1093
+ report += f"- Compact lots maximize dwelling yield\n"
1094
+ report += f"- Potential for duplex/triplex on wider lots (≥7m)\n"
1095
+ else:
1096
+ report += f"- Corner splays provide safe sight lines at intersections\n"
1097
+ report += f"- All lots have identical rear alignment for visual consistency\n"
1098
+ report += f"- Diverse lot mix ensures varied streetscape\n"
1099
+ report += f"- SLHC lots grouped for efficient garbage collection\n"
1100
 
1101
  report += f"\n---\n*Report generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}*"
1102
 
 
1115
  def create_advanced_app():
1116
  optimizer = AdvancedGridOptimizer()
1117
 
1118
+ def update_available_widths(development_mode, md_load_type):
1119
+ """Update the available width options based on development mode"""
1120
+ if development_mode == "Medium Density":
1121
+ if md_load_type == "Rear Loaded":
1122
+ # Rear loaded MD widths
1123
+ return gr.update(visible=False), gr.update(visible=True), gr.update(visible=False)
1124
+ else:
1125
+ # Front loaded MD widths
1126
+ return gr.update(visible=False), gr.update(visible=False), gr.update(visible=True)
1127
+ else:
1128
+ # Conventional widths
1129
+ return gr.update(visible=True), gr.update(visible=False), gr.update(visible=False)
1130
+
1131
  def optimize_grid(
1132
  stage_width,
1133
  stage_depth,
1134
+ development_mode,
1135
+ md_load_type,
1136
+ # Conventional widths
1137
  enable_8_5, enable_10_5, enable_12_5, enable_14, enable_16, enable_18,
1138
  enable_corners, enable_11, enable_13_3, enable_14_8, enable_16_8,
1139
+ # MD rear widths
1140
+ enable_4_5, enable_6_0, enable_7_5,
1141
+ # MD front widths
1142
+ enable_7_0, enable_8_0, enable_md_8_5, enable_md_10_5,
1143
  allow_custom_corners, color_scheme
1144
  ):
1145
+ # Update optimizer mode
1146
+ if development_mode == "Medium Density":
1147
+ optimizer.set_development_mode('medium_density', 'rear' if md_load_type == "Rear Loaded" else 'front')
1148
+ else:
1149
+ optimizer.set_development_mode('conventional')
1150
+
1151
  # Update color scheme
1152
  optimizer.current_scheme = color_scheme
1153
 
1154
+ # Collect enabled widths based on mode
1155
  enabled_widths = []
1156
+
1157
+ if development_mode == "Conventional Land":
1158
+ if enable_8_5: enabled_widths.append(8.5)
1159
+ if enable_10_5: enabled_widths.append(10.5)
1160
+ if enable_12_5: enabled_widths.append(12.5)
1161
+ if enable_14: enabled_widths.append(14.0)
1162
+ if enable_16: enabled_widths.append(16.0)
1163
+ if enable_18: enabled_widths.append(18.0)
1164
+
1165
+ if enable_corners:
1166
+ if enable_11: enabled_widths.append(11.0)
1167
+ if enable_13_3: enabled_widths.append(13.3)
1168
+ if enable_14_8: enabled_widths.append(14.8)
1169
+ if enable_16_8: enabled_widths.append(16.8)
1170
+ else:
1171
+ if md_load_type == "Rear Loaded":
1172
+ if enable_4_5: enabled_widths.append(4.5)
1173
+ if enable_6_0: enabled_widths.append(6.0)
1174
+ if enable_7_5: enabled_widths.append(7.5)
1175
+ else:
1176
+ if enable_7_0: enabled_widths.append(7.0)
1177
+ if enable_8_0: enabled_widths.append(8.0)
1178
+ if enable_md_8_5: enabled_widths.append(8.5)
1179
+ if enable_md_10_5: enabled_widths.append(10.5)
1180
 
1181
  if not enabled_widths:
1182
  return None, None, pd.DataFrame(), "Please select at least one lot width!", "", ""
 
1198
 
1199
  # Verify solution
1200
  if not optimized_solution or abs(sum(w for w, _ in optimized_solution) - stage_width) > 0.001:
1201
+ # Provide mode-specific suggestions
1202
+ if development_mode == "Medium Density":
1203
+ width_suggestions = "4.5m, 6m, 7.5m" if md_load_type == "Rear Loaded" else "7m, 8m, 8.5m, 10.5m"
1204
+ stage_suggestions = "54m, 72m, 90m"
1205
+ else:
1206
+ width_suggestions = "8.5m-18m plus corner widths"
1207
+ stage_suggestions = "84m, 105m, 126m"
1208
+
1209
  return None, pd.DataFrame(), f"""
1210
  ### ❌ Cannot achieve 100% usage with selected widths
1211
 
1212
  **Stage Width**: {stage_width}m
1213
+ **Mode**: {development_mode} {f'({md_load_type})' if development_mode == 'Medium Density' else ''}
1214
  **Available Widths**: {', '.join([f"{w}m" for w in sorted(enabled_widths)])}
1215
 
1216
  **Try:**
1217
  1. Enable more lot types for flexibility
1218
  2. Enable "Custom Corners" option
1219
+ 3. Try common stage widths: {stage_suggestions}
1220
+ 4. Available widths: {width_suggestions}
1221
  """, "", ""
1222
 
1223
  # Create visualizations with variance indicator
1224
+ title = f"{'MD ' if development_mode == 'Medium Density' else ''}Grid Cut Optimization"
1225
  fig_2d = optimizer.create_enhanced_visualization(
1226
  optimized_solution, stage_width, stage_depth,
1227
+ title,
1228
  show_variance=variance
1229
  )
1230
 
 
1251
  'count': 1,
1252
  'type': spec.get('type', 'Custom'),
1253
  'squares': spec.get('squares', 'N/A'),
1254
+ 'area': width * stage_depth,
1255
+ 'build': spec.get('build', 'N/A')
1256
  }
1257
 
1258
  results_data = []
1259
  for width, info in sorted(width_counts.items()):
1260
+ row_data = {
1261
  'Lot Width': width,
1262
  'Count': info['count'],
1263
  'Type': info['type'],
1264
  'Area Each': f"{info['area']:.0f}m²",
1265
  'Total Width': f"{float(width[:-1]) * info['count']:.1f}m",
1266
  'Total Area': f"{info['area'] * info['count']:.0f}m²"
1267
+ }
1268
+ if development_mode == "Medium Density":
1269
+ row_data['Build Type'] = info['build']
1270
+ results_data.append(row_data)
1271
 
1272
  results_df = pd.DataFrame(results_data)
1273
 
 
1278
  total_lots = len(optimized_solution)
1279
  unique_widths = len(set(w for w, _ in optimized_solution))
1280
 
1281
+ if development_mode == "Medium Density":
1282
+ # MD specific metrics
1283
+ potential_dwellings = sum(2 if w >= 7.0 else 1 for w, _ in optimized_solution)
1284
+ density = potential_dwellings / (stage_width * stage_depth / 10000)
 
 
 
 
 
 
1285
 
1286
+ summary = f"""
 
 
 
 
 
 
 
 
 
1287
  **Stage**: {stage_width}m × {stage_depth}m = {stage_width * stage_depth}m²
1288
+ **Development**: {development_mode} ({md_load_type})
1289
  **Total Lots**: {total_lots}
1290
+ **Potential Dwellings**: {potential_dwellings} ({density:.0f}/ha)
1291
  **Unique Lot Types**: {unique_widths}
1292
  **Grid Variance**: {variance:+.2f}m {"✅" if abs(variance) < 0.001 else "⚠️"}
1293
+ """
1294
+ else:
1295
+ # Conventional metrics
1296
+ slhc_pairs = sum(1 for i in range(len(optimized_solution) - 1)
1297
+ if optimized_solution[i][0] <= 10.5 and optimized_solution[i+1][0] <= 10.5)
1298
+
1299
+ summary = f"""
1300
+ **Stage**: {stage_width}m × {stage_depth}m = {stage_width * stage_depth}m²
1301
+ **Total Lots**: {total_lots}
1302
+ **Unique Lot Types**: {unique_widths}
1303
+ **SLHC Pairs**: {slhc_pairs}
1304
+ **Grid Variance**: {variance:+.2f}m {"✅" if abs(variance) < 0.001 else "⚠️"}
1305
  """
1306
 
1307
  # Convert solution to string for manual editing
 
1309
 
1310
  return fig_2d, results_df, summary, report, manual_edit_string
1311
 
1312
+ def update_manual_adjustment(manual_widths_text, stage_width, stage_depth, development_mode, md_load_type, color_scheme):
1313
  """Update visualization based on manual adjustment"""
1314
+ # Set mode
1315
+ if development_mode == "Medium Density":
1316
+ optimizer.set_development_mode('medium_density', 'rear' if md_load_type == "Rear Loaded" else 'front')
1317
+ else:
1318
+ optimizer.set_development_mode('conventional')
1319
+
1320
  optimizer.current_scheme = color_scheme
1321
 
1322
  # Parse manual widths
 
1403
  with gr.Row():
1404
  with gr.Column(scale=1):
1405
  with gr.Group():
1406
+ gr.Markdown("### 📐 Stage Configuration")
1407
+
1408
+ development_mode = gr.Radio(
1409
+ ["Conventional Land", "Medium Density"],
1410
+ label="🏘️ Development Mode",
1411
+ value="Conventional Land",
1412
+ info="Select the type of development"
1413
+ )
1414
+
1415
+ md_load_type = gr.Radio(
1416
+ ["Front Loaded", "Rear Loaded"],
1417
+ label="🚗 MD Access Type",
1418
+ value="Front Loaded",
1419
+ visible=False,
1420
+ info="Rear loaded includes 7m laneway"
1421
+ )
1422
+
1423
  stage_width = gr.Number(
1424
  label="Stage Width (m)",
1425
  value=105.0,
 
1433
 
1434
  gr.Markdown("### 📏 Lot Width Options")
1435
 
1436
+ # Conventional widths group
1437
+ with gr.Group(visible=True) as conventional_group:
1438
  gr.Markdown("**Standard Widths**")
1439
  with gr.Row():
1440
  enable_8_5 = gr.Checkbox(label="8.5m SLHC", value=True)
 
1444
  enable_14 = gr.Checkbox(label="14.0m", value=True)
1445
  enable_16 = gr.Checkbox(label="16.0m", value=True)
1446
  enable_18 = gr.Checkbox(label="18.0m", value=False)
1447
+
 
1448
  enable_corners = gr.Checkbox(
1449
  label="Enable Corner-Specific Widths",
1450
  value=True,
 
1456
  with gr.Row():
1457
  enable_14_8 = gr.Checkbox(label="14.8m", value=True)
1458
  enable_16_8 = gr.Checkbox(label="16.8m", value=True)
1459
+
1460
+ # MD Rear Loaded widths
1461
+ with gr.Group(visible=False) as md_rear_group:
1462
+ gr.Markdown("**MD Rear Loaded Widths**")
1463
+ enable_4_5 = gr.Checkbox(label="4.5m (2/2/1)", value=True)
1464
+ enable_6_0 = gr.Checkbox(label="6.0m (3/2/2)", value=True)
1465
+ enable_7_5 = gr.Checkbox(label="7.5m (3-4/2/2)", value=True)
1466
+
1467
+ # MD Front Loaded widths
1468
+ with gr.Group(visible=False) as md_front_group:
1469
+ gr.Markdown("**MD Front Loaded Widths**")
1470
+ enable_7_0 = gr.Checkbox(label="7.0m (3/2/1)", value=True)
1471
+ enable_8_0 = gr.Checkbox(label="8.0m (3-4/2/2)", value=True)
1472
+ enable_md_8_5 = gr.Checkbox(label="8.5m (3/2/1)", value=True)
1473
+ enable_md_10_5 = gr.Checkbox(label="10.5m (3-4/2/2)", value=True)
1474
 
1475
  with gr.Column(scale=1):
1476
  gr.Markdown("### ⚙️ Settings")
 
1497
 
1498
  gr.Markdown("""
1499
  ### 💡 Quick Tips:
1500
+ - **Conventional**: Traditional lots with corner splays
1501
+ - **Medium Density**: Compact lots for higher yield
1502
+ - **Rear Loaded**: Includes 7m laneway visualization
1503
  - **Grid Variance**: Shows if layout is perfect (0.0m)
 
 
1504
  """)
1505
 
1506
  with gr.Row():
1507
+ plot_2d = gr.Plot(label="2D Layout Visualization")
1508
 
1509
  # Manual adjustment section
1510
  gr.Markdown("### ✏️ Fine-Tune Result")
 
1532
  with gr.Column():
1533
  report_output = gr.Markdown(label="Professional Report")
1534
 
1535
+ # Wire up development mode changes
1536
+ def handle_mode_change(mode):
1537
+ if mode == "Medium Density":
1538
+ return gr.update(visible=True), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)
1539
+ else:
1540
+ return gr.update(visible=False), gr.update(visible=True), gr.update(visible=False), gr.update(visible=False)
1541
+
1542
+ def handle_md_type_change(md_type):
1543
+ if md_type == "Rear Loaded":
1544
+ return gr.update(visible=True), gr.update(visible=False)
1545
+ else:
1546
+ return gr.update(visible=False), gr.update(visible=True)
1547
+
1548
+ development_mode.change(
1549
+ handle_mode_change,
1550
+ inputs=[development_mode],
1551
+ outputs=[md_load_type, conventional_group, md_rear_group, md_front_group]
1552
+ )
1553
+
1554
+ md_load_type.change(
1555
+ handle_md_type_change,
1556
+ inputs=[md_load_type],
1557
+ outputs=[md_rear_group, md_front_group]
1558
+ )
1559
+
1560
+ # Wire up the optimize button
1561
  optimize_btn.click(
1562
  optimize_grid,
1563
  inputs=[
1564
  stage_width,
1565
  stage_depth,
1566
+ development_mode,
1567
+ md_load_type,
1568
+ # Conventional
1569
  enable_8_5, enable_10_5, enable_12_5, enable_14, enable_16, enable_18,
1570
  enable_corners, enable_11, enable_13_3, enable_14_8, enable_16_8,
1571
+ # MD Rear
1572
+ enable_4_5, enable_6_0, enable_7_5,
1573
+ # MD Front
1574
+ enable_7_0, enable_8_0, enable_md_8_5, enable_md_10_5,
1575
  allow_custom_corners, color_scheme
1576
  ],
1577
  outputs=[plot_2d, results_table, summary_output, report_output, manual_widths]
 
1579
 
1580
  update_btn.click(
1581
  update_manual_adjustment,
1582
+ inputs=[manual_widths, stage_width, stage_depth, development_mode, md_load_type, color_scheme],
1583
  outputs=[plot_2d, adjustment_feedback]
1584
  )
1585