|
|
import streamlit as st |
|
|
from groq import Groq |
|
|
import json |
|
|
import os |
|
|
import matplotlib.pyplot as plt |
|
|
import numpy as np |
|
|
import plotly.graph_objects as go |
|
|
from dotenv import load_dotenv |
|
|
|
|
|
|
|
|
load_dotenv() |
|
|
|
|
|
|
|
|
try: |
|
|
client = Groq(api_key=os.getenv("GROQ_API_KEY")) |
|
|
except Exception as e: |
|
|
st.error(f"Failed to initialize Groq client: {e}") |
|
|
|
|
|
|
|
|
st.title("π AI Architect: Home Planner") |
|
|
st.markdown("Get an easy-to-understand house plan with 2D/3D visualization!") |
|
|
|
|
|
|
|
|
st.header("π Step 1: Plot Details") |
|
|
col1, col2 = st.columns(2) |
|
|
with col1: |
|
|
plot_length = st.number_input("Plot Length (ft)", min_value=20, max_value=500, value=50) |
|
|
with col2: |
|
|
plot_width = st.number_input("Plot Width (ft)", min_value=20, max_value=500, value=40) |
|
|
|
|
|
st.header("πͺ Step 2: Room Requirements") |
|
|
master_bed = st.number_input("Master Bedrooms (12x14 ft min)", min_value=0, max_value=5, value=1) |
|
|
bedrooms = st.number_input("Standard Bedrooms (10x12 ft min)", min_value=0, max_value=10, value=2) |
|
|
kitchens = st.number_input("Kitchens (8x10 ft min)", min_value=1, max_value=3, value=1) |
|
|
study_rooms = st.number_input("Study Rooms (8x8 ft min)", min_value=0, max_value=3, value=0) |
|
|
lounges = st.number_input("Lounges (12x16 ft min)", min_value=1, max_value=3, value=1) |
|
|
|
|
|
st.header("π½ Step 3: Washrooms") |
|
|
attached_washrooms = st.number_input("Attached Washrooms (6x8 ft min)", min_value=0, max_value=5, value=1) |
|
|
common_washrooms = st.number_input("Common Washrooms (5x7 ft min)", min_value=0, max_value=3, value=1) |
|
|
|
|
|
st.header("π³ Step 4: Extras") |
|
|
parking = st.checkbox("Parking Space (12x20 ft min) π") |
|
|
lawn = st.checkbox("Lawn/Garden π³") |
|
|
|
|
|
|
|
|
if st.button("Generate Professional Plan"): |
|
|
prompt = f""" |
|
|
Design a house plan for a {plot_length}x{plot_width} ft plot following architectural best practices. |
|
|
Return response in STRICT JSON format with these EXACT keys: |
|
|
{{ |
|
|
"floor_plan": {{ |
|
|
"rooms": [ |
|
|
{{ |
|
|
"name": "string", |
|
|
"x": number, |
|
|
"y": number, |
|
|
"width": number, |
|
|
"height": number |
|
|
}} |
|
|
], |
|
|
"washrooms": [ |
|
|
{{ |
|
|
"name": "string", |
|
|
"x": number, |
|
|
"y": number, |
|
|
"width": number, |
|
|
"height": number |
|
|
}} |
|
|
] |
|
|
}}, |
|
|
"visualization_tips": ["string"], |
|
|
"material_suggestions": ["string"], |
|
|
"warnings": ["string"] |
|
|
}} |
|
|
|
|
|
Requirements: |
|
|
1. All dimensions must be numbers (not arrays) |
|
|
2. Positions must be x,y coordinates as separate numbers |
|
|
3. Room names should be clear (e.g., "Master Bedroom") |
|
|
4. Include at least {master_bed} master bedroom(s) and {bedrooms} standard bedroom(s) |
|
|
""" |
|
|
|
|
|
try: |
|
|
response = client.chat.completions.create( |
|
|
messages=[{"role": "user", "content": prompt}], |
|
|
model="llama3-70b-8192", |
|
|
response_format={"type": "json_object"}, |
|
|
temperature=0.3 |
|
|
) |
|
|
|
|
|
|
|
|
plan = json.loads(response.choices[0].message.content) |
|
|
|
|
|
|
|
|
required_keys = ["floor_plan", "visualization_tips", "material_suggestions", "warnings"] |
|
|
for key in required_keys: |
|
|
if key not in plan: |
|
|
raise ValueError(f"Missing required key: {key}") |
|
|
|
|
|
if "rooms" not in plan["floor_plan"]: |
|
|
raise ValueError("Missing rooms in floor plan") |
|
|
|
|
|
st.success("β
Professionally Designed Plan Generated!") |
|
|
|
|
|
|
|
|
st.header("π 2D Floor Plan") |
|
|
|
|
|
fig, ax = plt.subplots(figsize=(10, 8)) |
|
|
colors = plt.cm.tab20.colors |
|
|
|
|
|
|
|
|
for i, room in enumerate(plan['floor_plan']['rooms']): |
|
|
rect = plt.Rectangle( |
|
|
(room['x'], room['y']), |
|
|
room['width'], |
|
|
room['height'], |
|
|
linewidth=2, |
|
|
edgecolor='black', |
|
|
facecolor=colors[i % len(colors)]) |
|
|
ax.add_patch(rect) |
|
|
|
|
|
|
|
|
center_x = room['x'] + room['width']/2 |
|
|
center_y = room['y'] + room['height']/2 |
|
|
ax.text(center_x, center_y, |
|
|
f"{room.get('name', 'Room')}\n{room['width']}x{room['height']}ft", |
|
|
ha='center', va='center', fontsize=8) |
|
|
|
|
|
|
|
|
for washroom in plan['floor_plan'].get('washrooms', []): |
|
|
rect = plt.Rectangle( |
|
|
(washroom['x'], washroom['y']), |
|
|
washroom['width'], |
|
|
washroom['height'], |
|
|
linewidth=2, |
|
|
edgecolor='black', |
|
|
facecolor='lightblue', |
|
|
hatch='/') |
|
|
ax.add_patch(rect) |
|
|
ax.text(washroom['x'] + washroom['width']/2, |
|
|
washroom['y'] + washroom['height']/2, |
|
|
"Bath", |
|
|
ha='center', va='center', fontsize=8) |
|
|
|
|
|
|
|
|
ax.set_xlim(0, plot_length) |
|
|
ax.set_ylim(0, plot_width) |
|
|
ax.set_aspect('equal') |
|
|
ax.set_xlabel('Length (ft)') |
|
|
ax.set_ylabel('Width (ft)') |
|
|
ax.set_title('2D Floor Plan') |
|
|
st.pyplot(fig) |
|
|
|
|
|
|
|
|
st.header("β¨ 3D Visualization") |
|
|
|
|
|
fig_3d = go.Figure() |
|
|
|
|
|
|
|
|
for i, room in enumerate(plan['floor_plan']['rooms']): |
|
|
fig_3d.add_trace(go.Mesh3d( |
|
|
x=[room['x'], room['x'], room['x']+room['width'], room['x']+room['width'], room['x']], |
|
|
y=[room['y'], room['y']+room['height'], room['y']+room['height'], room['y'], room['y']], |
|
|
z=[0, 0, 0, 0, 0], |
|
|
opacity=0.7, |
|
|
color=f'rgb({colors[i][0]*255},{colors[i][1]*255},{colors[i][2]*255})', |
|
|
name=room.get('name', 'Room'), |
|
|
showlegend=True |
|
|
)) |
|
|
fig_3d.add_trace(go.Mesh3d( |
|
|
x=[room['x'], room['x'], room['x']+room['width'], room['x']+room['width'], room['x']], |
|
|
y=[room['y'], room['y']+room['height'], room['y']+room['height'], room['y'], room['y']], |
|
|
z=[10, 10, 10, 10, 10], |
|
|
opacity=0.7, |
|
|
color=f'rgb({colors[i][0]*255},{colors[i][1]*255},{colors[i][2]*255})', |
|
|
showlegend=False |
|
|
)) |
|
|
|
|
|
fig_3d.update_layout( |
|
|
scene=dict( |
|
|
xaxis=dict(title='Length (ft)'), |
|
|
yaxis=dict(title='Width (ft)'), |
|
|
zaxis=dict(title='Height (ft)'), |
|
|
aspectmode='manual', |
|
|
aspectratio=dict(x=1, y=plot_width/plot_length, z=0.2) |
|
|
), |
|
|
margin=dict(r=20, l=10, b=10, t=30), |
|
|
legend=dict(x=1, y=1) |
|
|
) |
|
|
st.plotly_chart(fig_3d) |
|
|
|
|
|
|
|
|
st.header("π‘ Visualization Tips") |
|
|
for tip in plan['visualization_tips']: |
|
|
st.markdown(f"- {tip}") |
|
|
|
|
|
st.header("ποΈ Material Suggestions") |
|
|
for material in plan['material_suggestions']: |
|
|
st.markdown(f"- {material}") |
|
|
|
|
|
st.header("β οΈ Warnings") |
|
|
for warning in plan['warnings']: |
|
|
st.markdown(f"- β οΈ {warning}") |
|
|
|
|
|
except json.JSONDecodeError: |
|
|
st.error("Failed to parse the AI response. Please try again.") |
|
|
except Exception as e: |
|
|
st.error(f"Error: {str(e)}. Please adjust your inputs and try again.") |
|
|
|
|
|
|
|
|
st.markdown("---") |
|
|
st.caption("β
Easy-to-understand visualizations | Powered by Groq API") |