phg-vis 1.3.2__py3-none-any.whl → 1.4.1__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 CHANGED
@@ -11,21 +11,33 @@ Main Features:
11
11
  - Multiple pipe visualization support
12
12
  - Real-time and image rendering capabilities
13
13
 
14
- Version: 1.3.1
15
- Author: PanGuoJun
16
- License: MIT
17
- """
18
-
19
- from .visphg import vis, image
20
- from .shader_render import ShaderRenderer, render_shader
21
- from .phg_to_shader import PHGToShaderConverter, phg_to_shader
22
- from .pipe_string_phg import (
23
- world_pipe_to_phg,
24
- local_pipe_to_phg,
25
- world_pipestr_vis,
26
- local_pipestr_vis,
27
- multiple_pipes_vis
28
- )
14
+ Version: 1.4.1
15
+ Author: PanGuoJun
16
+ License: MIT
17
+ """
18
+
19
+ import os
20
+ import tempfile
21
+
22
+ from .visphg import vis, image
23
+ from .shader_render import ShaderRenderer, render_shader
24
+ from .phg_to_shader import PHGToShaderConverter, phg_to_shader
25
+ from .pipe_string_phg import (
26
+ world_to_local_pipe_str,
27
+ local_pipe_to_phg,
28
+ world_pipe_to_phg,
29
+ world_pipestr_vis,
30
+ local_pipestr_vis,
31
+ multiple_pipes_vis
32
+ )
33
+ from .ai_bridge import (
34
+ compile_ai_phg,
35
+ vis_ai,
36
+ image_ai,
37
+ export_ai_obj,
38
+ web_view_ai,
39
+ )
40
+ from .web_three import write_three_view_html, serve_three_view
29
41
 
30
42
  # Extend ShaderRenderer class to support direct PHG rendering
31
43
  class PHGShaderRenderer(ShaderRenderer):
@@ -52,8 +64,8 @@ class PHGShaderRenderer(ShaderRenderer):
52
64
 
53
65
  return self.render_shader(shader_code, duration, interactive)
54
66
 
55
- # Convenience functions
56
- def render_phg(phg_script: str, width=800, height=600, duration=0, title="PHG Renderer"):
67
+ # Convenience functions
68
+ def render_phg(phg_script: str, width=800, height=600, duration=0, title="PHG Renderer"):
57
69
  """
58
70
  Directly render PHG script
59
71
 
@@ -79,7 +91,7 @@ def convert_phg_to_shader(phg_script: str) -> str:
79
91
  Returns:
80
92
  shader_code: GLSL shader code
81
93
  """
82
- return phg_to_shader(phg_script)
94
+ return phg_to_shader(phg_script)
83
95
 
84
96
  def visualize_pipe_variants(pipe_variants, start_position=None, colors=None, coordinate_system='world'):
85
97
  """
@@ -262,8 +274,8 @@ def generate_pipe_variants_from_rules(base_pipe, num_variants=5, apply_rules=Non
262
274
  return variants[:num_variants]
263
275
 
264
276
  # Enhanced pipe visualization with transformation support
265
- def visualize_pipe_with_transformations(pipe_string, start_position=None,
266
- transformations=None, show_variants=3):
277
+ def visualize_pipe_with_transformations(pipe_string, start_position=None,
278
+ transformations=None, show_variants=3):
267
279
  """
268
280
  Visualize a pipe string along with its transformed variants
269
281
 
@@ -284,23 +296,82 @@ def visualize_pipe_with_transformations(pipe_string, start_position=None,
284
296
  )
285
297
 
286
298
  # Create comparison visualization
