phg-vis 1.3.1__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/shader_render.py CHANGED
@@ -1,9 +1,18 @@
1
- import pygame
2
- import numpy as np
3
- from OpenGL.GL import *
4
- from OpenGL.GL.shaders import compileProgram, compileShader
5
- import sys
6
- import time
1
+ import sys
2
+ import time
3
+
4
+ try:
5
+ import pygame
6
+ import numpy as np
7
+ from OpenGL.GL import * # noqa: F401,F403 - OpenGL symbols used dynamically
8
+ from OpenGL.GL.shaders import compileProgram, compileShader
9
+ _IMPORT_ERROR = None
10
+ except Exception as exc: # pragma: no cover - optional dependency
11
+ pygame = None
12
+ np = None
13
+ compileProgram = None
14
+ compileShader = None
15
+ _IMPORT_ERROR = exc
7
16
 
8
17
  class Camera:
9
18
  """Camera class with mouse interaction controls"""
@@ -93,7 +102,7 @@ def normalize(v):
93
102
  return v
94
103
  return v / norm
95
104
 
96
- class ShaderRenderer:
105
+ class ShaderRenderer:
97
106
  """
98
107
  Shader renderer class for visualizing GLSL shaders
99
108
 
@@ -102,15 +111,20 @@ class ShaderRenderer:
102
111
  renderer.render_shader(fragment_shader_code)
103
112
  """
104
113
 
105
- def __init__(self, width=800, height=600, title="Shader Renderer"):
106
- """
107
- Initialize Shader renderer
114
+ def __init__(self, width=800, height=600, title="Shader Renderer"):
115
+ """
116
+ Initialize Shader renderer
108
117
 
109
118
  Args:
110
119
  width: Window width
111
120
  height: Window height
112
121
  title: Window title
113
- """
122
+ """
123
+ if _IMPORT_ERROR is not None:
124
+ raise ImportError(
125
+ "ShaderRenderer requires pygame, numpy, and PyOpenGL. "
126
+ "Install with: pip install phg_vis[shader]"
127
+ ) from _IMPORT_ERROR
114
128
  self.width = width
115
129
  self.height = height
116
130
  self.title = title
