phg-vis 1.3.2__py3-none-any.whl → 1.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
phg/gcu_api.py ADDED
@@ -0,0 +1,709 @@
1
+ #################################
2
+ # #
3
+ # C++ API Module #
4
+ # #
5
+ #################################
6
+ #系统目录下 gcu_api.py
7
+ import importlib.resources
8
+ import http.client
9
+ import subprocess
10
+ import os
11
+ import psutil
12
+ import ctypes
13
+ import numpy as np
14
+ import phg
15
+ from coordinate_system import vec3, quat, coord3
16
+
17
+ #################################
18
+ # C++ DLL Related Code
19
+ #################################
20
+
21
+ library_module = "phg"
22
+
23
+ try:
24
+ # 获取phg.vis模块的安装目录
25
+ with importlib.resources.path(library_module, "") as pkg_path:
26
+ # 拼接GCU.dll的完整路径
27
+ gcu_dll_path = os.path.join(pkg_path, "vis\GCU.dll")
28
+ current_dir = os.path.join(pkg_path, "vis")
29
+ # 加载DLL
30
+ gcu = ctypes.CDLL(str(gcu_dll_path))
31
+ print(f"成功加载DLL:{gcu_dll_path}")
32
+ except (ImportError, FileNotFoundError) as e:
33
+ print(f"获取路径失败:{e}")
34
+
35
+ #################################
36
+ # Common
37
+ #################################
38
+
39
+ # Define echo function
40
+ echo_func = gcu.echo
41
+ echo_func.restype = None
42
+ echo_func.argtypes = [ctypes.c_int]
43
+
44
+ def echo(becho):
45
+ """Echo a given integer value to the C++ function."""
46
+ echo_func(ctypes.c_int(becho))
47
+
48
+ # GT_get_int Function
49
+ GT_get_int = gcu.GT_get_int
50
+ GT_get_int.restype = ctypes.c_int
51
+ GT_get_int.argtypes = [ctypes.c_char_p, ctypes.c_int]
52
+
53
+ def GetInt(key, defaultvalue):
54
+ """Get integer value associated with a key."""
55
+ return GT_get_int(key.encode(), defaultvalue)
56
+
57
+ # GT_get_str Function
58
+ GT_get_str = gcu.GT_get_string
59
+ GT_get_str.restype = ctypes.c_char_p
60
+ GT_get_str.argtypes = [ctypes.c_char_p, ctypes.c_char_p]
61
+
62
+ def getstr(key, defaultvalue=""):
63
+ """Get GT_set_string value associated with a key.
64
+
65
+ Args:
66
+ key: The key to look up (string or bytes)
67
+ defaultvalue: Default value if key not found (string or bytes)
68
+
69
+ Returns:
70
+ str: The string value associated with the key, or default if not found
71
+ """
72
+ # Convert inputs to bytes if they aren't already
73
+ key_bytes = key.encode('utf-8') if isinstance(key, str) else bytes(key)
74
+ default_bytes = defaultvalue.encode('utf-8') if isinstance(defaultvalue, str) else bytes(defaultvalue)
75
+
76
+ # Call the C function
77
+ result = GT_get_str(key_bytes, default_bytes)
78
+
79
+ # Convert result back to Python string
80
+ return result.decode('utf-8') if result else ""
81
+
82
+ # GT_set_string Function
83
+ gcu.GT_set_string.argtypes = (ctypes.c_char_p, ctypes.c_char_p)
84
+
85
+ # 定义函数返回类型(如果有的话)
86
+ gcu.GT_set_string.restype = None
87
+
88
+ # 创建 Python 封装函数
89
+ def setstr(key: str, value: str):
90
+ gcu.GT_set_string(key.encode('utf-8'), value.encode('utf-8'))
91
+
92
+ # Get Mark Coordinates
93
+ GT_get_mark = gcu.GT_get_mark # Assuming this is defined elsewhere
94
+ def get_markC(id):
95
+ """Retrieve mark coordinates by ID."""
96
+ m = (ctypes.c_float * 15)() # Create an array for mark data
97
+ result = GT_get_mark(id, m)
98
+ if result == 1:
99
+ c = coord3() # Create coord3 instance
100
+ c.ux = vec3(m[0], m[1], m[2])
101
+ c.uy = vec3(m[3], m[4], m[5])
102
+ c.uz = vec3(m[6], m[7], m[8])
103
+ c.s = vec3(m[9], m[10], m[11])
104
+ c.o = vec3(m[12], m[13], m[14])
105
+ return c
106
+ return None # If the mark is not found
107
+
108
+ #####################################
109
+ # PHG
110
+ #####################################
111
+ # DoPHG Function
112
+ do_phg_func = gcu.doPHG
113
+ do_phg_func.restype = ctypes.c_void_p
114
+ do_phg_func.argtypes = [ctypes.c_char_p]
115
+
116
+ def DoPHG(script):
117
+ # 先打印 script 内容,检查是否符合预期
118
+ try:
119
+ do_phg_func(script.encode())
120
+ except OSError as e:
121
+ print(f"OSError occurred: {e}")
122
+
123
+ # Get PHG Node Coordinates
124
+ get_phg_nodeCL = gcu.get_phg_nodeCL
125
+ get_phg_nodeCL.restype = ctypes.c_bool
126
+ get_phg_nodeCL.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_float)]
127
+
128
+ def phg_nodeCL(node_name):
129
+ """Get coordinates of a PHG node by name."""
130
+ c = coord3() # Create coord3 instance
131
+ m = (ctypes.c_float * 15)() # Create an array for coordinates
132
+ node_name_encoded = node_name.encode('utf-8') # Encode string to bytes
133
+ success = get_phg_nodeCL(node_name_encoded, m) # Call C++ function
134
+
135
+ if not success:
136
+ raise ValueError(f"Node '{node_name}' not found")
137
+
138
+ c.ux = vec3(m[0], m[1], m[2])
139
+ c.uy = vec3(m[3], m[4], m[5])
140
+ c.uz = vec3(m[6], m[7], m[8])
141
+ c.s = vec3(m[9], m[10], m[11])
142
+ c.o = vec3(m[12], m[13], m[14])
143
+
144
+ return c # Return coord3 instance
145
+
146
+ get_phg_nodeCW = gcu.get_phg_nodeCW
147
+ get_phg_nodeCW.restype = ctypes.c_bool
148
+ get_phg_nodeCW.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_float)]
149
+ def phg_nodeCW(node_name):
150
+ """Get coordinates of a PHG node by name."""
151
+ c = coord3() # Create coord3 instance
152
+ m = (ctypes.c_float * 15)() # Create an array for coordinates
153
+ node_name_encoded = node_name.encode('utf-8') # Encode string to bytes
154
+ success = get_phg_nodeCW(node_name_encoded, m) # Call C++ function
155
+
156
+ if not success:
157
+ raise ValueError(f"Node '{node_name}' not found")
158
+
159
+ c.ux = vec3(m[0], m[1], m[2])
160
+ c.uy = vec3(m[3], m[4], m[5])
161
+ c.uz = vec3(m[6], m[7], m[8])
162
+ c.s = vec3(m[9], m[10], m[11])
163
+ c.o = vec3(m[12], m[13], m[14])
164
+
165
+ return c # Return coord3 instance
166
+
167
+ #####################################
168
+ # Pipe
169
+ #####################################
170
+ pipe_directions = ['F', 'U', 'B', 'D'] # 前进、上升、后退、下降
171
+
172
+ # 4. 改进的路径距离度量
173
+ def path_distance(path1, path2):
174
+ """综合考虑转向点、三维形状和方向分布的复合距离"""
175
+ # 转向点差异
176
+ def get_turns(path):
177
+ return {i for i in range(1, len(path)) if path[i] != path[i-1]}
178
+ turn_dist = len(get_turns(path1).symmetric_difference(get_turns(path2))) * 0.5
179
+
180
+ # 三维形状差异
181
+ def get_shape(path):
182
+ pos = np.zeros(3)
183
+ shape = {tuple(pos)}
184
+ for d in path:
185
+ pos += {'F': [1,0,0], 'B': [-1,0,0],
186
+ 'U': [0,1,0], 'D': [0,-1,0]}[d]
187
+ shape.add(tuple(pos))
188
+ return shape
189
+ shape_dist = len(get_shape(path1).symmetric_difference(get_shape(path2))) * 0.8
190
+
191
+ # 方向分布差异
192
+ hist_dist = sum(abs(path1.count(d) - path2.count(d)) for d in pipe_directions) * 0.3
193
+
194
+ return turn_dist + shape_dist + hist_dist
195
+
196
+ # Get Pipes Coordinates
197
+ get_pipes_coord_ = gcu.get_pipes_coord
198
+ get_pipes_coord_.restype = None
199
+ get_pipes_coord_.argtypes = [ctypes.POINTER(ctypes.c_float)]
200
+
201
+ def get_pipes_coord():
202
+ """Retrieve coordinates of pipes."""
203
+ c = coord3() # Create coord3 instance
204
+ m = (ctypes.c_float * 15)() # Create an array for coordinates
205
+ get_pipes_coord_(m) # Call the C++ function
206
+ c.ux = vec3(m[0], m[1], m[2])
207
+ c.uy = vec3(m[3], m[4], m[5])
208
+ c.uz = vec3(m[6], m[7], m[8])
209
+ c.s = vec3(m[9], m[10], m[11])
210
+ c.o = vec3(m[12], m[13], m[14])
211
+ return c
212
+
213
+ #######################################
214
+ # Grid
215
+ #######################################
216
+ # Create Grid Scene Function
217
+ gcu.create_grid_scene.restype = None
218
+ gcu.create_grid_scene.argtypes = [
219
+ ctypes.c_float, # x1
220
+ ctypes.c_float, # y1
221
+ ctypes.c_float, # z1
222
+ ctypes.c_float, # x2
223
+ ctypes.c_float, # y2
224
+ ctypes.c_float # z2
225
+ ]
226
+
227
+ def create_grid_scene(x1: float, y1: float, z1: float, x2: float, y2: float, z2: float):
228
+ """
229
+ 创建网格场景
230
+
231
+ 参数:
232
+ x1, y1, z1: 场景起始坐标
233
+ x2, y2, z2: 场景结束坐标
234
+
235
+ 示例:
236
+ create_grid_scene(0, 0, 0, 50, 50, 50) # 创建50x50x50的网格场景
237
+ """
238
+ try:
239
+ gcu.create_grid_scene(
240
+ ctypes.c_float(x1), ctypes.c_float(y1), ctypes.c_float(z1),
241
+ ctypes.c_float(x2), ctypes.c_float(y2), ctypes.c_float(z2)
242
+ )
243
+ print(f"网格场景创建成功: ({x1},{y1},{z1}) 到 ({x2},{y2},{z2})")
244
+ except Exception as e:
245
+ print(f"创建网格场景失败: {e}")
246
+
247
+ # Create Writable Grid Function
248
+ gcu.create_grid_writable.restype = None
249
+ gcu.create_grid_writable.argtypes = []
250
+
251
+ def create_grid_writable():
252
+ """
253
+ 创建可写网格
254
+
255
+ 说明:
256
+ 基于当前网格场景创建可编辑的网格副本
257
+ """
258
+ try:
259
+ gcu.create_grid_writable()
260
+ print("可写网格创建成功")
261
+ except Exception as e:
262
+ print(f"创建可写网格失败: {e}")
263
+
264
+ gcu.get_grid_valW.restype = ctypes.c_int
265
+ gcu.get_grid_valW.argtypes = [ctypes.c_float, ctypes.c_float, ctypes.c_float]
266
+
267
+ def get_grid_value(x, y, z):
268
+ """Get grid value at specified coordinates."""
269
+ return gcu.get_grid_valW(ctypes.c_float(x), ctypes.c_float(y), ctypes.c_float(z))
270
+
271
+ gcu.get_grid_val.restype = ctypes.c_int
272
+ gcu.get_grid_val.argtypes = [ctypes.c_float, ctypes.c_float, ctypes.c_float]
273
+
274
+ def cell(x, y, z):
275
+ """Get grid value at specified coordinates."""
276
+ return gcu.get_grid_val(ctypes.c_float(x), ctypes.c_float(y), ctypes.c_float(z))
277
+
278
+ def cellat(p):
279
+ """Get grid value at specified coordinates."""
280
+ return gcu.get_grid_val(ctypes.c_float(p.x), ctypes.c_float(p.y), ctypes.c_float(p.z))
281
+
282
+ getcell = cell
283
+ getgrid = cell
284
+
285
+ gcu.set_grid_val.restype = None
286
+ gcu.set_grid_val.argtypes = [ctypes.c_float, ctypes.c_float, ctypes.c_float, ctypes.c_int]
287
+
288
+ def set_grid_value(x, y, z, val):
289
+ """Set grid value at specified coordinates."""
290
+ gcu.set_grid_val(ctypes.c_float(x), ctypes.c_float(y), ctypes.c_float(z), ctypes.c_int(val))
291
+
292
+ setcell = set_grid_value
293
+ setgrid = set_grid_value
294
+
295
+ def set_grid_valueXZY(x, z, y, val):
296
+ """Set grid value at specified coordinates."""
297
+ gcu.set_grid_val(ctypes.c_float(x), ctypes.c_float(y), ctypes.c_float(z), ctypes.c_int(val))
298
+
299
+ setcellXZY = set_grid_valueXZY
300
+
301
+ # Field Value Function
302
+ gcu.get_fieldV_at.restype = ctypes.c_float
303
+ gcu.get_fieldV_at.argtypes = [ctypes.c_float, ctypes.c_float, ctypes.c_float]
304
+
305
+ def get_field_value(x, y, z):
306
+ """Get field value at specified coordinates."""
307
+ return gcu.get_fieldV_at(ctypes.c_float(x), ctypes.c_float(y), ctypes.c_float(z))
308
+
309
+ # Get Grid Coordinate Function
310
+ get_grid_coord_ = gcu.get_grid_coord
311
+ get_grid_coord_.restype = None
312
+ get_grid_coord_.argtypes = [ctypes.POINTER(ctypes.c_float)]
313
+
314
+ def get_grid_coord():
315
+ """Retrieve grid coordinates."""
316
+ c = coord3() # Create coord3 instance
317
+ m = (ctypes.c_float * 15)() # Create an array for coordinates
318
+ get_grid_coord_(m) # Call the C++ function
319
+ c.ux = vec3(m[0], m[1], m[2])
320
+ c.uy = vec3(m[3], m[4], m[5])
321
+ c.uz = vec3(m[6], m[7], m[8])
322
+ c.s = vec3(m[9], m[10], m[11])
323
+ c.o = vec3(m[12], m[13], m[14])
324
+ return c
325
+
326
+ # AABB (Axis-Aligned Bounding Box)
327
+ gcu.get_grid_aabb.restype = None # No return value
328
+ gcu.get_grid_aabb.argtypes = [
329
+ ctypes.POINTER(ctypes.c_int), # x1
330
+ ctypes.POINTER(ctypes.c_int), # y1
331
+ ctypes.POINTER(ctypes.c_int), # z1
332
+ ctypes.POINTER(ctypes.c_int), # x2
333
+ ctypes.POINTER(ctypes.c_int), # y2
334
+ ctypes.POINTER(ctypes.c_int) # z2
335
+ ]
336
+
337
+ def get_grid_aabb():
338
+ """Retrieve the AABB of the grid."""
339
+ x1, y1, z1 = ctypes.c_int(), ctypes.c_int(), ctypes.c_int()
340
+ x2, y2, z2 = ctypes.c_int(), ctypes.c_int(), ctypes.c_int()
341
+
342
+ # Call the C++ function
343
+ gcu.get_grid_aabb(ctypes.byref(x1), ctypes.byref(y1), ctypes.byref(z1),
344
+ ctypes.byref(x2), ctypes.byref(y2), ctypes.byref(z2))
345
+
346
+ return (x1.value, y1.value, z1.value, x2.value, y2.value, z2.value)
347
+
348
+ gridaabb = get_grid_aabb
349
+
350
+ # Collision Detection
351
+ def check_collision(p):
352
+ """Check for collision at point p."""
353
+ offsets = np.array([
354
+ [0, 0, 0],
355
+ [-0.17, 0, 0], [0.17, 0, 0],
356
+ [0, -0.17, 0], [0, 0.17, 0],
357
+ [0, 0, -0.17], [0, 0, 0.17],
358
+ [-0.17, -0.17, -0.17], [0.17, 0.17, 0.17],
359
+ [0.17, 0, -0.17]
360
+ ])
361
+
362
+ positions = np.array([p.x, p.y, p.z])
363
+ return any(get_grid_value(*(positions + offset)) != 0 for offset in offsets)
364
+
365
+ # Load Grid Function
366
+ gcu.load_grid.restype = None
367
+ gcu.load_grid.argtypes = [ctypes.c_char_p]
368
+
369
+ def grid_scene_read(filename):
370
+ """Load grid data from file."""
371
+ gcu.load_grid(filename.encode('utf-8'))
372
+
373
+ # Grid Import Function
374
+ gcu.import_grid_file.restype = None
375
+ gcu.import_grid_file.argtypes = [ctypes.c_char_p]
376
+
377
+ def import_grid(filename):
378
+ """
379
+ 导入grid文件到当前场景grid中
380
+
381
+ 参数:
382
+ filename: 要导入的grid文件名
383
+ """
384
+ try:
385
+ gcu.import_grid_file(filename.encode('utf-8'))
386
+ except Exception as e:
387
+ print(f"导入grid失败: {e}")
388
+
389
+
390
+ # Save Grid Function
391
+ gcu.save_grid.restype = None
392
+ gcu.save_grid.argtypes = [ctypes.c_char_p]
393
+
394
+ def save_scene_grid(filename):
395
+ """Save grid data to file."""
396
+ gcu.save_grid(filename.encode('utf-8'))
397
+
398
+ # Load Writable Grid Function
399
+ gcu.load_grid_writable.restype = None
400
+ gcu.load_grid_writable.argtypes = [ctypes.c_char_p]
401
+
402
+ def load_grid_writable(filename):
403
+ """Load grid data into writable grid."""
404
+ gcu.load_grid_writable(filename.encode('utf-8'))
405
+
406
+ # Save Writable Grid Function
407
+ gcu.save_grid_writable.restype = None
408
+ gcu.save_grid_writable.argtypes = [ctypes.c_char_p]
409
+
410
+ def save_grid_writable(filename):
411
+ """Save writable grid data to file."""
412
+ gcu.save_grid_writable(filename.encode('utf-8'))
413
+
414
+ # object_grid_from_sm
415
+ gcu.object_grid_from_sm.restype = ctypes.c_bool
416
+ gcu.object_grid_from_sm.argtypes = [ctypes.c_char_p, ctypes.c_char_p]
417
+
418
+ def object_grid_from_sm(smfile: str, name: str) -> bool:
419
+ try:
420
+ # 将字符串编码为UTF-8字节串
421
+ smfile_encoded = smfile.encode('utf-8')
422
+ name_encoded = name.encode('utf-8')
423
+
424
+ # 调用C++函数
425
+ success = gcu.object_grid_from_sm(smfile_encoded, name_encoded)
426
+
427
+ return success
428
+ except Exception as e:
429
+ print(f"Error in object_grid_from_sm: {e}")
430
+ return False
431
+
432
+ # object_grid_from_obj
433
+ gcu.object_grid_from_obj.restype = ctypes.c_bool
434
+ gcu.object_grid_from_obj.argtypes = [ctypes.c_char_p, ctypes.c_char_p]
435
+
436
+ def object_grid_from_obj(smfile: str, name: str) -> bool:
437
+ try:
438
+ # 将字符串编码为UTF-8字节串
439
+ smfile_encoded = smfile.encode('utf-8')
440
+ name_encoded = name.encode('utf-8')
441
+
442
+ # 调用C++函数
443
+ success = gcu.object_grid_from_obj(smfile_encoded, name_encoded)
444
+
445
+ return success
446
+ except Exception as e:
447
+ print(f"Error in object_grid_from_obj: {e}")
448
+ return False
449
+
450
+ # Object Grid AABB 接口
451
+ gcu.object_grid_aabb.restype = ctypes.c_bool
452
+ gcu.object_grid_aabb.argtypes = [
453
+ ctypes.c_char_p, # name
454
+ ctypes.POINTER(ctypes.c_int), # x1
455
+ ctypes.POINTER(ctypes.c_int), # y1
456
+ ctypes.POINTER(ctypes.c_int), # z1
457
+ ctypes.POINTER(ctypes.c_int), # x2
458
+ ctypes.POINTER(ctypes.c_int), # y2
459
+ ctypes.POINTER(ctypes.c_int) # z2
460
+ ]
461
+
462
+ def get_object_aabb(obj_name: str):
463
+ """
464
+ 获取指定对象的轴对齐包围盒(AABB)
465
+
466
+ 参数:
467
+ obj_name: 对象名称
468
+
469
+ 返回:
470
+ tuple: (x1, y1, z1, x2, y2, z2) 或 None(如果对象不存在)
471
+ """
472
+ x1, y1, z1 = ctypes.c_int(), ctypes.c_int(), ctypes.c_int()
473
+ x2, y2, z2 = ctypes.c_int(), ctypes.c_int(), ctypes.c_int()
474
+
475
+ success = gcu.object_grid_aabb(
476
+ obj_name.encode('utf-8'),
477
+ ctypes.byref(x1),
478
+ ctypes.byref(y1),
479
+ ctypes.byref(z1),
480
+ ctypes.byref(x2),
481
+ ctypes.byref(y2),
482
+ ctypes.byref(z2)
483
+ )
484
+
485
+ if success:
486
+ return (x1.value, y1.value, z1.value,
487
+ x2.value, y2.value, z2.value)
488
+ return None
489
+
490
+ # 位置碰撞检测函数
491
+ gcu.object_grid_pos_collision.restype = ctypes.c_bool
492
+ gcu.object_grid_pos_collision.argtypes = [
493
+ ctypes.c_char_p, # obj_name
494
+ ctypes.c_float, # x
495
+ ctypes.c_float, # y
496
+ ctypes.c_float # z
497
+ ]
498
+
499
+ def object_grid_pos_collision(obj_name: str, x: float, y: float, z: float) -> bool:
500
+ """
501
+ 测试指定位置是否与对象发生碰撞(修正函数名引用)
502
+
503
+ 参数:
504
+ obj_name: 对象名称
505
+ x, y, z: 测试位置坐标
506
+
507
+ 返回:
508
+ True: 发生碰撞
509
+ False: 未发生碰撞
510
+ """
511
+ try:
512
+ obj_name_encoded = obj_name.encode('utf-8')
513
+ return gcu.object_grid_pos_collision(obj_name_encoded, x, y, z) # 调用正确的函数名
514
+ except Exception as e:
515
+ print(f"碰撞检测错误: {e}")
516
+ return False
517
+
518
+ # Grid PHG Node with ID Function (支持扩散版本)
519
+ gcu.grid_phgnode_with_id.restype = None
520
+ gcu.grid_phgnode_with_id.argtypes = [ctypes.c_int, ctypes.c_char_p, ctypes.c_int, ctypes.c_char]
521
+
522
+ def grid_phgnode_with_id(gridid: int, node_name: str, diffusion_distance: int = 0, diffusion_value: int = 1):
523
+ """
524
+ Create a grid from a PHG node with specified grid ID, with optional diffusion
525
+
526
+ Args:
527
+ gridid: The ID to assign to the grid cells
528
+ node_name: Name of the PHG node to gridize
529
+ diffusion_distance: Distance for diffusion (0 = no diffusion)
530
+ diffusion_value: Value to use for diffused cells
531
+
532
+ Example:
533
+ # Basic usage (no diffusion)
534
+ grid_phgnode_with_id(1, "my_node")
535
+
536
+ # With diffusion
537
+ grid_phgnode_with_id(1, "my_node", 2, 64) # Diffusion distance 2, value 64
538
+ grid_phgnode_with_id(1, "my_node", 1) # Diffusion distance 1, default value 1
539
+ """
540
+ try:
541
+ # Convert parameters to C types
542
+ c_gridid = ctypes.c_int(gridid)
543
+ c_node_name = node_name.encode('utf-8')
544
+ c_diffusion_distance = ctypes.c_int(diffusion_distance)
545
+ c_diffusion_value = ctypes.c_char(diffusion_value)
546
+
547
+ # Call the C++ function
548
+ gcu.grid_phgnode_with_id(c_gridid, c_node_name, c_diffusion_distance, c_diffusion_value)
549
+ except Exception as e:
550
+ print(f"Error in grid_phgnode_with_id: {e}")
551
+
552
+ # Test Mesh Grid Collision Function
553
+ gcu.test_phgnode_collision.restype = ctypes.c_bool
554
+ gcu.test_phgnode_collision.argtypes = [ctypes.c_char_p, ctypes.c_int]
555
+
556
+ def test_phgnode_collision(node_name: str, gridid: int) -> bool:
557
+ """
558
+ Test if a mesh would collide with existing grid cells when gridized
559
+
560
+ Args:
561
+ node_name: Name of the PHG node to test
562
+
563
+ Returns:
564
+ bool: True if collision detected, False otherwise
565
+
566
+ Example:
567
+ if test_phgnode_collision("my_object"):
568
+ print("Collision detected!")
569
+ """
570
+ try:
571
+ c_node_name = node_name.encode('utf-8')
572
+ return gcu.test_phgnode_collision(c_node_name, gridid)
573
+ except Exception as e:
574
+ print(f"Error in test_phgnode_collision: {e}")
575
+ return False
576
+
577
+ def phg_to_grid(phg_script: str, node_name: str = "root", grid_id: int = 1) -> bool:
578
+ """
579
+ 将PHG脚本转换为Grid
580
+
581
+ 参数:
582
+ phg_script: PHG脚本字符串
583
+ node_name: PHG节点名称
584
+ grid_id: 网格ID
585
+
586
+ 返回:
587
+ success: 是否成功
588
+
589
+ 示例:
590
+ # 简单用法
591
+ phg_to_grid("{md:sphere 1;}")
592
+
593
+ # 指定节点名称和网格ID
594
+ phg_to_grid("{my_sphere{md:sphere 1; p:5,5,5}}", "my_sphere", 2)
595
+ """
596
+ try:
597
+ # 第一步:执行PHG脚本
598
+ DoPHG(phg_script)
599
+
600
+ # 第二步:设置绘制
601
+ DoPHG(f"setup_draw('{node_name}');")
602
+
603
+ # 第三步:转换为Grid
604
+ grid_phgnode_with_id(grid_id, node_name)
605
+
606
+ print(f"成功将PHG转换为Grid (节点: {node_name}, 网格ID: {grid_id})")
607
+ return True
608
+
609
+ except Exception as e:
610
+ print(f"PHG到Grid转换失败: {e}")
611
+ return False
612
+
613
+ # Grid Dump Function
614
+ gcu.grid_scene_dump.restype = None
615
+ gcu.grid_scene_dump.argtypes = []
616
+
617
+ def grid_dump():
618
+ """Dump grid information to console."""
619
+ gcu.grid_scene_dump()
620
+
621
+ #######################################
622
+ # Grid ID Map
623
+ #######################################
624
+
625
+ # Get Grid ID Map Function
626
+ gcu.get_grid_idmap.restype = ctypes.c_short
627
+ gcu.get_grid_idmap.argtypes = [ctypes.c_float, ctypes.c_float, ctypes.c_float]
628
+
629
+ def get_grid_idmap(x, y, z):
630
+ """
631
+ Get grid ID value at specified coordinates using octree structure.
632
+
633
+ Args:
634
+ x (float): X coordinate
635
+ y (float): Y coordinate
636
+ z (float): Z coordinate
637
+
638
+ Returns:
639
+ short: Grid ID value at the specified position
640
+ """
641
+ return gcu.get_grid_idmap(ctypes.c_float(x), ctypes.c_float(y), ctypes.c_float(z))
642
+
643
+ #####################################
644
+ # mesh
645
+ #####################################
646
+ # Get Mesh AABB Bounding Box
647
+ get_mesh_aabb = gcu.get_mesh_aabb
648
+ get_mesh_aabb.restype = ctypes.c_bool
649
+ get_mesh_aabb.argtypes = [
650
+ ctypes.c_char_p,
651
+ ctypes.POINTER(ctypes.c_float), # min_x
652
+ ctypes.POINTER(ctypes.c_float), # min_y
653
+ ctypes.POINTER(ctypes.c_float), # min_z
654
+ ctypes.POINTER(ctypes.c_float), # max_x
655
+ ctypes.POINTER(ctypes.c_float), # max_y
656
+ ctypes.POINTER(ctypes.c_float) # max_z
657
+ ]
658
+
659
+ def mesh_aabb(mesh_name):
660
+ """Get AABB bounding box of a mesh by name.
661
+
662
+ Args:
663
+ mesh_name (str): Name of the mesh
664
+
665
+ Returns:
666
+ tuple: (min_x, min_y, min_z, max_x, max_y, max_z) if successful
667
+
668
+ Raises:
669
+ ValueError: If mesh not found or empty
670
+ """
671
+ # Create variables to store the results
672
+ min_x = ctypes.c_float()
673
+ min_y = ctypes.c_float()
674
+ min_z = ctypes.c_float()
675
+ max_x = ctypes.c_float()
676
+ max_y = ctypes.c_float()
677
+ max_z = ctypes.c_float()
678
+
679
+ # Encode string to bytes
680
+ mesh_name_encoded = mesh_name.encode('utf-8')
681
+
682
+ # Call C++ function
683
+ success = get_mesh_aabb(
684
+ mesh_name_encoded,
685
+ ctypes.byref(min_x),
686
+ ctypes.byref(min_y),
687
+ ctypes.byref(min_z),
688
+ ctypes.byref(max_x),
689
+ ctypes.byref(max_y),
690
+ ctypes.byref(max_z)
691
+ )
692
+
693
+ if not success:
694
+ raise ValueError(f"Mesh '{mesh_name}' not found or empty")
695
+
696
+ # Return the AABB coordinates as a tuple
697
+ return (min_x.value, min_y.value, min_z.value,
698
+ max_x.value, max_y.value, max_z.value)
699
+
700
+
701
+ #####################################
702
+ # gcu_api VIZ
703
+ #####################################
704
+ def VisPHG(script):
705
+ phg.vis(script)
706
+
707
+ #screen shot
708
+ def ImagePHG(script, filename='shot.png'):
709
+ phg.image(script, filename)