287
- return visualize_pipe_variants(variants, start_position)
299
+ return visualize_pipe_variants(variants, start_position)
300
+
301
+
302
+ def web_view(script: str, out_html: str = "phg_view.html", out_obj: str = None,
303
+ ai_root: str = None, title: str = "PHG Web Viewer") -> str:
304
+ """
305
+ Export OBJ via AI parser and write a three.js HTML viewer.
306
+ """
307
+ return web_view_ai(script, out_html=out_html, out_obj=out_obj, ai_root=ai_root, title=title)
308
+
309
+
310
+ def web_serve(script: str, host: str = "127.0.0.1", port: int = 8766,
311
+ ai_root: str = None, title: str = "PHG Web Viewer") -> str:
312
+ """
313
+ Export OBJ via AI parser, write HTML, and serve with a local HTTP server.
314
+ """
315
+ temp_dir = tempfile.mkdtemp(prefix="phg_web_")
316
+ out_obj = os.path.join(temp_dir, "scene.obj")
317
+ out_html = os.path.join(temp_dir, "viewer.html")
318
+ web_view_ai(script, out_html=out_html, out_obj=out_obj, ai_root=ai_root, title=title)
319
+ return serve_three_view(out_obj, html_path=out_html, host=host, port=port, title=title)
320
+
321
+
322
+ def visualize(script: str, mode: str = "vis", ai: bool = False, **kwargs):
323
+ """
324
+ Unified visualization entrypoint.
325
+ mode: vis | image | shader | web
326
+ ai: use AI parser (DGE++) to normalize/expand before visualization
327
+ """
328
+ mode = (mode or "vis").lower()
329
+ ai_root = kwargs.pop("ai_root", None)
330
+
331
+ if mode in ("vis", "native", "exe"):
332
+ return vis_ai(script, ai_root=ai_root) if ai else vis(script)
333
+
334
+ if mode in ("image", "img", "png"):
335
+ filename = kwargs.pop("filename", "shot.png")
336
+ return image_ai(script, filename=filename, ai_root=ai_root) if ai else image(script, filename=filename)
337
+
338
+ if mode in ("shader", "ray", "rt"):
339
+ phg_script = compile_ai_phg(script, ai_root=ai_root, include_setup=True) if ai else script
340
+ return render_phg(phg_script, **kwargs)
341
+
342
+ if mode in ("web", "three"):
343
+ out_html = kwargs.pop("out_html", "phg_view.html")
344
+ out_obj = kwargs.pop("out_obj", None)
345
+ title = kwargs.pop("title", "PHG Web Viewer")
346
+ return web_view_ai(script, out_html=out_html, out_obj=out_obj, ai_root=ai_root, title=title)
347
+
348
+ raise ValueError(f"Unknown mode: {mode}")
288
349
 