@@ -422,4 +436,4 @@ def render_shader(fragment_shader, width=800, height=600, duration=0, title="Sha
422
436
  renderer = ShaderRenderer(width, height, title)
423
437
  success = renderer.render_shader(fragment_shader, duration)
424
438
  renderer.close()
425
- return success
439
+ return success
phg/visphg.py CHANGED
@@ -1,55 +1,79 @@
1
- #################################
2
- # Launch vis.exe
3
- #################################
4
- import http.client
5
- import subprocess
6
- import os
7
- import psutil # Make sure to import psutil
8
-
9
- # Set the path to vis.exe in the current directory of the current script
10
- current_directory = os.path.dirname(os.path.abspath(__file__))
11
- vis_exe_path = os.path.join(current_directory, "vis", "vis.exe")
12
- print(vis_exe_path)
13
- # Check if vis.exe is running
14
- def is_vis_running():
15
- for proc in psutil.process_iter(['name']):
16
- if proc.info['name'] == 'vis.exe':
17
- return True
18
- return False
19
-
20
- # Send network message
21
- def HTTPPOST(ip, phg):
22
- headers = {
23
- "Connection": "keep-alive",
24
- "Content-Type": "text/plain"
25
- }
26
- conn = http.client.HTTPConnection(f"{ip}:5088")
27
- conn.request('POST', '/phg', phg, headers)
28
- res = conn.getresponse()
29
- conn.close()
30
-
31
- # Call HTTPPOST function to send network message
32
- def vis(phg):
33
- if not is_vis_running(): # Check if the process is running
34
- subprocess.Popen(vis_exe_path) # Start the process
35
- return HTTPPOST('127.0.0.1', phg.encode())
36
-
37
- # Send network message
38
- def HTTPPOST_IMG(ip, phg, filename='shot.png'):
39
- headers = {
40
- "Connection": "keep-alive",
41
- "Content-Type": "text/plain"
42
- }
43
- # 构建完整的请求体: phg代码 ||| 渲染参数(包含文件名)
44
- request_body = f"{phg}|||file={filename}"
45
-
46
- conn = http.client.HTTPConnection(f"{ip}:5088")
47
- conn.request('POST', '/phg_img', request_body, headers)
48
- res = conn.getresponse()
49
- conn.close()
50
-
51
- # Call HTTPPOST function to send network message
52
- def image(phg, filename='shot.png'):
53
- if not is_vis_running(): # Check if the process is running
54
- subprocess.Popen(vis_exe_path) # Start the process
55
- return HTTPPOST_IMG('127.0.0.1', phg.encode('utf-8').decode('utf-8'), filename)
1
+ #################################
2
+ # Launch vis.exe
3
+ #################################
4
+ import http.client
5
+ import os
6
+ import re
7
+ import subprocess
8
+
9
+ try:
10
+ import psutil
11
+ except Exception: # pragma: no cover - optional dependency
12
+ psutil = None
13
+
14
+ # Set the path to vis.exe in the current directory of the current script
15
+ current_directory = os.path.dirname(os.path.abspath(__file__))
16
+ vis_exe_path = os.path.join(current_directory, "vis", "vis.exe")
17
+
18
+
19
+ # Check if vis.exe is running
20
+ def is_vis_running():
21
+ if psutil is None:
22
+ return False
23
+ for proc in psutil.process_iter(["name"]):
24
+ if proc.info.get("name") == "vis.exe":
25
+ return True
26
+ return False
27
+
28
+
29
+ # Send network message
30
+ def HTTPPOST(ip, phg):
31
+ headers = {
32
+ "Connection": "keep-alive",
33
+ "Content-Type": "text/plain",
34
+ }
35
+ conn = http.client.HTTPConnection(f"{ip}:5088")
36
+ conn.request("POST", "/phg", phg, headers)
37
+ conn.getresponse()
38
+ conn.close()
39
+
40
+
41
+ # Call HTTPPOST function to send network message
42
+ def vis(phg):
43
+ if not is_vis_running():
44
+ subprocess.Popen(vis_exe_path)
45
+ return HTTPPOST("127.0.0.1", phg.encode())
46
+
47
+
48
+ # Build image request payload with parameters
49
+ def _build_img_request(phg, filename):
50
+ if "|||" not in phg:
51
+ return f"{phg}|||file={filename}"
52
+ code, params = phg.split("|||", 1)
53
+ params = params.strip()
54
+ if "file=" in params:
55
+ params = re.sub(r"file=\\S+", f"file={filename}", params)
56
+ else:
57
+ params = f"{params} file={filename}".strip()
58
+ return f"{code}|||{params}"
59
+
60
+
61
+ # Send image request
62
+ def HTTPPOST_IMG(ip, phg, filename="shot.png"):
63
+ headers = {
64
+ "Connection": "keep-alive",
65
+ "Content-Type": "text/plain",
66
+ }
67
+ request_body = _build_img_request(phg, filename)
68
+
69
+ conn = http.client.HTTPConnection(f"{ip}:5088")
70
+ conn.request("POST", "/phg_img", request_body, headers)
71
+ conn.getresponse()
72
+ conn.close()
73
+
74
+
75
+ # Call HTTPPOST function to send network message
76
+ def image(phg, filename="shot.png"):
77
+ if not is_vis_running():
78
+ subprocess.Popen(vis_exe_path)
79
+ return HTTPPOST_IMG("127.0.0.1", phg.encode("utf-8").decode("utf-8"), filename)
phg/web_three.py ADDED
@@ -0,0 +1,164 @@
1
+ """
2
+ Three.js web viewer for PHG OBJ exports.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import html
8
+ import os
9
+ from functools import partial
10
+ from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
11
+ from typing import Optional
12
+
13
+
14
+ def _relpath_for_url(path: str, base_dir: str) -> str:
15
+ rel = os.path.relpath(os.path.abspath(path), base_dir)
16
+ return rel.replace("\\", "/")
17
+
18
+
19
+ def write_three_view_html(obj_path: str, out_html: str, title: str = "PHG Web Viewer") -> str:
20
+ base_dir = os.path.dirname(os.path.abspath(out_html))
21
+ rel_obj = _relpath_for_url(obj_path, base_dir)
22
+
23
+ doc = f"""<!DOCTYPE html>
24
+ <html>
25
+ <head>
26
+ <meta charset="utf-8" />
27
+ <title>{html.escape(title)}</title>
28
+ <style>
29
+ :root {{
30
+ --bg: #f6f3ee;
31
+ --panel: #ffffff;
32
+ --text: #1d1d1f;
33
+ --line: #dcd7cf;
34
+ --accent: #1b5e6b;
35
+ }}
36
+ * {{ box-sizing: border-box; }}
37
+ body {{
38
+ margin: 0;
39
+ background: var(--bg);
40
+ color: var(--text);
41
+ font-family: "Segoe UI", "Helvetica Neue", Arial, sans-serif;
42
+ }}
43
+ #app {{
44
+ position: fixed;
45
+ inset: 0;
46
+ }}
47
+ canvas {{ display: block; }}
48
+ .hud {{
49
+ position: absolute;
50
+ top: 16px;
51
+ left: 16px;
52
+ background: rgba(255,255,255,0.9);
53
+ border: 1px solid var(--line);
54
+ border-radius: 10px;
55
+ padding: 10px 12px;
56
+ box-shadow: 0 6px 18px rgba(0,0,0,0.08);
57
+ font-size: 12px;
58
+ line-height: 1.5;
59
+ }}
60
+ .hud strong {{ color: var(--accent); }}
61
+ </style>
62
+ </head>
63
+ <body>
64
+ <div id="app"></div>
65
+ <div class="hud">
66
+ <div><strong>PHG Web Viewer</strong></div>
67
+ <div>OBJ: {html.escape(os.path.basename(obj_path))}</div>
68
+ <div>Controls: LMB rotate, RMB pan, wheel zoom</div>
69
+ </div>
70
+ <script type="module">
71
+ import * as THREE from "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js";
72
+ import {{ OrbitControls }} from "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/controls/OrbitControls.js";
73
+ import {{ OBJLoader }} from "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/loaders/OBJLoader.js";
74
+
75
+ const container = document.getElementById("app");
76
+ const scene = new THREE.Scene();
77
+ scene.background = new THREE.Color(0xf6f3ee);
78
+
79
+ const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 2000);
80
+ camera.position.set(6, 6, 10);
81
+
82
+ const renderer = new THREE.WebGLRenderer({{ antialias: true }});
83
+ renderer.setPixelRatio(window.devicePixelRatio || 1);
84
+ renderer.setSize(window.innerWidth, window.innerHeight);
85
+ container.appendChild(renderer.domElement);
86
+
87
+ const controls = new OrbitControls(camera, renderer.domElement);
88
+ controls.enableDamping = true;
89
+ controls.dampingFactor = 0.08;
90
+
91
+ const lightA = new THREE.DirectionalLight(0xffffff, 0.9);
92
+ lightA.position.set(6, 10, 8);
93
+ scene.add(lightA);
94
+ scene.add(new THREE.AmbientLight(0xffffff, 0.35));
95
+
96
+ const loader = new OBJLoader();
97
+ loader.load("{html.escape(rel_obj)}", (obj) => {{
98
+ obj.traverse((child) => {{
99
+ if (child.isMesh) {{
100
+ child.material = new THREE.MeshStandardMaterial({{
101
+ color: 0x9fb3c8,
102
+ roughness: 0.55,
103
+ metalness: 0.05
104
+ }});
105
+ }}
106
+ }});
107
+ scene.add(obj);
108
+
109
+ const box = new THREE.Box3().setFromObject(obj);
110
+ const size = box.getSize(new THREE.Vector3());
111
+ const center = box.getCenter(new THREE.Vector3());
112
+ const maxDim = Math.max(size.x, size.y, size.z) || 1;
113
+ const camDist = maxDim * 2.2;
114
+ camera.position.set(center.x + camDist, center.y + camDist, center.z + camDist);
115
+ controls.target.copy(center);
116
+ controls.update();
117
+ }});
118
+
119
+ function onResize() {{
120
+ camera.aspect = window.innerWidth / window.innerHeight;
121
+ camera.updateProjectionMatrix();
122
+ renderer.setSize(window.innerWidth, window.innerHeight);
123
+ }}
124
+ window.addEventListener("resize", onResize);
125
+
126
+ function animate() {{
127
+ requestAnimationFrame(animate);
128
+ controls.update();
129
+ renderer.render(scene, camera);
130
+ }}
131
+ animate();
132
+ </script>
133
+ </body>
134
+ </html>
135
+ """
136
+ with open(out_html, "w", encoding="utf-8") as f:
137
+ f.write(doc)
138
+ return out_html
139
+
140
+
141
+ def serve_three_view(
142
+ obj_path: str,
143
+ html_path: Optional[str] = None,
144
+ host: str = "127.0.0.1",
145
+ port: int = 8766,
146
+ title: str = "PHG Web Viewer",
147
+ ) -> str:
148
+ if html_path is None:
149
+ base, _ = os.path.splitext(obj_path)
150
+ html_path = base + ".html"
151
+ write_three_view_html(obj_path, html_path, title=title)
152
+
153
+ directory = os.path.dirname(os.path.abspath(html_path)) or os.getcwd()
154
+ handler = partial(SimpleHTTPRequestHandler, directory=directory)
155
+ server = ThreadingHTTPServer((host, port), handler)
156
+ url = f"http://{host}:{port}/{os.path.basename(html_path)}"
157
+ print(url)
158
+ try:
159
+ server.serve_forever()
160
+ except KeyboardInterrupt:
161
+ pass
162
+ finally:
163
+ server.server_close()
164
+ return url
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: phg_vis
3
- Version: 1.3.1
3
+ Version: 1.4.0
4
4
  Summary: A package for the PHG modeling language and 3D visualization tool.
5
5
  Home-page: https://github.com/panguojun/Coordinate-System
6
6
  Author: romeosoft
@@ -12,6 +12,11 @@ Classifier: Operating System :: Microsoft :: Windows
12
12
  Requires-Python: >=3.8
13
13
  Description-Content-Type: text/markdown
14
14
  License-File: LICENSE
15
+ Requires-Dist: psutil
16
+ Provides-Extra: shader
17
+ Requires-Dist: numpy; extra == "shader"
18
+ Requires-Dist: pygame; extra == "shader"
19
+ Requires-Dist: PyOpenGL; extra == "shader"
15
20
 
16
21
  # PHG - Python Hypergraphics Library
17
22
 
@@ -27,6 +32,8 @@ PHG (Python Hypergraphics) is a powerful 3D modeling and visualization library t
27
32
  - **Pipe Visualization** - Specialized system for visualizing pipe structures with world/local coordinate support
28
33
  - **Shader Conversion** - Convert PHG scripts to GLSL shaders for GPU rendering
29
34
  - **Dual Rendering Modes** - Both real-time visualization (vis.exe) and offscreen rendering
35
+ - **AI Parser Bridge** - Use DGE++ PHG parser to normalize/enhance scripts before visualization
36
+ - **Web Viewer** - Export OBJ and view in a three.js browser viewer
30
37
  - **Python Integration** - Comprehensive Python API for all features
31
38
 
32
39
  ---
@@ -42,6 +49,11 @@ Or install from source:
42
49
  pip install phg
43
50
  ```
44
51
 
52
+ Shader renderer dependencies (optional):
53
+ ```bash
54
+ pip install phg_vis[shader]
55
+ ```
56
+
45
57
  ---
46
58
 
47
59
  ## 🚀 Quick Start
@@ -86,6 +98,49 @@ phg.image(scene, filename="tower.png")
86
98
  phg.image("sphere(5);|||view=2 width=1920 height=1080", filename="top_view.png")
87
99
  ```
88
100
 
101
+ ---
102
+
103
+ ## ✨ Unified Visualization API
104
+
105
+ ```python
106
+ import phg
107
+
108
+ # Traditional vis.exe
109
+ phg.visualize("box(10);", mode="vis")
110
+
111
+ # Shader ray-march preview (GLSL)
112
+ phg.visualize("sphere(5);", mode="shader", width=800, height=600)
113
+
114
+ # Web (three.js) viewer
115
+ phg.visualize("box(10);", mode="web", out_html="view.html")
116
+ ```
117
+
118
+ ### AI Parser Bridge (for improved PHG)
119
+ Use DGE++ PHG parser to normalize/enhance scripts before visualization:
120
+
121
+ ```python
122
+ import phg
123
+
124
+ phg.visualize(script, mode="vis", ai=True)
125
+ phg.visualize(script, mode="shader", ai=True)
126
+ phg.visualize(script, mode="web", ai=True, out_html="ai_view.html")
127
+ ```
128
+
129
+ Environment override (optional):
130
+ ```
131
+ set PHG_AI_ROOT=C:\Users\18858\Documents\_AILab\DGE++\PHG
132
+ ```
133
+
134
+ Web live server (serves HTML + OBJ):
135
+ ```python
136
+ phg.web_serve(script, host="127.0.0.1", port=8766, ai_root=None)
137
+ ```
138
+
139
+ Notes for web viewer:
140
+ - Uses three.js CDN (requires network access)
141
+ - Uses AI bridge to export OBJ; ensure DGE++ is available
142
+ - Use `web_serve` to avoid file:// loader restrictions
143
+
89
144
  ---
90
145
 
91
146
  ## 📖 Syntax Reference
@@ -1,8 +1,11 @@
1
- phg/__init__.py,sha256=TvWMc1ho6g9enlj0nhtr5eidViu6xyt0CrUOW31Q9F4,3097
1
+ phg/__init__.py,sha256=PcG3uC0eL2vY7L4cpNqE-e6Q1AjUbT4kn-18eBSNhZg,13241
2
+ phg/ai_bridge.py,sha256=ZH4X-Sa8C3cbgeI5chi6Valsm0G1B__NrWMjy1YVbM8,5283
3
+ phg/gcu_api.py,sha256=yjCWAJPyIAL_fAdj2umX39wNmw56-LmkcmUPqt3V8DI,22957
2
4
  phg/phg_to_shader.py,sha256=rb_LnGE1Py08oz72ES5CnjyCDbjVWDU3If0cYZq4SUU,15172
3
- phg/pipe_string_phg.py,sha256=FPFatph2WCVoUzEflIJWRotwH0Yb79VJaKvmyaj4zDI,10654
4
- phg/shader_render.py,sha256=_T8IFp1h5Hcj8TMMHwFMbxhGdHLnOqka7pJ4pde7zMA,16780
5
- phg/visphg.py,sha256=CtALoNJs0eDT8wnXk_76XG4UsOcV6cWucvWqz7AhOSc,1923
5
+ phg/pipe_string_phg.py,sha256=9YW5wewLUmyqGUnYNBYpCyk_SMS6fBfyPWTnniy6xdY,16589
6
+ phg/shader_render.py,sha256=ncQQHp5OMlOVeTsoIHOL2xnc6SQct7B8YRKPA-D9FlM,17278
7
+ phg/visphg.py,sha256=TpT6icBoBmPCbCm212qKC9zft9MQ2UK2dWvmgnlupwQ,2163
8
+ phg/web_three.py,sha256=GPftXoSUBfAMHfEAn4qapmJ1tsnfoxtKu2JiJwOBqAg,4979
6
9
  phg/vis/GCU.dll,sha256=k_slWZ5nOzZNi9zCe0_SKc0F7zXoLyRuRDomyirPY4I,2687488
7
10
  phg/vis/dragpad.exe,sha256=4qWG4VH1vginIGpKkBHwEBFlpTY8G6xPejeloJurNkE,51712
8
11
  phg/vis/dragpad_config.ini,sha256=MD5no-RjzzcBxyz3JWuI88qH5Q4WVivRLqk_1GE5VQY,78
@@ -15,8 +18,8 @@ phg/vis/vis.exe,sha256=9LGgETiO4lwhz_gJzdUg6gPP9EwGLT5SuLjN3wR2sso,1388032
15
18
  phg/vis/vis.ini,sha256=IjS0Av2BqresPR2oGCOoq5zliXN2wFRMK3rXcU15vKs,161
16
19
  phg/vis/zlib1.dll,sha256=oVw_r326ve3oveVZhlSiF8BloZQ6lfqXJEAhUYfMjOQ,89088
17
20
  phg/vis/imgui/main.lua,sha256=AIB5dpGrPHXuRiNW7Bg7eu5Lpi2DDcn6n4JS7OS1Hdk,4742
18
- phg_vis-1.3.1.dist-info/LICENSE,sha256=tDnRkJxBYPzWdfh2gArRqrUPJxQZRZHJVs68qqBHIq4,1083
19
- phg_vis-1.3.1.dist-info/METADATA,sha256=rJB_6RA15BncZfzq2BPKCgI02cesmNrB67eIzPtdpbM,17945
20
- phg_vis-1.3.1.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
21
- phg_vis-1.3.1.dist-info/top_level.txt,sha256=5GGhpTP8yi-zTXwW2HrI-zcxKxIM_nHXhL7r0iIDE_k,4
22
- phg_vis-1.3.1.dist-info/RECORD,,
21
+ phg_vis-1.4.0.dist-info/LICENSE,sha256=tDnRkJxBYPzWdfh2gArRqrUPJxQZRZHJVs68qqBHIq4,1083
22
+ phg_vis-1.4.0.dist-info/METADATA,sha256=wHVu7Q42AzQ_jcuvhjPYHE8EaJkmIqQIRX9hBJFSUbA,19424
23
+ phg_vis-1.4.0.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
24
+ phg_vis-1.4.0.dist-info/top_level.txt,sha256=5GGhpTP8yi-zTXwW2HrI-zcxKxIM_nHXhL7r0iIDE_k,4
25
+ phg_vis-1.4.0.dist-info/RECORD,,