Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import pandas as pd | |
| import ezdxf | |
| import matplotlib.pyplot as plt | |
| import numpy as np | |
| import io | |
| import tempfile | |
| import json | |
| from pathlib import Path | |
| from PIL import Image | |
| # ============================================================ | |
| # ۱. الگوریتم چیدمان هوشمند (Rectangle Packing) | |
| # ============================================================ | |
| class FloorplanEngine: | |
| """ | |
| موتور چیدمان پلان مبتنی بر Rectangle Packing | |
| با رعایت محدودیتهای معماری | |
| """ | |
| def __init__(self, site_width, site_depth, setback_front, setback_back, | |
| setback_left, setback_right, corridor_width=1.2): | |
| self.site_width = site_width | |
| self.site_depth = site_depth | |
| self.corridor_width = corridor_width | |
| # محاسبه محدوده قابل ساخت | |
| self.min_x = setback_right | |
| self.max_x = site_width - setback_left | |
| self.min_y = setback_front | |
| self.max_y = site_depth - setback_back | |
| self.buildable_width = self.max_x - self.min_x | |
| self.buildable_depth = self.max_y - self.min_y | |
| # شبکه اشغال (Grid) برای تشخیص فضای خالی | |
| self.grid_resolution = 0.1 # هر سلول ۱۰ سانتیمتر | |
| self.grid_cols = int(self.buildable_width / self.grid_resolution) + 1 | |
| self.grid_rows = int(self.buildable_depth / self.grid_resolution) + 1 | |
| self.grid = np.zeros((self.grid_rows, self.grid_cols), dtype=bool) | |
| def _world_to_grid(self, x, y): | |
| """تبدیل مختصات واقعی به مختصات گرید""" | |
| col = int((x - self.min_x) / self.grid_resolution) | |
| row = int((y - self.min_y) / self.grid_resolution) | |
| return max(0, min(row, self.grid_rows - 1)), max(0, min(col, self.grid_cols - 1)) | |
| def _is_space_free(self, x, y, width, height): | |
| """بررسی آزاد بودن یک مستطیل در گرید""" | |
| r1, c1 = self._world_to_grid(x, y) | |
| r2, c2 = self._world_to_grid(x + width, y + height) | |
| if r2 >= self.grid_rows or c2 >= self.grid_cols: | |
| return False | |
| # بررسی عدم برخورد با حاشیه امن | |
| margin = int(0.3 / self.grid_resolution) # ۳۰ سانت حاشیه | |
| r1 = max(0, r1 - margin) | |
| c1 = max(0, c1 - margin) | |
| r2 = min(self.grid_rows - 1, r2 + margin) | |
| c2 = min(self.grid_cols - 1, c2 + margin) | |
| return not np.any(self.grid[r1:r2+1, c1:c2+1]) | |
| def _mark_occupied(self, x, y, width, height): | |
| """علامتگذاری یک مستطیل به عنوان اشغال شده""" | |
| r1, c1 = self._world_to_grid(x, y) | |
| r2, c2 = self._world_to_grid(x + width, y + height) | |
| self.grid[r1:r2+1, c1:c2+1] = True | |
| def _find_best_position(self, width, height, prefer_facade='south'): | |
| """ | |
| پیدا کردن بهترین موقعیت برای یک مستطیل | |
| با ترجیح جبهه جنوبی برای فضاهای نورگیر | |
| """ | |
| best_pos = None | |
| best_score = float('inf') | |
| step = int(0.5 / self.grid_resolution) # گام ۵۰ سانتی | |
| for r in range(0, self.grid_rows, step): | |
| y = self.min_y + r * self.grid_resolution | |
| if y + height > self.max_y: | |
| break | |
| for c in range(0, self.grid_cols, step): | |
| x = self.min_x + c * self.grid_resolution | |
| if x + width > self.max_x: | |
| break | |
| if self._is_space_free(x, y, width, height): | |
| # امتیازدهی: نزدیکی به جبهه مطلوب + نزدیکی به مبدأ | |
| score = r * 10 # ترجیح پایین بودن (جنوب) | |
| if prefer_facade == 'north': | |
| score = (self.grid_rows - r) * 10 # ترجیح بالا بودن | |
| score += c # ترجیح چپ بودن | |
| if score < best_score: | |
| best_score = score | |
| best_pos = (x, y) | |
| return best_pos | |
| def layout(self, spaces): | |
| """ | |
| چیدمان فضاها با الگوریتم Packing | |
| spaces: لیست dict با keys: name, area, min_width, type, daylit, privacy | |
| """ | |
| # مرتبسازی: اول فضاهای بزرگ، سپس فضاهای نورگیر | |
| def sort_key(s): | |
| return (s.get('daylit', True) == False, -s['area']) | |
| sorted_spaces = sorted(spaces, key=sort_key) | |
| layout = [] | |
| for space in sorted_spaces: | |
| area = space['area'] | |
| min_w = space.get('min_width', 2.0) | |
| daylit = space.get('daylit', True) | |
| # محاسبه ابعاد بهینه | |
| best_w, best_h = self._optimal_dimensions(area, min_w) | |
| # تلاش برای چیدن با ابعاد مختلف | |
| placed = False | |
| for scale in [1.0, 0.9, 1.1, 0.8, 1.2, 1.3]: | |
| w = best_w * scale | |
| h = area / w | |
| pos = self._find_best_position(w, h, 'south' if daylit else 'north') | |
| if pos: | |
| x, y = pos | |
| self._mark_occupied(x, y, w, h) | |
| layout.append({ | |
| 'name': space['name'], | |
| 'x': x, 'y': y, | |
| 'width': w, 'height': h, | |
| 'type': space.get('type', 'unknown') | |
| }) | |
| placed = True | |
| break | |
| if not placed: | |
| # آخرین تلاش: هر طور شده جا بده | |
| w = max(min_w, np.sqrt(area)) | |
| h = area / w | |
| pos = self._find_best_position(w, h, 'any') | |
| if pos: | |
| x, y = pos | |
| self._mark_occupied(x, y, w, h) | |
| layout.append({ | |
| 'name': space['name'], | |
| 'x': x, 'y': y, | |
| 'width': w, 'height': h, | |
| 'type': space.get('type', 'unknown') | |
| }) | |
| return layout | |
| def _optimal_dimensions(self, area, min_width): | |
| """محاسبه ابعاد بهینه با نسبت ۱:۱.۵""" | |
| w = np.sqrt(area * 1.5) | |
| h = area / w | |
| if w < min_width: | |
| w = min_width | |
| h = area / w | |
| return w, h | |
| # ============================================================ | |
| # ۲. تولید خروجیها | |
| # ============================================================ | |
| def create_dxf(layout, site_width, site_depth, setbacks): | |
| """تولید فایل DXF با زمین و دیوارها""" | |
| doc = ezdxf.new(setup=True) | |
| doc.units = ezdxf.units.M | |
| msp = doc.modelspace() | |
| doc.layers.add(name="SITE", color=8) | |
| doc.layers.add(name="BUILDABLE", color=9) | |
| doc.layers.add(name="WALLS", color=1) | |
| doc.layers.add(name="TEXT", color=7) | |
| # محدوده زمین | |
| msp.add_line((0, 0), (site_width, 0), dxfattribs={'layer': 'SITE'}) | |
| msp.add_line((site_width, 0), (site_width, site_depth), dxfattribs={'layer': 'SITE'}) | |
| msp.add_line((site_width, site_depth), (0, site_depth), dxfattribs={'layer': 'SITE'}) | |
| msp.add_line((0, site_depth), (0, 0), dxfattribs={'layer': 'SITE'}) | |
| # محدوده ساخت | |
| bx = setbacks['left'] | |
| by = setbacks['front'] | |
| bw = site_width - setbacks['left'] - setbacks['right'] | |
| bd = site_depth - setbacks['front'] - setbacks['back'] | |
| msp.add_line((bx, by), (bx + bw, by), dxfattribs={'layer': 'BUILDABLE'}) | |
| msp.add_line((bx + bw, by), (bx + bw, by + bd), dxfattribs={'layer': 'BUILDABLE'}) | |
| msp.add_line((bx + bw, by + bd), (bx, by + bd), dxfattribs={'layer': 'BUILDABLE'}) | |
| msp.add_line((bx, by + bd), (bx, by), dxfattribs={'layer': 'BUILDABLE'}) | |
| # فضاها | |
| for space in layout: | |
| x, y, w, h = space['x'], space['y'], space['width'], space['height'] | |
| name = str(space['name']) | |
| msp.add_line((x, y), (x + w, y), dxfattribs={'layer': 'WALLS'}) | |
| msp.add_line((x + w, y), (x + w, y + h), dxfattribs={'layer': 'WALLS'}) | |
| msp.add_line((x + w, y + h), (x, y + h), dxfattribs={'layer': 'WALLS'}) | |
| msp.add_line((x, y + h), (x, y), dxfattribs={'layer': 'WALLS'}) | |
| mtext = msp.add_mtext( | |
| name, | |
| dxfattribs={ | |
| 'layer': 'TEXT', | |
| 'char_height': 0.35, | |
| 'width': w * 0.8, | |
| 'attachment_point': ezdxf.lldxf.const.MTEXT_MIDDLE_CENTER, | |
| } | |
| ) | |
| mtext.set_location((x + w/2, y + h/2)) | |
| with tempfile.NamedTemporaryFile(suffix='.dxf', delete=False) as tmp: | |
| doc.saveas(tmp.name) | |
| return tmp.name | |
| def create_preview_image(layout, site_width, site_depth, setbacks): | |
| """تولید تصویر پیشنمایش""" | |
| fig, ax = plt.subplots(figsize=(14, 11)) | |
| ax.set_facecolor('#FAFAFA') | |
| # زمین | |
| site_rect = plt.Rectangle((0, 0), site_width, site_depth, | |
| linewidth=3, edgecolor='#333333', | |
| facecolor='#E8E8E8', alpha=0.5) | |
| ax.add_patch(site_rect) | |
| # محدوده ساخت | |
| bx = setbacks['left'] | |
| by = setbacks['front'] | |
| bw = site_width - setbacks['left'] - setbacks['right'] | |
| bd = site_depth - setbacks['front'] - setbacks['back'] | |
| buildable_rect = plt.Rectangle((bx, by), bw, bd, | |
| linewidth=2, edgecolor='#FF9800', | |
| facecolor='none', linestyle='--') | |
| ax.add_patch(buildable_rect) | |
| colors = { | |
| 'living_dining': '#FFE0B2', | |
| 'living': '#FFE0B2', | |
| 'bedroom': '#C8E6C9', | |
| 'kitchen': '#FFCCBC', | |
| 'bathroom': '#B3E5FC', | |
| 'wc': '#B3E5FC', | |
| 'corridor': '#EEEEEE', | |
| 'balcony': '#E1BEE7', | |
| 'parking': '#CFD8DC', | |
| } | |
| default_color = '#FFF9C4' | |
| for space in layout: | |
| x, y, w, h = space['x'], space['y'], space['width'], space['height'] | |
| space_type = str(space.get('type', '')).lower() | |
| color = colors.get(space_type, default_color) | |
| rect = plt.Rectangle((x, y), w, h, linewidth=2.5, | |
| edgecolor='#424242', facecolor=color, alpha=0.8) | |
| ax.add_patch(rect) | |
| name = str(space['name']) | |
| area = w * h | |
| ax.text(x + w/2, y + h/2 + 0.2, name, | |
| ha='center', va='center', fontsize=9, fontweight='bold', | |
| bbox=dict(boxstyle='round,pad=0.3', facecolor='white', alpha=0.85)) | |
| ax.text(x + w/2, y + h/2 - 0.5, f"{w:.1f}×{h:.1f}m | {area:.1f}m²", | |
| ha='center', va='center', fontsize=7, color='#666666') | |
| margin = 3 | |
| ax.set_xlim(-margin, site_width + margin) | |
| ax.set_ylim(-margin, site_depth + margin) | |
| ax.set_aspect('equal') | |
| ax.set_xlabel('X (متر)', fontsize=12) | |
| ax.set_ylabel('Y (متر)', fontsize=12) | |
| ax.set_title('پیشنمایش پلان معماری', fontsize=15, fontweight='bold', pad=15) | |
| ax.grid(True, alpha=0.2, linestyle='--') | |
| from matplotlib.patches import Patch | |
| legend_elements = [ | |
| Patch(facecolor='#FFE0B2', label='پذیرایی/نشیمن'), | |
| Patch(facecolor='#C8E6C9', label='اتاق خواب'), | |
| Patch(facecolor='#FFCCBC', label='آشپزخانه'), | |
| Patch(facecolor='#B3E5FC', label='سرویس'), | |
| Patch(facecolor='#E1BEE7', label='بالکن'), | |
| Patch(facecolor='#EEEEEE', label='راهرو'), | |
| ] | |
| ax.legend(handles=legend_elements, loc='upper right', fontsize=9, framealpha=0.9) | |
| buf = io.BytesIO() | |
| fig.savefig(buf, format='png', dpi=130, bbox_inches='tight') | |
| buf.seek(0) | |
| plt.close(fig) | |
| return Image.open(buf) | |
| # ============================================================ | |
| # ۳. تابع اصلی پردازش | |
| # ============================================================ | |
| def process_form( | |
| site_width, site_depth, | |
| setback_front, setback_back, setback_left, setback_right, | |
| corridor_width, | |
| spaces_json | |
| ): | |
| if not spaces_json or spaces_json.strip() == '' or spaces_json == '[]': | |
| return "❌ لطفاً حداقل یک فضا تعریف کنید.", None, None, None | |
| try: | |
| spaces = json.loads(spaces_json) | |
| except json.JSONDecodeError as e: | |
| return f"❌ خطا در خواندن داده فضاها: {str(e)}", None, None, None | |
| if len(spaces) == 0: | |
| return "❌ لیست فضاها خالی است.", None, None, None | |
| total_area = sum(s.get('area', 0) for s in spaces) | |
| buildable_area = (site_width - setback_left - setback_right) * (site_depth - setback_front - setback_back) | |
| occupancy = (total_area / buildable_area * 100) if buildable_area > 0 else 0 | |
| if site_width <= 0 or site_depth <= 0: | |
| return "❌ ابعاد زمین باید مثبت باشد.", None, None, None | |
| if occupancy > 95: | |
| return f"❌ سطح اشغال {occupancy:.0f}٪ است. فضای کافی برای راهرو وجود ندارد.", None, None, None | |
| try: | |
| engine = FloorplanEngine( | |
| site_width, site_depth, | |
| setback_front, setback_back, setback_left, setback_right, | |
| corridor_width | |
| ) | |
| layout = engine.layout(spaces) | |
| if not layout: | |
| return "❌ نتونستم فضاها رو بچینم. ابعاد زمین یا فضاها را تغییر دهید.", None, None, None | |
| setbacks = { | |
| 'front': setback_front, | |
| 'back': setback_back, | |
| 'left': setback_left, | |
| 'right': setback_right | |
| } | |
| dxf_path = create_dxf(layout, site_width, site_depth, setbacks) | |
| preview = create_preview_image(layout, site_width, site_depth, setbacks) | |
| output_json = json.dumps({ | |
| 'site': {'width': site_width, 'depth': site_depth, 'setbacks': setbacks}, | |
| 'spaces': spaces, | |
| 'layout': layout, | |
| 'stats': { | |
| 'total_area': total_area, | |
| 'buildable_area': buildable_area, | |
| 'occupancy_rate': round(occupancy, 1) | |
| } | |
| }, ensure_ascii=False, indent=2) | |
| with tempfile.NamedTemporaryFile(suffix='.json', delete=False, mode='w', encoding='utf-8') as tmp: | |
| tmp.write(output_json) | |
| json_path = tmp.name | |
| placed_count = len(layout) | |
| unplaced_count = len(spaces) - placed_count | |
| status = f"✅ چیدمان با موفقیت انجام شد.\n\n" | |
| status += f"📐 {placed_count} فضا چیده شد" | |
| if unplaced_count > 0: | |
| status += f" ({unplaced_count} فضا جا نشد)" | |
| status += f"\n📏 مساحت کل: {total_area:.1f} m²" | |
| status += f"\n🏠 سطح اشغال: {occupancy:.1f}٪" | |
| status += f"\n💾 خروجیها: DXF + JSON" | |
| return status, preview, dxf_path, json_path | |
| except Exception as e: | |
| import traceback | |
| error_details = traceback.format_exc() | |
| print(error_details) | |
| return f"❌ خطا: {str(e)}", None, None, None, None | |
| # ============================================================ | |
| # ۴. مدیریت فضاها | |
| # ============================================================ | |
| def add_space(space_name, space_type, area, min_width, daylit, spaces_state): | |
| if not space_name or area <= 0: | |
| return spaces_state, "⚠️ نام فضا و مساحت را وارد کنید." | |
| try: | |
| spaces = json.loads(spaces_state) if spaces_state else [] | |
| except: | |
| spaces = [] | |
| spaces.append({ | |
| 'name': space_name, | |
| 'type': space_type, | |
| 'area': float(area), | |
| 'min_width': float(min_width), | |
| 'daylit': daylit | |
| }) | |
| summary = f"📋 {len(spaces)} فضا | مجموع مساحت: {sum(s['area'] for s in spaces):.1f} m²" | |
| return json.dumps(spaces, ensure_ascii=False), summary | |
| def clear_spaces(): | |
| return "[]", "📋 ۰ فضا" | |
| def get_spaces_table(spaces_json): | |
| if not spaces_json or spaces_json == '[]': | |
| return pd.DataFrame(columns=['نام فضا', 'نوع', 'مساحت', 'حداقل عرض', 'نورگیر']) | |
| try: | |
| spaces = json.loads(spaces_json) | |
| data = [] | |
| for s in spaces: | |
| data.append([ | |
| s.get('name', ''), | |
| s.get('type', ''), | |
| s.get('area', 0), | |
| s.get('min_width', 0), | |
| '✅' if s.get('daylit', True) else '❌' | |
| ]) | |
| return pd.DataFrame(data, columns=['نام فضا', 'نوع', 'مساحت', 'حداقل عرض', 'نورگیر']) | |
| except: | |
| return pd.DataFrame(columns=['نام فضا', 'نوع', 'مساحت', 'حداقل عرض', 'نورگیر']) | |
| def update_occupancy(site_w, site_d, sf, sb, sl, sr, spaces_json): | |
| if not spaces_json or spaces_json == '[]': | |
| return "📋 هنوز فضایی تعریف نشده." | |
| try: | |
| spaces = json.loads(spaces_json) | |
| total = sum(s['area'] for s in spaces) | |
| buildable = (site_w - sl - sr) * (site_d - sf - sb) | |
| if buildable <= 0: | |
| return "⚠️ ابعاد نامعتبر" | |
| occ = total / buildable * 100 | |
| if occ > 90: | |
| return f"⚠️ سطح اشغال: {occ:.1f}٪ — بسیار زیاد! فضا برای راهرو کم است." | |
| elif occ > 70: | |
| return f"⚡ سطح اشغال: {occ:.1f}٪ — نسبتاً زیاد. راهروها حداقلی خواهند بود." | |
| else: | |
| return f"✅ سطح اشغال: {occ:.1f}٪ | مساحت کل: {total:.1f} m² | محدوده ساخت: {buildable:.1f} m²" | |
| except: | |
| return "⚠️ خطا در محاسبه" | |
| # ============================================================ | |
| # ۵. اینترفیس Gradio | |
| # ============================================================ | |
| with gr.Blocks(title="پلتفرم طراحی پلان معماری") as demo: | |
| gr.Markdown( | |
| """ | |
| # 🏗️ پلتفرم طراحی پلان معماری — نسخه تعاملی | |
| ### ✨ تمام پارامترها را مستقیماً تنظیم کنید و نتیجه را زنده ببینید | |
| """ | |
| ) | |
| spaces_state = gr.State(value="[]") | |
| with gr.Tabs(): | |
| # تب ۱: تعریف زمین | |
| with gr.Tab("🏠 زمین و ضوابط"): | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| gr.Markdown("### ابعاد زمین") | |
| site_width = gr.Number(label="عرض زمین (متر)", value=15.0, minimum=5, maximum=50) | |
| site_depth = gr.Number(label="عمق زمین (متر)", value=20.0, minimum=5, maximum=50) | |
| with gr.Column(scale=1): | |
| gr.Markdown("### عقبنشینیها") | |
| setback_front = gr.Number(label="عقبنشینی جلو (متر)", value=3.0, minimum=0, maximum=10) | |
| setback_back = gr.Number(label="عقبنشینی عقب (متر)", value=2.0, minimum=0, maximum=10) | |
| setback_left = gr.Number(label="عقبنشینی چپ (متر)", value=1.5, minimum=0, maximum=10) | |
| setback_right = gr.Number(label="عقبنشینی راست (متر)", value=1.5, minimum=0, maximum=10) | |
| with gr.Column(scale=1): | |
| gr.Markdown("### سایر تنظیمات") | |
| corridor_width = gr.Slider(label="عرض راهرو (متر)", minimum=0.8, maximum=2.5, value=1.2, step=0.1) | |
| occupancy_info = gr.Textbox(label="اطلاعات سطح اشغال", value="📋 هنوز فضایی تعریف نشده.", interactive=False) | |
| # تب ۲: تعریف فضاها | |
| with gr.Tab("🚪 فضاها"): | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| gr.Markdown("### افزودن فضای جدید") | |
| with gr.Row(): | |
| space_name = gr.Textbox(label="نام فضا", placeholder="مثلاً: پذیرایی", scale=3) | |
| space_type = gr.Dropdown( | |
| label="نوع فضا", | |
| choices=['living_dining', 'bedroom', 'kitchen', 'bathroom', 'corridor', 'balcony', 'parking'], | |
| value='living_dining', | |
| scale=2 | |
| ) | |
| with gr.Row(): | |
| area = gr.Number(label="مساحت (m²)", value=25.0, minimum=2, maximum=200, scale=1) | |
| min_width = gr.Number(label="حداقل عرض (m)", value=3.0, minimum=1, maximum=20, scale=1) | |
| daylit = gr.Checkbox(label="نیاز به نورگیری", value=True, scale=1) | |
| with gr.Row(): | |
| add_btn = gr.Button("➕ افزودن فضا", variant="primary") | |
| clear_btn = gr.Button("🗑️ پاک کردن همه") | |
| space_message = gr.Textbox(label="وضعیت", interactive=False) | |
| with gr.Column(scale=3): | |
| gr.Markdown("### فضاهای تعریف شده") | |
| spaces_table = gr.Dataframe( | |
| headers=['نام فضا', 'نوع', 'مساحت', 'حداقل عرض', 'نورگیر'], | |
| label="", | |
| interactive=False, | |
| ) | |
| spaces_summary = gr.Textbox(label="خلاصه", value="📋 ۰ فضا", interactive=False) | |
| # تب ۳: خروجی | |
| with gr.Tab("📊 نتیجه"): | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| submit_btn = gr.Button("🚀 تولید پلان", variant="primary", size="lg") | |
| status_output = gr.Textbox(label="وضعیت پردازش", interactive=False, lines=8) | |
| with gr.Row(): | |
| dxf_output = gr.File(label="📥 DXF") | |
| json_output = gr.File(label="📥 JSON پروژه") | |
| with gr.Column(scale=2): | |
| preview_output = gr.Image(label="پیشنمایش پلان", type="pil") | |
| # رویدادها | |
| add_btn.click( | |
| fn=add_space, | |
| inputs=[space_name, space_type, area, min_width, daylit, spaces_state], | |
| outputs=[spaces_state, space_message] | |
| ).then( | |
| fn=get_spaces_table, | |
| inputs=[spaces_state], | |
| outputs=[spaces_table] | |
| ).then( | |
| fn=lambda s: f"📋 {len(json.loads(s))} فضا | مجموع: {sum(sp['area'] for sp in json.loads(s)):.1f} m²" if s and s != '[]' else "📋 ۰ فضا", | |
| inputs=[spaces_state], | |
| outputs=[spaces_summary] | |
| ) | |
| clear_btn.click( | |
| fn=clear_spaces, | |
| inputs=[], | |
| outputs=[spaces_state, space_message] | |
| ).then( | |
| fn=lambda: (pd.DataFrame(columns=['نام فضا', 'نوع', 'مساحت', 'حداقل عرض', 'نورگیر']), "📋 ۰ فضا"), | |
| inputs=[], | |
| outputs=[spaces_table, spaces_summary] | |
| ) | |
| # بهروزرسانی زنده سطح اشغال | |
| for inp in [site_width, site_depth, setback_front, setback_back, setback_left, setback_right]: | |
| inp.change( | |
| fn=update_occupancy, | |
| inputs=[site_width, site_depth, setback_front, setback_back, setback_left, setback_right, spaces_state], | |
| outputs=[occupancy_info] | |
| ) | |
| # بهروزرسانی وقتی فضا اضافه میشه | |
| spaces_state.change( | |
| fn=update_occupancy, | |
| inputs=[site_width, site_depth, setback_front, setback_back, setback_left, setback_right, spaces_state], | |
| outputs=[occupancy_info] | |
| ) | |
| submit_btn.click( | |
| fn=process_form, | |
| inputs=[site_width, site_depth, setback_front, setback_back, setback_left, setback_right, | |
| corridor_width, spaces_state], | |
| outputs=[status_output, preview_output, dxf_output, json_output] | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch( | |
| server_name="0.0.0.0", | |
| server_port=7860, | |
| theme=gr.themes.Soft() | |
| ) |