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/__init__.py +113 -42
- phg/ai_bridge.py +174 -0
- phg/gcu_api.py +709 -0
- phg/pipe_string_phg.py +144 -116
- phg/shader_render.py +26 -12
- phg/visphg.py +79 -55
- phg/web_three.py +164 -0
- {phg_vis-1.3.2.dist-info → phg_vis-1.4.0.dist-info}/METADATA +56 -1
- {phg_vis-1.3.2.dist-info → phg_vis-1.4.0.dist-info}/RECORD +12 -9
- {phg_vis-1.3.2.dist-info → phg_vis-1.4.0.dist-info}/LICENSE +0 -0
- {phg_vis-1.3.2.dist-info → phg_vis-1.4.0.dist-info}/WHEEL +0 -0
- {phg_vis-1.3.2.dist-info → phg_vis-1.4.0.dist-info}/top_level.txt +0 -0
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)
|