TeeA commited on
Commit
5e4b407
·
1 Parent(s): be48866
Files changed (5) hide show
  1. .gitignore +2 -0
  2. app.py +5 -15
  3. llm_service.py +2 -6
  4. mv_utils_zs.py +60 -93
  5. test.py +73 -0
.gitignore ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ .venv/*
2
+ .env/*
app.py CHANGED
@@ -1,10 +1,10 @@
1
- import asyncio
2
  import os
3
  import platform
4
- import random
5
  import subprocess # used to connect to FreeCAD via terminal sub process
6
- import sys
7
  import tempfile
 
 
8
  from typing import Any, Dict, List, Tuple
9
 
10
  import gradio as gr # demo with gradio
@@ -52,6 +52,8 @@ def convert_step_to_obj_with_freecad(step_path, obj_path):
52
  freecad_executable = "/usr/bin/freecadcmd" # freecadcmd
53
  elif os_name == "Darwin":
54
  freecad_executable = "/Applications/FreeCAD.app/Contents/MacOS/FreeCAD"
 
 
55
  # Python script to be executed by FreeCAD
56
  _, ext = os.path.splitext(step_path)
57
  ext = ext.lower()
@@ -229,10 +231,6 @@ def query_3D_object(query: str, embedding_dict: dict, top_k: int = 4):
229
  # Metadata Extraction
230
  ####################################################################################################################
231
 
232
- import os
233
- import xml.etree.ElementTree as ET
234
- import zipfile
235
-
236
 
237
  def extract_header_from_3dxml(file_path):
238
  header_info = {}
@@ -267,8 +265,6 @@ def extract_header_from_3dxml(file_path):
267
 
268
  #######################################################################################################################
269
 
270
- import re
271
-
272
 
273
  def extract_step_metadata(file_path):
274
  metadata = {}
@@ -313,16 +309,10 @@ def extract_step_metadata(file_path):
313
  return metadata
314
 
315
 
316
- #######################################################################################################################
317
-
318
-
319
  def dict_to_markdown(metadata: dict) -> str:
320
  return "\n".join(f"{key}: {value}" for key, value in metadata.items())
321
 
322
 
323
- #######################################################################################################################
324
-
325
-
326
  # Dummy parser - Replace with real parser
327
  def parse_3d_file(original_filepath: str):
328
  if original_filepath is None:
 
 
1
  import os
2
  import platform
3
+ import re
4
  import subprocess # used to connect to FreeCAD via terminal sub process
 
5
  import tempfile
6
+ import xml.etree.ElementTree as ET
7
+ import zipfile
8
  from typing import Any, Dict, List, Tuple
9
 
10
  import gradio as gr # demo with gradio
 
52
  freecad_executable = "/usr/bin/freecadcmd" # freecadcmd
53
  elif os_name == "Darwin":
54
  freecad_executable = "/Applications/FreeCAD.app/Contents/MacOS/FreeCAD"
55
+ else:
56
+ raise Exception("Unsupported OS for FreeCAD execution: " + os_name)
57
  # Python script to be executed by FreeCAD
58
  _, ext = os.path.splitext(step_path)
59
  ext = ext.lower()
 
231
  # Metadata Extraction
232
  ####################################################################################################################
233
 
 
 
 
 
234
 
235
  def extract_header_from_3dxml(file_path):
236
  header_info = {}
 
265
 
266
  #######################################################################################################################
267
 
 
 
268
 
269
  def extract_step_metadata(file_path):
270
  metadata = {}
 
309
  return metadata
310
 
311
 
 
 
 
312
  def dict_to_markdown(metadata: dict) -> str:
313
  return "\n".join(f"{key}: {value}" for key, value in metadata.items())
314
 
315
 
 
 
 
316
  # Dummy parser - Replace with real parser
317
  def parse_3d_file(original_filepath: str):
318
  if original_filepath is None:
llm_service.py CHANGED
@@ -1,16 +1,12 @@
1
  # %%writefile llm_service.py
2
  import asyncio
3
- import base64
4
- import io
5
  import os
6
- from enum import Enum
7
- from typing import List, Tuple, Union, cast
8
 
9
- import cv2
10
  import numpy as np
 
11
  from openai import AsyncOpenAI
12
  from PIL import Image
13
- from loguru import logger
14
 
15
  from encode_image import encode_image
16
  from string_utils import StringUtils
 
1
  # %%writefile llm_service.py
2
  import asyncio
 
 
3
  import os
4
+ from typing import Union
 
5
 
 
6
  import numpy as np
7
+ from loguru import logger
8
  from openai import AsyncOpenAI
9
  from PIL import Image
 
10
 
11
  from encode_image import encode_image
12
  from string_utils import StringUtils
mv_utils_zs.py CHANGED
@@ -71,7 +71,7 @@ class Grid2Image(nn.Module):
71
  zsigma=params["convsigmaz"],
72
  )
73
  self.conv.weight.data = torch.Tensor(kn3d).repeat(1, 1, 1, 1, 1)
74
- self.conv.bias.data.fill_(0)
75
 
76
  def forward(self, x):
77
  x = self.maxpool(x.unsqueeze(1))
@@ -138,50 +138,48 @@ def points_to_2d_grid(
138
  points, grid_h=params["grid_height"], grid_w=params["grid_width"]
139
  ):
140
  """
141
- Chuyển đổi point cloud thành lưới 2D dựa trên tọa độ X, Y.
142
- Các điểm được chiếu lên một mặt phẳng được lượng tử hóa vào các ô lưới.
143
 
144
  Args:
145
- points (torch.tensor): Tensor chứa các điểm, kích thước [B, P, 3]
146
- (B: batch size, P: số lượng điểm, 3: tọa độ x, y, z)
147
- grid_h (int): Chiều cao của lưới 2D đầu ra.
148
- grid_w (int): Chiều rộng của lưới 2D đầu ra.
149
 
150
  Returns:
151
- grid (torch.tensor): Lưới 2D biểu diễn sự chiếm dụng của các điểm,
152
- kích thước [B, grid_h, grid_w].
153
- Giá trị 1.0 tại ô (y, x) nếu ít nhất một điểm rơi vào đó,
154
- ngược lại giá trị nền (params["bg_clr"]).
155
  """
156
  batch, pnum, _ = points.shape
157
  device = points.device
158
 
159
- # --- Bước 1: Chuẩn hóa tọa độ điểm ---
160
- # Tìm min/max cho từng point cloud trong batch (chỉ xét X, Y để chuẩn hóa 2D tốt hơn)
161
  pmax_xy = points[:, :, :2].max(dim=1)[0]
162
  pmin_xy = points[:, :, :2].min(dim=1)[0]
163
 
164
- # Tính tâm phạm vi dựa trên X, Y
165
  pcent_xy = (pmax_xy + pmin_xy) / 2
166
- pcent_xy = pcent_xy[:, None, :] # Thêm chiều P để broadcast [B, 1, 2]
167
 
168
- # Sử dụng phạm vi lớn nhất giữa X Y để giữ tỷ lệ aspect ratio
169
  prange_xy = (pmax_xy - pmin_xy).max(dim=-1)[0][:, None, None] # [B, 1, 1]
170
 
171
- # Thêm một epsilon nhỏ để tránh chia cho 0 nếu tất cả các điểm trùng nhau
172
  epsilon = 1e-8
173
- # Chỉ chuẩn hóa X, Y vào khoảng [-1, 1] dựa trên phạm vi X, Y
174
- # (points[:, :, :2] - pcent_xy) -> [B, P, 2]
175
- # prange_xy -> [B, 1, 1]
176
  points_normalized_xy = (points[:, :, :2] - pcent_xy) / (prange_xy + epsilon) * 2.0
177
 
178
- # Điều chỉnh tỷ lệ theo obj_ratio (nếu cần)
179
  points_normalized_xy = points_normalized_xy * params["obj_ratio"]
180
 
181
- # --- Bước 2: Ánh xạ tọa độ chuẩn hóa vào chỉ số lưới 2D ---
182
- # Ánh xạ X từ khoảng [-obj_ratio, obj_ratio] -> [0, grid_w]
183
- # Ánh xạ Y từ khoảng [-obj_ratio, obj_ratio] -> [0, grid_h]
184
- # Công thức chung: (normalized_coord + scale) / (2 * scale) * grid_dim
185
  _x = (
186
  (points_normalized_xy[:, :, 0] + params["obj_ratio"])
187
  / (2 * params["obj_ratio"])
@@ -193,32 +191,32 @@ def points_to_2d_grid(
193
  * grid_h
194
  )
195
 
196
- # Làm tròn xuống để xác định chỉ số ô lưới (index)
197
  _x = torch.floor(_x).long()
198
  _y = torch.floor(_y).long()
199
 
200
- # --- Bước 3: Giới hạn chỉ số vào phạm vi hợp lệ của lưới ---
201
- # Clip _x vào [0, grid_w - 1]
202
- # Clip _y vào [0, grid_h - 1]
203
  _x = torch.clip(_x, 0, grid_w - 1)
204
  _y = torch.clip(_y, 0, grid_h - 1)
205
 
206
- # --- Bước 4: Tạo lưới 2D đánh dấu các ô bị chiếm dụng ---
207
- # Khởi tạo lưới 2D với giá trị nền
208
  grid = torch.full(
209
  (batch, grid_h, grid_w), params["bg_clr"], dtype=torch.float32, device=device
210
  )
211
 
212
- # Tạo chỉ số batch tương ứng với mỗi điểm
213
  batch_indices = torch.arange(batch, device=device).view(-1, 1).repeat(1, pnum)
214
 
215
- # Flatten các chỉ số để dễ dàng gán giá trị
216
  batch_idx_flat = batch_indices.view(-1)
217
  y_idx_flat = _y.view(-1)
218
  x_idx_flat = _x.view(-1)
219
 
220
- # Gán giá trị 1.0 vào các ô lưới (y, x) tương ứng với vị trí các điểm
221
- # Nếu nhiều điểm rơi vào cùng một ô, ô đó vẫn chỉ giá trị 1.0
222
  grid[batch_idx_flat, y_idx_flat, x_idx_flat] = 1.0
223
 
224
  return grid
@@ -262,55 +260,27 @@ def points2grid(points, resolution=params["resolution"], depth=params["depth"]):
262
  * params["bg_clr"]
263
  )
264
 
265
- # # *** THAY ĐỔI CHÍNH Ở ĐÂY ***
266
- # # Tạo tensor nguồn (src) chứa giá trị 1.0 cho mỗi điểm
267
- # # Kích thước phải phù hợp với coordinates khi flatten: [B * pnum]
268
- # values_to_scatter = torch.ones(batch * pnum, dtype=torch.float32, device=points.device)
269
-
270
- # # Scatter giá trị 1.0 vào grid tại các vị trí `coordinates`
271
- # # Sử dụng reduce="max". Nếu ô có ít nhất một điểm, max(1.0, bg_clr) sẽ là 1.0 (nếu bg_clr <= 1)
272
- # # Nếu muốn chắc chắn là 1 bất kể bg_clr, có thể dùng reduce khác hoặc xử lý sau scatter.
273
- # # Lựa chọn an toàn hơn nếu bg_clr có thể > 1 là khởi tạo grid bằng 0 và dùng reduce='max'/'mean'
274
- # # Hoặc khởi tạo bằng bg_clr và xử lý sau scatter.
275
- # # Giả định bg_clr = 0.0 là phổ biến nhất cho occupancy grid.
276
-
277
- # grid = scatter(
278
- # values_to_scatter,
279
- # coordinates.view(-1).long(), # Flatten coordinates thành [B*pnum]
280
- # dim=0, # Scatter trên chiều 0 của grid đã flatten [B*D*R*R]
281
- # # Cần chỉ số batch tương ứng nếu grid chưa flatten theo batch
282
- # out=grid.view(-1), # Flatten grid thành [B*D*R*R] để scatter trên dim 0
283
- # reduce="max",
284
- # ) # Nếu có điểm -> giá trị ô là 1, nếu không là bg_clr
285
- # # **********************************
286
-
287
  grid = scatter(_z, coordinates.long(), dim=1, out=grid, reduce="max")
288
  grid = grid.reshape((batch, depth, resolution, resolution)).permute((0, 1, 3, 2))
289
 
290
  return grid
291
 
292
 
293
- # Giả sử bạn có thư viện scatter, ví dụ: from torch_scatter import scatter
294
- # Hoặc hàm scatter tương đương
295
- # import torch # Đảm bảo đã import torch
296
- # from torch_scatter import scatter # Ví dụ
297
-
298
-
299
  def points_to_occupancy_grid(
300
  points, resolution=params["resolution"], depth=params["depth"]
301
  ):
302
- """Quantize each point cloud to a 3D occupancy grid."""
303
 
304
  batch, pnum, _ = points.shape
305
- device = points.device # Lấy device để tạo tensor mới
306
 
307
- # --- Phần chuẩn hóa ánh xạ tọa độ giữ nguyên ---
308
  pmax, pmin = points.max(dim=1)[0], points.min(dim=1)[0]
309
  pcent = (pmax + pmin) / 2
310
  pcent = pcent[:, None, :]
311
  prange = (pmax - pmin).max(dim=-1)[0][
312
  :, None, None
313
- ] + 1e-8 # Thêm epsilon tránh chia 0
314
  points_norm = (points - pcent) / prange * 2.0
315
  points_norm[:, :, :2] = points_norm[:, :, :2] * params["obj_ratio"]
316
 
@@ -325,35 +295,33 @@ def points_to_occupancy_grid(
325
 
326
  _x = torch.clip(_x, 1, resolution - 2)
327
  _y = torch.clip(_y, 1, resolution - 2)
328
- # z_int cũng nên được clip nếu dùng làm chỉ số tọa độ
329
  z_int = torch.clip(z_int, 1, depth - 2)
330
 
331
- # --- Tính toán flattened coordinates giữ nguyên ---
332
  coordinates = z_int * resolution * resolution + _y * resolution + _x
333
- coordinates = coordinates.long() # Chuyển sang Long
334
 
335
- # --- Tạo Grid Scatter ---
336
- # Khởi tạo grid với giá trị nền ( dụ: 0)
337
- # Sử dụng torch.zeros thay torch.ones nhân bg_clr
338
- bg_clr_value = params.get("bg_clr", 0.0) # Lấy bg_clr, mặc định 0
339
  grid = torch.full(
340
  (batch, depth * resolution * resolution),
341
  bg_clr_value,
342
- dtype=torch.float32, # Hoặc dtype phù hợp
343
  device=device,
344
  )
345
 
346
- # *** THAY ĐỔI CHÍNH ĐÂY ***
347
- # Tạo tensor nguồn (src) chứa giá trị 1.0 cho mỗi điểm
348
- # Kích thước phải phù hợp với coordinates khi flatten: [B * pnum]
349
  values_to_scatter = torch.ones(batch * pnum, dtype=torch.float32, device=device)
350
 
351
- # Scatter giá trị 1.0 vào grid tại các vị trí `coordinates`
352
- # Sử dụng reduce="max". Nếu ô ít nhất một điểm, max(1.0, bg_clr) sẽ 1.0 (nếu bg_clr <= 1)
353
- # Nếu muốn chắc chắn 1 bất kể bg_clr, thể dùng reduce khác hoặc xử lý sau scatter.
354
- # Lựa chọn an toàn hơn nếu bg_clr thể > 1 khởi tạo grid bằng 0 dùng reduce='max'/'mean'
355
- # Hoặc khởi tạo bằng bg_clr xử sau scatter.
356
- # Giả định bg_clr = 0.0 là phổ biến nhất cho occupancy grid.
357
  if bg_clr_value != 0.0:
358
  print(
359
  "Warning: bg_clr is not 0.0, occupancy grid might not be strictly binary 0/1 with reduce='max'. Consider initializing grid with 0."
@@ -361,17 +329,16 @@ def points_to_occupancy_grid(
361
 
362
  grid = scatter(
363
  values_to_scatter,
364
- coordinates.view(-1), # Flatten coordinates thành [B*pnum]
365
- dim=0, # Scatter trên chiều 0 của grid đã flatten [B*D*R*R]
366
- # Cần chỉ số batch tương ứng nếu grid chưa flatten theo batch
367
- out=grid.view(-1), # Flatten grid thành [B*D*R*R] để scatter trên dim 0
368
  reduce="max",
369
- ) # Nếu điểm -> giá trị ô 1, nếu không là bg_clr
370
 
371
- # --- Reshape Permute giữ nguyên ---
372
- # Reshape lại grid về đúng kích thước 3D + batch
373
- # Lưu ý: scatter vào grid đã flatten cần reshape cẩn thận
374
- grid = grid.view(batch, depth, resolution, resolution) # Reshape lại
375
  grid = grid.permute((0, 1, 3, 2))
376
 
377
  return grid
 
71
  zsigma=params["convsigmaz"],
72
  )
73
  self.conv.weight.data = torch.Tensor(kn3d).repeat(1, 1, 1, 1, 1)
74
+ self.conv.bias.data.fill_(0) # type: ignore
75
 
76
  def forward(self, x):
77
  x = self.maxpool(x.unsqueeze(1))
 
138
  points, grid_h=params["grid_height"], grid_w=params["grid_width"]
139
  ):
140
  """
141
+ Converts a point cloud into a 2D grid based on X, Y coordinates.
142
+ Points are projected onto a plane and quantized into grid cells.
143
 
144
  Args:
145
+ points (torch.tensor): Tensor containing points, shape [B, P, 3]
146
+ (B: batch size, P: number of points, 3: x, y, z coordinates)
147
+ grid_h (int): Height of the output 2D grid.
148
+ grid_w (int): Width of the output 2D grid.
149
 
150
  Returns:
151
+ grid (torch.tensor): 2D grid representing the occupancy of points,
152
+ shape [B, grid_h, grid_w].
153
+ Value 1.0 at cell (y, x) if at least one point falls into it,
154
+ otherwise the background value (params["bg_clr"]).
155
  """
156
  batch, pnum, _ = points.shape
157
  device = points.device
158
 
159
+ # --- Step 1: Normalize point coordinates ---
160
+ # Find min/max for each point cloud in the batch (considering only X, Y for better 2D normalization)
161
  pmax_xy = points[:, :, :2].max(dim=1)[0]
162
  pmin_xy = points[:, :, :2].min(dim=1)[0]
163
 
164
+ # Compute the center and range based on X, Y
165
  pcent_xy = (pmax_xy + pmin_xy) / 2
166
+ pcent_xy = pcent_xy[:, None, :] # Add P dimension for broadcasting [B, 1, 2]
167
 
168
+ # Use the larger range between X and Y to maintain aspect ratio
169
  prange_xy = (pmax_xy - pmin_xy).max(dim=-1)[0][:, None, None] # [B, 1, 1]
170
 
171
+ # Add a small epsilon to avoid division by zero if all points overlap
172
  epsilon = 1e-8
173
+ # Normalize X, Y into the range [-1, 1] based on the X, Y range
 
 
174
  points_normalized_xy = (points[:, :, :2] - pcent_xy) / (prange_xy + epsilon) * 2.0
175
 
176
+ # Adjust the scale according to obj_ratio (if needed)
177
  points_normalized_xy = points_normalized_xy * params["obj_ratio"]
178
 
179
+ # --- Step 2: Map normalized coordinates to 2D grid indices ---
180
+ # Map X from the range [-obj_ratio, obj_ratio] -> [0, grid_w]
181
+ # Map Y from the range [-obj_ratio, obj_ratio] -> [0, grid_h]
182
+ # General formula: (normalized_coord + scale) / (2 * scale) * grid_dim
183
  _x = (
184
  (points_normalized_xy[:, :, 0] + params["obj_ratio"])
185
  / (2 * params["obj_ratio"])
 
191
  * grid_h
192
  )
193
 
194
+ # Round down to determine the grid cell indices
195
  _x = torch.floor(_x).long()
196
  _y = torch.floor(_y).long()
197
 
198
+ # --- Step 3: Clamp indices to valid grid range ---
199
+ # Clip _x to [0, grid_w - 1]
200
+ # Clip _y to [0, grid_h - 1]
201
  _x = torch.clip(_x, 0, grid_w - 1)
202
  _y = torch.clip(_y, 0, grid_h - 1)
203
 
204
+ # --- Step 4: Create a 2D grid and mark occupied cells ---
205
+ # Initialize the 2D grid with the background value
206
  grid = torch.full(
207
  (batch, grid_h, grid_w), params["bg_clr"], dtype=torch.float32, device=device
208
  )
209
 
210
+ # Create batch indices corresponding to each point
211
  batch_indices = torch.arange(batch, device=device).view(-1, 1).repeat(1, pnum)
212
 
213
+ # Flatten indices for easier assignment
214
  batch_idx_flat = batch_indices.view(-1)
215
  y_idx_flat = _y.view(-1)
216
  x_idx_flat = _x.view(-1)
217
 
218
+ # Assign a value of 1.0 to grid cells (y, x) corresponding to point positions
219
+ # If multiple points fall into the same cell, the cell still has a value of 1.0
220
  grid[batch_idx_flat, y_idx_flat, x_idx_flat] = 1.0
221
 
222
  return grid
 
260
  * params["bg_clr"]
261
  )
262
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
263
  grid = scatter(_z, coordinates.long(), dim=1, out=grid, reduce="max")
264
  grid = grid.reshape((batch, depth, resolution, resolution)).permute((0, 1, 3, 2))
265
 
266
  return grid
267
 
268
 
 
 
 
 
 
 
269
  def points_to_occupancy_grid(
270
  points, resolution=params["resolution"], depth=params["depth"]
271
  ):
272
+ """Quantize each point cloud into a 3D occupancy grid."""
273
 
274
  batch, pnum, _ = points.shape
275
+ device = points.device # Get device to create new tensors
276
 
277
+ # --- Normalization and coordinate mapping remain unchanged ---
278
  pmax, pmin = points.max(dim=1)[0], points.min(dim=1)[0]
279
  pcent = (pmax + pmin) / 2
280
  pcent = pcent[:, None, :]
281
  prange = (pmax - pmin).max(dim=-1)[0][
282
  :, None, None
283
+ ] + 1e-8 # Add epsilon to avoid division by zero
284
  points_norm = (points - pcent) / prange * 2.0
285
  points_norm[:, :, :2] = points_norm[:, :, :2] * params["obj_ratio"]
286
 
 
295
 
296
  _x = torch.clip(_x, 1, resolution - 2)
297
  _y = torch.clip(_y, 1, resolution - 2)
298
+ # z_int should also be clipped if used as coordinate indices
299
  z_int = torch.clip(z_int, 1, depth - 2)
300
 
301
+ # --- Compute flattened coordinates ---
302
  coordinates = z_int * resolution * resolution + _y * resolution + _x
303
+ coordinates = coordinates.long() # Convert to Long
304
 
305
+ # --- Create Grid and Scatter ---
306
+ # Initialize the grid with the background value (e.g., 0)
307
+ # Use torch.zeros instead of torch.ones and multiply by bg_clr
308
+ bg_clr_value = params.get("bg_clr", 0.0) # Get bg_clr, default is 0
309
  grid = torch.full(
310
  (batch, depth * resolution * resolution),
311
  bg_clr_value,
312
+ dtype=torch.float32, # Or appropriate dtype
313
  device=device,
314
  )
315
 
316
+ # Create a source tensor (src) containing a value of 1.0 for each point
317
+ # The size must match the flattened coordinates: [B * pnum]
 
318
  values_to_scatter = torch.ones(batch * pnum, dtype=torch.float32, device=device)
319
 
320
+ # Scatter the value 1.0 into the grid at the positions `coordinates`
321
+ # Use reduce="max". If a cell has at least one point, max(1.0, bg_clr) will be 1.0 (if bg_clr <= 1)
322
+ # To ensure the value is always 1 regardless of bg_clr, use a different reduce or post-process after scatter.
323
+ # A safer choice if bg_clr can be > 1 is to initialize the grid with 0 and use reduce='max'/'mean'
324
+ # Or initialize with bg_clr and process after scatter.
 
325
  if bg_clr_value != 0.0:
326
  print(
327
  "Warning: bg_clr is not 0.0, occupancy grid might not be strictly binary 0/1 with reduce='max'. Consider initializing grid with 0."
 
329
 
330
  grid = scatter(
331
  values_to_scatter,
332
+ coordinates.view(-1), # Flatten coordinates to [B*pnum]
333
+ dim=0, # Scatter along dimension 0 of the flattened grid [B*D*R*R]
334
+ out=grid.view(-1), # Flatten grid to [B*D*R*R] for scatter along dim 0
 
335
  reduce="max",
336
+ ) # If a point exists -> cell value is 1, otherwise bg_clr
337
 
338
+ # --- Reshape and Permute remain unchanged ---
339
+ # Reshape the grid back to the correct 3D + batch size
340
+ # Note: scatter into a flattened grid requires careful reshaping
341
+ grid = grid.view(batch, depth, resolution, resolution) # Reshape back
342
  grid = grid.permute((0, 1, 3, 2))
343
 
344
  return grid
test.py ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+ import os
3
+
4
+ from loguru import logger
5
+
6
+ from app import convert_to_obj, embedding_3d_object, parse_3d_file
7
+
8
+
9
+ class ModuleTest:
10
+ @staticmethod
11
+ async def test_process_file(file_path: str, debug_mode: bool = False) -> dict:
12
+ report = {}
13
+ try:
14
+ obj_path = convert_to_obj(file_path)
15
+ report["CONV_TO_OBJ"] = "PASSED"
16
+ if debug_mode:
17
+ logger.info(f"Obj path: {obj_path}")
18
+ except Exception:
19
+ report["CONV_TO_OBJ"] = "FAILED"
20
+
21
+ if report["CONV_TO_OBJ"] == "PASSED":
22
+ obj_path = locals().get("obj_path", None)
23
+ assert obj_path is not None, "Conversion to OBJ failed"
24
+ assert os.path.exists(obj_path), "Converted OBJ file does not exist"
25
+
26
+ try:
27
+ embeddings = await embedding_3d_object(obj_path)
28
+ report["EMBEDDING_3D_OBJ"] = "PASSED"
29
+ if debug_mode:
30
+ logger.info(f"Description: {embeddings['description']}")
31
+ except Exception:
32
+ report["EMBEDDING_3D_OBJ"] = "FAILED"
33
+ else:
34
+ report["EMBEDDING_3D_OBJ"] = "FAILED"
35
+
36
+ try:
37
+ metadata = parse_3d_file(file_path)
38
+ report["PARSE_METADATA"] = "PASSED"
39
+ if debug_mode:
40
+ logger.info(f"Parsed metadata: {metadata}")
41
+ except Exception:
42
+ report["PARSE_METADATA"] = "FAILED"
43
+ return report
44
+
45
+ async def test(self, file_paths: list[str], debug_mode: bool = False) -> None:
46
+ for file_path in file_paths:
47
+ basename = os.path.basename(file_path)
48
+ response = await self.test_process_file(
49
+ file_path=file_path, debug_mode=debug_mode
50
+ )
51
+ for key, value in response.items():
52
+ if value == "FAILED":
53
+ logger.error(f"Processed file `{basename}` failed {key}")
54
+ else:
55
+ logger.info(f"Processed file `{basename}` successfully {key}!")
56
+
57
+
58
+ if __name__ == "__main__":
59
+ asyncio.run(
60
+ ModuleTest().test(
61
+ file_paths=[
62
+ # "/Users/tridoan/Spartan/Datum/service-ai/poc/resources/notebooks/3d_files/c5-corvette-knuckle-1.snapshot.1/C5 Knuckle Mesh.stl", # ok
63
+ # "/Users/tridoan/Spartan/Datum/service-ai/poc/resources/notebooks/3d_files/c5-corvette-knuckle-1.snapshot.1/C5 Knuckle 3mf.3mf", # ok
64
+ # "/Users/tridoan/Spartan/Datum/service-ai/poc/resources/notebooks/3d_files/nema-17-stepper-motors-coaxial-60-48-39-23mm-1.snapshot.3/NEMA 17 Stepper Motor 23mm-NEMA 17 Stepper Motor 23mm.obj", # ok
65
+ # "/Users/tridoan/Spartan/Datum/service-ai/poc/resources/notebooks/3d_files/nema-17-stepper-motors-coaxial-60-48-39-23mm-1.snapshot.3/NEMA 17 Stepper Motor 48mm-NEMA 17 Stepper Motor 48mm.step", # ok
66
+ # "/Users/tridoan/Spartan/Datum/service-ai/poc/resources/notebooks/3d_files/engrenagens-5.snapshot.6/Engre_con_Z16_mod_1_5.FCStd", # ok
67
+ # "/Users/tridoan/Spartan/Datum/service-ai/poc/resources/notebooks/3d_files/radial engine.3dxml",
68
+ # "/Users/tridoan/Spartan/Datum/service-ai/poc/resources/notebooks/3d_files/ARBOR GEAR.dwg",
69
+ # "/Users/tridoan/Spartan/Datum/service-ai/poc/resources/notebooks/3d_files/electrical-switch-1.snapshot.3/Electrical switch.IGS"
70
+ ],
71
+ debug_mode=True,
72
+ )
73
+ )