Engineer-Areeb commited on
Commit
e9b96a2
·
verified ·
1 Parent(s): 7e46848

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -516
app.py DELETED
@@ -1,516 +0,0 @@
1
- # app.py
2
- # Combined Engineering Suite for Hugging Face Deployment
3
-
4
- # --- 1. IMPORTS ---
5
- # Standard library imports
6
- import re
7
- import os
8
- import io
9
- import warnings
10
-
11
- # Third-party imports
12
- import gradio as gr
13
- import numpy as np
14
- import matplotlib
15
- matplotlib.use('Agg') # Use a non-interactive backend
16
- import matplotlib.pyplot as plt
17
- from matplotlib import cm
18
- from PIL import Image, ImageDraw, ImageFont
19
- import plotly.graph_objects as go
20
- from shapely.geometry import box, Point
21
- from shapely.affinity import scale
22
- import trimesh
23
- import pyvista as pv
24
-
25
- # Suppress warnings for a cleaner output
26
- warnings.filterwarnings('ignore')
27
-
28
- # --- 2. CORE LOGIC FROM PDFS ---
29
-
30
- # === MODULE 1: Text to CAD & G-Code (from g-code.pdf) ===
31
-
32
- def parse_simple_description(description):
33
- """Parses a textual description to extract geometric parameters for a plate."""
34
- width, height = 100, 100
35
- holes, slots, ovals = [], [], []
36
-
37
- # Dimensions
38
- dim_match = re.search(r'(\d+)\s*mm\s*[xX×]\s*(\d+)\s*mm', description)
39
- if dim_match:
40
- width, height = int(dim_match.group(1)), int(dim_match.group(2))
41
-
42
- # Holes
43
- for hole_match in re.finditer(r'(hole|circle|circular cutout)[^\d]*(\d+)\s*mm', description):
44
- holes.append({'x': width / 2, 'y': height / 2, 'diameter': int(hole_match.group(2))})
45
-
46
- # Slots
47
- for slot_match in re.finditer(r'(\d+)\s*mm\s+long\s+and\s+(\d+)\s*mm\s+wide\s+slot', description):
48
- slots.append({'x': width / 2, 'y': height / 2, 'length': int(slot_match.group(1)), 'width': int(slot_match.group(2))})
49
-
50
- # Ovals
51
- for oval_match in re.finditer(r'oval\s+hole\s+(\d+)\s*mm\s+long\s+and\s+(\d+)\s*mm\s+wide', description):
52
- ovals.append({'x': width / 2, 'y': height / 2, 'length': int(oval_match.group(1)), 'width': int(oval_match.group(2))})
53
-
54
- return {"width": width, "height": height, "holes": holes, "slots": slots, "ovals": ovals}
55
-
56
- def generate_simple_3_view_drawing(description):
57
- """Generates a 3-view engineering drawing from a simple description."""
58
- parsed = parse_simple_description(description)
59
- width, height, depth = parsed["width"], parsed["height"], 5
60
- fig, axes = plt.subplots(1, 3, figsize=(15, 5))
61
- plt.style.use('grayscale')
62
-
63
- views = ['Top View', 'Front View', 'Side View']
64
- for ax, view in zip(axes, views):
65
- ax.set_title(view)
66
- ax.grid(True, linestyle='--', linewidth=0.5)
67
- ax.set_aspect('equal', adjustable='box')
68
-
69
- if view == "Top View":
70
- shape = box(0, 0, width, height)
71
- x, y = shape.exterior.xy
72
- ax.plot(x, y, color='black')
73
- for hole in parsed["holes"]:
74
- ax.plot(*Point(hole['x'], hole['y']).buffer(hole['diameter'] / 2).exterior.xy, color='black')
75
- for slot in parsed["slots"]:
76
- ax.plot(*scale(Point(slot['x'], slot['y']).buffer(1), slot['length']/2, slot['width']/2).exterior.xy, color='black')
77
- for oval in parsed["ovals"]:
78
- ax.plot(*scale(Point(oval['x'], oval['y']).buffer(1), oval['length']/2, oval['width']/2).exterior.xy, color='black')
79
- ax.set_xlim(-10, width + 10)
80
- ax.set_ylim(-10, height + 10)
81
- elif view == "Front View":
82
- ax.plot(*box(0, 0, width, depth).exterior.xy, color='black')
83
- ax.set_xlim(-10, width + 10)
84
- ax.set_ylim(-10, depth + 10)
85
- elif view == "Side View":
86
- ax.plot(*box(0, 0, height, depth).exterior.xy, color='black')
87
- ax.set_xlim(-10, height + 10)
88
- ax.set_ylim(-10, depth + 10)
89
-
90
- plt.tight_layout()
91
- drawing_path = "simple_3_view_drawing.png"
92
- fig.savefig(drawing_path)
93
- plt.close(fig)
94
- return drawing_path
95
-
96
- def generate_gcode(description):
97
- """Generates G-code for milling the part based on the description."""
98
- parsed = parse_simple_description(description)
99
- w, h = parsed['width'], parsed['height']
100
- gcode = [
101
- "G21 ; Set units to mm", "G90 ; Use absolute positioning", "G17 ; Select XY plane",
102
- "M3 S1000 ; Start spindle", "G0 Z5 ; Lift Z to a safe height",
103
- "\n; --- Mill Outer Profile ---",
104
- "G0 X0 Y0", "G1 Z-1 F100", f"G1 X{w} F300", f"G1 Y{h}", f"G1 X0", "G1 Y0", "G0 Z5"
105
- ]
106
- for hole in parsed['holes']:
107
- x, y, r = hole['x'], hole['y'], hole['diameter'] / 2
108
- gcode.extend([f"\n; --- Mill Hole at X{x}, Y{y}, D{hole['diameter']} ---", f"G0 X{x - r} Y{y}", "G1 Z-1 F100", f"G2 I{r} J0 F200", "G0 Z5"])
109
- for slot in parsed['slots']:
110
- x, y, l, w_slot = slot['x'], slot['y'], slot['length'], slot['width']
111
- r = w_slot / 2
112
- x_start, x_end = x - (l - w_slot) / 2, x + (l - w_slot) / 2
113
- gcode.extend([f"\n; --- Mill Slot at center X{x}, Y{y} ---", f"G0 X{x_start} Y{y - r}", "G1 Z-1 F100", f"G1 X{x_end} F200", f"G2 I0 J{r}", f"G1 X{x_start}", f"G2 I0 J{r}", "G0 Z5"])
114
- for oval in parsed['ovals']:
115
- gcode.append(f"\n; --- Oval hole at X{oval['x']}, Y{oval['y']} (manual operation needed) ---")
116
- gcode.extend(["\nM5 ; Stop spindle", "G0 X0 Y0 ; Return to home", "M30 ; End of program"])
117
- return "\n".join(gcode)
118
-
119
- def export_svg(description):
120
- """Exports a 2D drawing of the part top-view as an SVG file."""
121
- parsed = parse_simple_description(description)
122
- width, height = parsed["width"], parsed["height"]
123
- fig, ax = plt.subplots(figsize=(width/25.4, height/25.4)) # Inches
124
- ax.set_aspect('equal')
125
- ax.plot(*box(0, 0, width, height).exterior.xy, color='black', linewidth=2)
126
- for hole in parsed["holes"]:
127
- ax.plot(*Point(hole['x'], hole['y']).buffer(hole['diameter'] / 2).exterior.xy, color='black', linewidth=1.5)
128
- ax.set_xlim(-10, width + 10)
129
- ax.set_ylim(-10, height + 10)
130
- ax.axis('off')
131
- svg_path = "drawing.svg"
132
- fig.savefig(svg_path, format="svg", bbox_inches='tight', pad_inches=0.1)
133
- plt.close(fig)
134
- return svg_path
135
-
136
- def process_simple_cad(description):
137
- """Top-level function for the simple CAD generator."""
138
- if not description.strip():
139
- return None, "Please enter a description.", None
140
- try:
141
- drawing_path = generate_simple_3_view_drawing(description)
142
- gcode = generate_gcode(description)
143
- svg_path = export_svg(description)
144
- return drawing_path, gcode, svg_path
145
- except Exception as e:
146
- return None, f"An error occurred: {e}", None
147
-
148
- # === MODULE 2: Advanced Text-to-CAD (from text to cad.pdf) ===
149
- class TextToCADGenerator:
150
- """Text-to-CAD generator using procedural geometry."""
151
- def __init__(self):
152
- self.shapes_library = {
153
- 'cube': self._create_cube, 'box': self._create_cube,
154
- 'sphere': self._create_sphere, 'ball': self._create_sphere,
155
- 'cylinder': self._create_cylinder, 'tube': self._create_cylinder,
156
- 'cone': self._create_cone, 'plate': self._create_plate,
157
- 'washer': self._create_washer, 'bracket': self._create_bracket,
158
- }
159
-
160
- def parse_prompt(self, prompt: str) -> dict:
161
- prompt = prompt.lower().strip()
162
- dimensions = self._extract_dimensions(prompt)
163
- shape_type = next((shape for shape in self.shapes_library if shape in prompt), 'cube')
164
- color = next((c for c in ['red', 'blue', 'green', 'yellow', 'gray'] if c in prompt), 'lightblue')
165
- return {'shape': shape_type, 'dimensions': dimensions, 'color': color, 'prompt': prompt}
166
-
167
- def _extract_dimensions(self, prompt: str) -> dict:
168
- dims = {'length': 10, 'width': 10, 'height': 10, 'radius': 5, 'diameter': 10}
169
- patterns = {
170
- 'length': r'length\s*[:=]?\s*(\d+\.?\d*)', 'width': r'width\s*[:=]?\s*(\d+\.?\d*)',
171
- 'height': r'height\s*[:=]?\s*(\d+\.?\d*)', 'radius': r'radius\s*[:=]?\s*(\d+\.?\d*)',
172
- 'diameter': r'diameter\s*[:=]?\s*(\d+\.?\d*)', 'thick': r'thick\w*\s*[:=]?\s*(\d+\.?\d*)'
173
- }
174
- for key, pattern in patterns.items():
175
- match = re.search(pattern, prompt, re.IGNORECASE)
176
- if match:
177
- dims[key] = float(match.group(1))
178
- return dims
179
-
180
- def generate_3d_model(self, params: dict) -> trimesh.Trimesh:
181
- shape_func = self.shapes_library.get(params['shape'], self._create_cube)
182
- return shape_func(params['dimensions'])
183
-
184
- def generate_2d_drawing(self, params: dict) -> Image.Image:
185
- fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(15, 5))
186
- fig.suptitle(f"Technical Drawing: {params['shape'].title()}", fontsize=16)
187
- dims = params['dimensions']
188
- # Simplified drawing logic
189
- self._draw_rectangular_part(ax1, ax2, ax3, dims, params['shape'])
190
- plt.tight_layout(rect=[0, 0.03, 1, 0.95])
191
- buf = io.BytesIO()
192
- plt.savefig(buf, format='png', dpi=150)
193
- buf.seek(0)
194
- img = Image.open(buf)
195
- plt.close(fig)
196
- return img
197
-
198
- def _draw_rectangular_part(self, ax1, ax2, ax3, dims, shape):
199
- l, w, h = dims['length'], dims['width'], dims['height']
200
- views = {"Front View": (w, h), "Top View": (w, l), "Side View": (l, h)}
201
- axes = {"Front View": ax1, "Top View": ax2, "Side View": ax3}
202
- for title, (dim1, dim2) in views.items():
203
- ax = axes[title]
204
- ax.set_title(title)
205
- ax.add_patch(plt.Rectangle((0, 0), dim1, dim2, fill=False, edgecolor='black', linewidth=2))
206
- ax.set_xlim(-dim1*0.1, dim1*1.1)
207
- ax.set_ylim(-dim2*0.1, dim2*1.1)
208
- ax.set_aspect('equal')
209
- ax.grid(True, alpha=0.3)
210
- ax.set_xlabel(f"Dim 1: {dim1:.2f}")
211
- ax.set_ylabel(f"Dim 2: {dim2:.2f}")
212
-
213
- def generate_3d_visualization(self, mesh: trimesh.Trimesh, color: str = 'lightblue') -> go.Figure:
214
- fig = go.Figure(data=[go.Mesh3d(
215
- x=mesh.vertices[:, 0], y=mesh.vertices[:, 1], z=mesh.vertices[:, 2],
216
- i=mesh.faces[:, 0], j=mesh.faces[:, 1], k=mesh.faces[:, 2],
217
- color=color, opacity=0.9
218
- )])
219
- fig.update_layout(title="3D CAD Model", scene=dict(xaxis_title="X", yaxis_title="Y", zaxis_title="Z"))
220
- return fig
221
-
222
- # Shape creation methods
223
- def _create_cube(self, dims: dict) -> trimesh.Trimesh:
224
- return trimesh.creation.box(extents=[dims['length'], dims['width'], dims['height']])
225
- def _create_sphere(self, dims: dict) -> trimesh.Trimesh:
226
- return trimesh.creation.icosphere(subdivisions=3, radius=dims.get('radius', dims.get('diameter', 10) / 2))
227
- def _create_cylinder(self, dims: dict) -> trimesh.Trimesh:
228
- return trimesh.creation.cylinder(radius=dims.get('radius', 5), height=dims.get('height', 10))
229
- def _create_cone(self, dims: dict) -> trimesh.Trimesh:
230
- return trimesh.creation.cone(radius=dims.get('radius', 5), height=dims.get('height', 10))
231
- def _create_plate(self, dims: dict) -> trimesh.Trimesh:
232
- return trimesh.creation.box(extents=[dims['length'], dims['width'], dims.get('height', 2)])
233
- def _create_washer(self, dims: dict) -> trimesh.Trimesh:
234
- outer = trimesh.creation.cylinder(radius=dims.get('radius', 10), height=dims.get('height', 2))
235
- inner = trimesh.creation.cylinder(radius=dims.get('radius', 10) * 0.5, height=dims.get('height', 2) * 1.1)
236
- return outer.difference(inner)
237
- def _create_bracket(self, dims: dict) -> trimesh.Trimesh:
238
- l, w, h, t = dims.get('length', 20), dims.get('width', 15), dims.get('height', 20), dims.get('thick', 3)
239
- base = trimesh.creation.box(extents=[l, w, t])
240
- base.apply_translation([l/2, w/2, t/2])
241
- wall = trimesh.creation.box(extents=[t, w, h])
242
- wall.apply_translation([t/2, w/2, h/2])
243
- return base.union(wall)
244
-
245
- cad_generator = TextToCADGenerator()
246
-
247
- def process_advanced_text_to_cad(prompt: str):
248
- """Main function for the advanced CAD generator."""
249
- try:
250
- params = cad_generator.parse_prompt(prompt)
251
- mesh_3d = cad_generator.generate_3d_model(params)
252
- drawing_2d = cad_generator.generate_2d_drawing(params)
253
- fig_3d = cad_generator.generate_3d_visualization(mesh_3d, params['color'])
254
- summary = f"*Shape:* {params['shape'].title()}\n*Dimensions:* {params['dimensions']}"
255
- return drawing_2d, fig_3d, summary
256
- except Exception as e:
257
- return None, go.Figure().add_annotation(text=f"Error: {e}", showarrow=False), f"Error: {e}"
258
-
259
-
260
- # === MODULE 3: 3D to Orthographic View (from cad to ortho.pdf) ===
261
-
262
- def generate_ortho_views(uploaded_file):
263
- """Generates orthographic views from an uploaded 3D model file."""
264
- if uploaded_file is None:
265
- return None, "Please upload a 3D model file."
266
- try:
267
- # PyVista reads from a file path
268
- mesh = pv.read(uploaded_file.name)
269
-
270
- # Generate base views with PyVista
271
- views, filenames = ['xy', 'xz', 'yz'], ['front.png', 'top.png', 'side.png']
272
- plotter = pv.Plotter(off_screen=True, window_size=[800, 800])
273
- plotter.background_color = 'white'
274
- plotter.enable_parallel_projection()
275
- for i, view in enumerate(views):
276
- plotter.clear()
277
- plotter.add_mesh(mesh.extract_feature_edges(), color='black', line_width=2)
278
- plotter.camera_position = view
279
- plotter.reset_camera()
280
- plotter.camera.zoom(1.2)
281
- plotter.screenshot(filenames[i])
282
-
283
- # Combine and add dimensions with Matplotlib
284
- fig, axes = plt.subplots(1, 3, figsize=(18, 6))
285
- fig.patch.set_facecolor('white')
286
- bounds = mesh.bounds
287
- x_dim, y_dim, z_dim = bounds[1]-bounds[0], bounds[3]-bounds[2], bounds[5]-bounds[4]
288
- dims = [(x_dim, y_dim), (x_dim, z_dim), (y_dim, z_dim)]
289
- view_titles = ["Front View (XY)", "Top View (XZ)", "Side View (YZ)"]
290
-
291
- for i, ax in enumerate(axes):
292
- img = plt.imread(filenames[i])
293
- ax.imshow(img)
294
- ax.set_title(view_titles[i], fontsize=12, pad=10)
295
- ax.axis('off')
296
- dim1, dim2 = dims[i]
297
- # Add annotations
298
- ax.text(0.5, 0.05, f"Width: {dim1:.2f}", transform=ax.transAxes, ha='center', va='bottom', fontsize=10)
299
- ax.text(0.05, 0.5, f"Height: {dim2:.2f}", transform=ax.transAxes, ha='left', va='center', rotation=90, fontsize=10)
300
-
301
- final_path = 'orthographic_views_with_dimensions.png'
302
- plt.tight_layout()
303
- plt.savefig(final_path, dpi=150)
304
- plt.close(fig)
305
- return final_path, f"Successfully generated views for {os.path.basename(uploaded_file.name)}."
306
-
307
- except Exception as e:
308
- return None, f"An error occurred: {e}"
309
-
310
-
311
- # === MODULE 4: 2D CFD Simulation (from cfd.pdf) ===
312
-
313
- def run_cfd_simulation(Lx, Ly, Nx, Ny, inlet_velocity_u, rho, mu, ox1, oy1, ox2, oy2, max_iter, p_iter):
314
- """Runs a 2D CFD simulation and returns the result plot."""
315
- try:
316
- # Grid and Initialization
317
- dx, dy = Lx / (Nx - 1), Ly / (Ny - 1)
318
- x, y = np.linspace(0, Lx, Nx), np.linspace(0, Ly, Ny)
319
- X, Y = np.meshgrid(x, y)
320
- u, v, p = np.zeros((Ny, Nx)), np.zeros((Ny, Nx)), np.zeros((Ny, Nx))
321
-
322
- # Obstacle
323
- obstacle_mask = np.zeros((Ny, Nx), dtype=bool)
324
- if ox1 < ox2 and oy1 < oy2:
325
- i1, i2 = int(ox1 / dx), int(ox2 / dx)
326
- j1, j2 = int(oy1 / dy), int(oy2 / dy)
327
- obstacle_mask[j1:j2, i1:i2] = True
328
-
329
- # Main Loop (SIMPLE algorithm simplified)
330
- nu = mu / rho
331
- udiff, stepcount = 1.0, 0
332
- alpha_p, alpha_uv = 0.1, 0.5 # Relaxation factors
333
-
334
- for it in range(max_iter):
335
- un, vn = u.copy(), v.copy()
336
-
337
- # Momentum predictor (simplified)
338
- # Pressure correction (simplified Poisson equation)
339
- p_prime = np.zeros_like(p)
340
- for _ in range(p_iter):
341
- pn_prime = p_prime.copy()
342
- p_prime[1:-1, 1:-1] = ((pn_prime[1:-1, 2:] + pn_prime[1:-1, :-2]) * dy**2 +
343
- (pn_prime[2:, 1:-1] + pn_prime[:-2, 1:-1]) * dx**2) / \
344
- (2 * (dx**2 + dy**2))
345
- # BCs for p_prime
346
- p_prime[:, -1] = 0 # Outlet
347
- p_prime[:, 0] = p_prime[:, 1]
348
- p_prime[0, :] = p_prime[1, :]
349
- p_prime[-1, :] = p_prime[-2, :]
350
-
351
- p += alpha_p * p_prime
352
-
353
- # Velocity corrector
354
- u[1:-1, 1:-1] -= alpha_uv * (p_prime[1:-1, 1:-1] - p_prime[1:-1, :-2]) / dx
355
- v[1:-1, 1:-1] -= alpha_uv * (p_prime[1:-1, 1:-1] - p_prime[:-2, 1:-1]) / dy
356
-
357
- # Boundary Conditions
358
- u[:, 0], v[:, 0] = inlet_velocity_u, 0
359
- u[:, -1], v[:, -1] = u[:, -2], v[:, -2]
360
- u[0, :], v[0, :] = 0, 0
361
- u[-1, :], v[-1, :] = 0, 0
362
- u[obstacle_mask], v[obstacle_mask] = 0, 0
363
-
364
- udiff = np.linalg.norm(u - un) / (np.linalg.norm(un) + 1e-6)
365
- if udiff < 1e-6: break
366
-
367
- # Visualization
368
- fig, ax = plt.subplots(figsize=(10, 5))
369
- velocity_magnitude = np.sqrt(u**2 + v**2)
370
- velocity_magnitude[obstacle_mask] = np.nan
371
-
372
- cf = ax.contourf(X, Y, velocity_magnitude, levels=50, cmap=cm.jet)
373
- fig.colorbar(cf, label='Velocity Magnitude (m/s)')
374
- ax.streamplot(X, Y, u, v, color='black', linewidth=0.7, density=1.5)
375
- if np.any(obstacle_mask):
376
- ax.contour(X, Y, obstacle_mask, levels=[0.5], colors='grey', linewidths=3)
377
-
378
- ax.set_title(f'CFD Simulation Results after {it+1} iterations')
379
- ax.set_xlabel('X (m)')
380
- ax.set_ylabel('Y (m)')
381
- ax.set_aspect('equal')
382
- plt.tight_layout()
383
-
384
- return fig
385
- except Exception as e:
386
- fig, ax = plt.subplots()
387
- ax.text(0.5, 0.5, f"Error during CFD simulation:\n{e}", ha='center', va='center')
388
- return fig
389
-
390
- # --- 3. GRADIO UI ---
391
-
392
- def create_gradio_interface():
393
- """Create and launch the main Gradio interface."""
394
-
395
- css = """
396
- .gradio-container { max-width: 1280px !important; margin: auto; }
397
- .gr-tabs { border: 1px solid #E0E0E0; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.05); }
398
- footer { display: none !important; }
399
- """
400
-
401
- with gr.Blocks(theme=gr.themes.Soft(), css=css, title="Engineering Suite") as demo:
402
- gr.Markdown("# 🛠️ Engineering Suite")
403
- gr.Markdown("A collection of tools for CAD generation, G-Code, and simulation. Created by combining multiple code sources.")
404
-
405
- with gr.Tabs():
406
- # --- Tab 1: Simple Text to CAD & G-Code ---
407
- with gr.TabItem("Text to CAD & G-Code"):
408
- gr.Markdown("### Describe a simple 2D part to generate drawings and G-Code.")
409
- with gr.Row():
410
- with gr.Column(scale=1):
411
- simple_cad_input = gr.Textbox(
412
- lines=4,
413
- label="Part Description",
414
- placeholder="e.g., A 100mm x 50mm plate with a 20mm diameter hole and a 30mm long and 10mm wide slot"
415
- )
416
- simple_cad_button = gr.Button("Generate", variant="primary")
417
- with gr.Column(scale=2):
418
- simple_cad_img_output = gr.Image(label="3-View Drawing")
419
- with gr.Row():
420
- simple_cad_gcode_output = gr.Code(label="Generated G-Code", language="gcode")
421
- simple_cad_svg_output = gr.File(label="Download SVG Drawing")
422
-
423
- gr.Examples(
424
- examples=[
425
- ["A 150mm x 100mm plate with a 25mm diameter circular cutout"],
426
- ["A 100mm x 100mm plate with a 50mm long and 10mm wide slot"],
427
- ],
428
- inputs=simple_cad_input
429
- )
430
-
431
- # --- Tab 2: Advanced Text-to-CAD ---
432
- with gr.TabItem("Advanced Text-to-CAD"):
433
- gr.Markdown("### Generate a 3D model and technical drawing from a more detailed description.")
434
- with gr.Row():
435
- with gr.Column(scale=1):
436
- adv_cad_input = gr.Textbox(lines=4, label="Design Prompt", placeholder="e.g., Create a blue bracket with length 50, width 30, height 40 and thickness 5")
437
- adv_cad_button = gr.Button("Generate 3D Model", variant="primary")
438
- adv_cad_summary = gr.Markdown(label="Generation Summary")
439
- with gr.Column(scale=2):
440
- adv_cad_img_output = gr.Image(label="2D Technical Drawing")
441
- adv_cad_3d_output = gr.Plot(label="Interactive 3D Model")
442
-
443
- gr.Examples(
444
- examples=[
445
- ["A red cube with length 20, width 30, height 15"],
446
- ["A green cylinder with radius 10 and height 40"],
447
- ["A gray washer with radius 15 and height 3"],
448
- ],
449
- inputs=adv_cad_input
450
- )
451
-
452
- # --- Tab 3: 3D to Orthographic View ---
453
- with gr.TabItem("3D to Orthographic View"):
454
- gr.Markdown("### Upload a 3D model file (.stl, .obj) to generate its dimensioned orthographic views.")
455
- with gr.Row():
456
- with gr.Column(scale=1):
457
- ortho_input = gr.File(label="Upload 3D Model (.stl, .obj, .ply)")
458
- ortho_button = gr.Button("Generate Ortho Views", variant="primary")
459
- ortho_status = gr.Textbox(label="Status", interactive=False)
460
- with gr.Column(scale=2):
461
- ortho_output = gr.Image(label="Orthographic Views with Dimensions")
462
-
463
- # --- Tab 4: 2D CFD Simulation ---
464
- with gr.TabItem("2D CFD Simulation"):
465
- gr.Markdown("### Configure and run a 2D channel flow simulation.")
466
- with gr.Row():
467
- with gr.Column():
468
- gr.Markdown("**Domain & Grid**")
469
- cfd_Lx = gr.Slider(1.0, 5.0, value=2.0, label="Channel Length (Lx)")
470
- cfd_Ly = gr.Slider(0.5, 2.0, value=1.0, label="Channel Height (Ly)")
471
- cfd_Nx = gr.Slider(31, 101, value=61, step=10, label="Grid Points X (Nx)")
472
- cfd_Ny = gr.Slider(21, 81, value=41, step=10, label="Grid Points Y (Ny)")
473
- gr.Markdown("**Fluid Properties**")
474
- cfd_vel = gr.Slider(0.001, 0.1, value=0.01, label="Inlet Velocity (m/s)")
475
- cfd_rho = gr.Slider(1.0, 1000.0, value=1.0, label="Density (kg/m^3)")
476
- cfd_mu = gr.Slider(0.001, 0.1, value=0.02, label="Viscosity (Pa.s)")
477
- with gr.Column():
478
- gr.Markdown("**Obstacle (relative to domain)**")
479
- cfd_ox1 = gr.Slider(0.0, 1.0, value=0.4, label="Obstacle X1")
480
- cfd_oy1 = gr.Slider(0.0, 1.0, value=0.4, label="Obstacle Y1")
481
- cfd_ox2 = gr.Slider(0.0, 1.0, value=0.6, label="Obstacle X2")
482
- cfd_oy2 = gr.Slider(0.0, 1.0, value=0.6, label="Obstacle Y2")
483
- gr.Markdown("**Solver Settings**")
484
- cfd_max_iter = gr.Slider(100, 5000, value=500, step=100, label="Max Iterations")
485
- cfd_p_iter = gr.Slider(20, 200, value=50, step=10, label="Pressure Solver Iterations")
486
- cfd_button = gr.Button("Run Simulation", variant="primary")
487
- cfd_output = gr.Plot(label="CFD Result")
488
-
489
- # --- Event Handlers ---
490
- simple_cad_button.click(
491
- fn=process_simple_cad,
492
- inputs=[simple_cad_input],
493
- outputs=[simple_cad_img_output, simple_cad_gcode_output, simple_cad_svg_output]
494
- )
495
- adv_cad_button.click(
496
- fn=process_advanced_text_to_cad,
497
- inputs=[adv_cad_input],
498
- outputs=[adv_cad_img_output, adv_cad_3d_output, adv_cad_summary]
499
- )
500
- ortho_button.click(
501
- fn=generate_ortho_views,
502
- inputs=[ortho_input],
503
- outputs=[ortho_output, ortho_status]
504
- )
505
- cfd_button.click(
506
- fn=run_cfd_simulation,
507
- inputs=[cfd_Lx, cfd_Ly, cfd_Nx, cfd_Ny, cfd_vel, cfd_rho, cfd_mu, cfd_ox1, cfd_oy1, cfd_ox2, cfd_oy2, cfd_max_iter, cfd_p_iter],
508
- outputs=[cfd_output]
509
- )
510
-
511
- return demo
512
-
513
- # --- 4. LAUNCH THE APP ---
514
- if __name__ == "__main__":
515
- app = create_gradio_interface()
516
- app.launch()