289
- __all__ = [
290
- # Core visualization functions
291
- 'vis',
292
- 'image',
350
+ __all__ = [
351
+ # Core visualization functions
352
+ 'vis',
353
+ 'image',
354
+ 'visualize',
293
355
 
294
356
  # Shader rendering
295
357
  'ShaderRenderer',
296
358
  'render_shader',
297
359
  'render_phg',
298
- 'PHGShaderRenderer',
360
+ 'PHGShaderRenderer',
299
361
 
300
362
  # PHG conversion
301
363
  'PHGToShaderConverter',
302
- 'phg_to_shader',
303
- 'convert_phg_to_shader',
364
+ 'phg_to_shader',
365
+ 'convert_phg_to_shader',
366
+ 'compile_ai_phg',
367
+ 'vis_ai',
368
+ 'image_ai',
369
+ 'export_ai_obj',
370
+ 'web_view',
371
+ 'web_view_ai',
372
+ 'web_serve',
373
+ 'write_three_view_html',
374
+ 'serve_three_view',
304
375
 
305
376
  # Pipe visualization
306
377
  'world_pipe_to_phg',
@@ -313,17 +384,17 @@ __all__ = [
313
384
  'visualize_pipe_variants',
314
385
  'create_pipe_comparison_grid',
315
386
  'generate_pipe_variants_from_rules',
316
- 'visualize_pipe_with_transformations',
317
- ]
318
-
319
- __version__ = "1.3.1"
320
- __author__ = "PanGuoJun"
321
- __description__ = "Python Hypergraphics Library"
322
-
323
- # Package initialization information
324
- print(f"PHG {__version__} - {__description__}")
325
- print("[OK] Visualization module loaded")
326
- print("[OK] Shader converter loaded")
327
- print("[OK] Pipe visualization loaded")
328
- print("[OK] Multiple pipe visualization support loaded")
329
- print("[OK] Pipe transformation utilities loaded")
387
+ 'visualize_pipe_with_transformations',
388
+ ]
389
+
390
+ __version__ = "1.4.1"
391
+ __author__ = "PanGuoJun"
392
+ __description__ = "Python Hypergraphics Library"
393
+
394
+ if os.environ.get("PHG_VERBOSE"):
395
+ print(f"PHG {__version__} - {__description__}")
396
+ print("[OK] Visualization module loaded")
397
+ print("[OK] Shader converter loaded")
398
+ print("[OK] Pipe visualization loaded")
399
+ print("[OK] Multiple pipe visualization support loaded")
400
+ print("[OK] Pipe transformation utilities loaded")
phg/ai_bridge.py ADDED
@@ -0,0 +1,218 @@
1
+ """
2
+ AI bridge for PHG (DGE++ parser -> normalized PHG -> vis.exe / web).
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import importlib
8
+ import importlib.util
9
+ import json
10
+ import os
11
+ import subprocess
12
+ import sys
13
+ import tempfile
14
+ from typing import Any, Dict, Iterable, List, Optional
15
+
16
+ from .visphg import vis as _vis, image as _image
17
+ from .web_three import write_three_view_html
18
+
19
+ DEFAULT_AI_ROOT = r"C:\Users\18858\Documents\_AILab\DGE++\PHG"
20
+ _AI_MODULE = None
21
+ _AI_MODULE_ROOT = None
22
+
23
+
24
+ def resolve_ai_root(ai_root: Optional[str] = None) -> str:
25
+ root = ai_root or os.environ.get("PHG_AI_ROOT") or DEFAULT_AI_ROOT
26
+ if not os.path.isdir(root):
27
+ raise FileNotFoundError(
28
+ f"AI PHG root not found: {root}. "
29
+ "Set PHG_AI_ROOT or pass ai_root explicitly."
30
+ )
31
+ return root
32
+
33
+
34
+ def _run_ai_cli(args: List[str], ai_root: Optional[str] = None, timeout: int = 120) -> subprocess.CompletedProcess:
35
+ root = resolve_ai_root(ai_root)
36
+ cmd = [sys.executable, "-m", "phg"] + args
37
+ return subprocess.run(
38
+ cmd,
39
+ cwd=root,
40
+ capture_output=True,
41
+ text=True,
42
+ timeout=timeout,
43
+ check=False,
44
+ )
45
+
46
+
47
+ def _load_ai_module(ai_root: Optional[str] = None):
48
+ global _AI_MODULE, _AI_MODULE_ROOT
49
+ root = resolve_ai_root(ai_root)
50
+ if _AI_MODULE is not None and _AI_MODULE_ROOT == root:
51
+ return _AI_MODULE
52
+
53
+ pkg_dir = os.path.join(root, "phg")
54
+ init_path = os.path.join(pkg_dir, "__init__.py")
55
+ if not os.path.isfile(init_path):
56
+ raise FileNotFoundError(f"AI PHG package not found: {init_path}")
57
+
58
+ spec = importlib.util.spec_from_file_location(
59
+ "phg_ai",
60
+ init_path,
61
+ submodule_search_locations=[pkg_dir],
62
+ )
63
+ if spec is None or spec.loader is None:
64
+ raise ImportError("Failed to load AI PHG package")
65
+
66
+ module = importlib.util.module_from_spec(spec)
67
+ sys.modules["phg_ai"] = module
68
+ spec.loader.exec_module(module)
69
+ _AI_MODULE = module
70
+ _AI_MODULE_ROOT = root
71
+ return module
72
+
73
+
74
+ def _write_temp_phg(script: str) -> str:
75
+ fd, path = tempfile.mkstemp(suffix=".phg")
76
+ os.close(fd)
77
+ with open(path, "w", encoding="utf-8") as f:
78
+ f.write(script)
79
+ return path
80
+
81
+
82
+ def dump_scene(script: str, ai_root: Optional[str] = None) -> Dict[str, Any]:
83
+ """
84
+ Use AI PHG (DGE++) to parse script and dump scene JSON.
85
+ """
86
+ phg_path = _write_temp_phg(script)
87
+ fd, json_path = tempfile.mkstemp(suffix=".json")
88
+ os.close(fd)
89
+ try:
90
+ try:
91
+ ai = _load_ai_module(ai_root)
92
+ engine_mod = importlib.import_module("phg_ai.engine")
93
+ engine = engine_mod.Engine()
94
+ engine.run(script)
95
+ if "setup" not in engine.calls:
96
+ engine.setup()
97
+ if "draw" not in engine.calls:
98
+ engine.draw()
99
+ return engine._scene_to_dict(engine.scene.root)
100
+ except Exception:
101
+ proc = _run_ai_cli(["dump", phg_path, "--out", json_path], ai_root=ai_root)
102
+ if proc.returncode != 0:
103
+ raise RuntimeError(proc.stderr.strip() or proc.stdout.strip() or "AI dump failed")
104
+ with open(json_path, "r", encoding="utf-8") as f:
105
+ return json.load(f)
106
+ finally:
107
+ for p in (phg_path, json_path):
108
+ try:
109
+ os.remove(p)
110
+ except OSError:
111
+ pass
112
+
113
+
114
+ def _format_value(value: Any) -> str:
115
+ if value is None:
116
+ return ""
117
+ if isinstance(value, bool):
118
+ return "1" if value else "0"
119
+ if isinstance(value, (int, float)):
120
+ return str(value)
121
+ if isinstance(value, (list, tuple)):
122
+ return ",".join(str(v) for v in value)
123
+ return str(value)
124
+
125
+
126
+ def _emit_props(props: Dict[str, Any], indent: str) -> List[str]:
127
+ lines = []
128
+ for key, value in props.items():
129
+ val = _format_value(value)
130
+ if val == "":
131
+ lines.append(f"{indent}{key};")
132
+ else:
133
+ lines.append(f"{indent}{key}:{val};")
134
+ return lines
135
+
136
+
137
+ def _emit_node(node: Dict[str, Any], indent: str = " ") -> List[str]:
138
+ name = node.get("name", "node")
139
+ props = node.get("props", {}) or {}
140
+ children = node.get("children", []) or []
141
+
142
+ lines = [f"{indent}{name}{{"]
143
+ lines.extend(_emit_props(props, indent + " "))
144
+ for child in children:
145
+ lines.extend(_emit_node(child, indent + " "))
146
+ lines.append(f"{indent}}}")
147
+ return lines
148
+
149
+
150
+ def scene_to_phg(scene: Dict[str, Any], include_setup: bool = True) -> str:
151
+ """
152
+ Convert AI scene JSON (dump) to normalized PHG script.
153
+ """
154
+ root_props = scene.get("props", {}) or {}
155
+ root_children = scene.get("children", []) or []
156
+
157
+ lines = ["{"]
158
+ if root_props:
159
+ # Preserve root-level props by creating a synthetic node.
160
+ root_node = {
161
+ "name": scene.get("name", "root"),
162
+ "props": root_props,
163
+ "children": root_children,
164
+ }
165
+ lines.extend(_emit_node(root_node, indent=" "))
166
+ else:
167
+ for child in root_children:
168
+ lines.extend(_emit_node(child, indent=" "))
169
+ lines.append("}")
170
+ if include_setup:
171
+ lines.append("setup_draw();")
172
+ return "\n".join(lines)
173
+
174
+
175
+ def compile_ai_phg(script: str, ai_root: Optional[str] = None, include_setup: bool = True) -> str:
176
+ scene = dump_scene(script, ai_root=ai_root)
177
+ return scene_to_phg(scene, include_setup=include_setup)
178
+
179
+
180
+ def vis_ai(script: str, ai_root: Optional[str] = None) -> None:
181
+ phg_script = compile_ai_phg(script, ai_root=ai_root, include_setup=True)
182
+ _vis(phg_script)
183
+
184
+
185
+ def image_ai(script: str, filename: str = "shot.png", ai_root: Optional[str] = None) -> None:
186
+ phg_script = compile_ai_phg(script, ai_root=ai_root, include_setup=True)
187
+ _image(phg_script, filename=filename)
188
+
189
+
190
+ def export_ai_obj(script: str, out_obj: str, ai_root: Optional[str] = None) -> str:
191
+ try:
192
+ ai = _load_ai_module(ai_root)
193
+ engine_mod = importlib.import_module("phg_ai.engine")
194
+ engine = engine_mod.Engine()
195
+ engine.run(script)
196
+ if "setup" not in engine.calls:
197
+ engine.setup()
198
+ if "draw" not in engine.calls:
199
+ engine.draw()
200
+ engine.export_obj(out_obj)
201
+ finally:
202
+ pass
203
+ return out_obj
204
+
205
+
206
+ def web_view_ai(
207
+ script: str,
208
+ out_html: str,
209
+ out_obj: Optional[str] = None,
210
+ ai_root: Optional[str] = None,
211
+ title: str = "PHG Web Viewer",
212
+ ) -> str:
213
+ if out_obj is None:
214
+ base, _ = os.path.splitext(out_html)
215
+ out_obj = base + ".obj"
216
+ export_ai_obj(script, out_obj, ai_root=ai_root)
217
+ write_three_view_html(out_obj, out_html, title=title)
218
+ return out_html