10from contextlib
import contextmanager
12from pathlib
import Path
13from typing
import List
15from .wrappers
import UWeberPennTreeWrapper
as wpt_wrapper
16from .wrappers.DataTypes
import vec3
17from .plugins.registry
import get_plugin_registry, graceful_plugin_fallback
18from .validation.plugins
import validate_wpt_parameters
19from .validation.datatypes
import validate_vec3
20from .validation.core
import validate_positive_value
21from .assets
import get_asset_manager
22from .validation.plugin_decorators
import (
23 validate_tree_uuid_params, validate_recursion_params, validate_trunk_segment_params,
24 validate_branch_segment_params, validate_leaf_subdivisions_params
30 Check if WeberPennTree plugin is available for use.
33 bool: True if WeberPennTree can be used, False otherwise
37 plugin_registry = get_plugin_registry()
38 if not plugin_registry.is_plugin_available(
'weberpenntree'):
42 if not wpt_wrapper._WPT_FUNCTIONS_AVAILABLE:
49from .Context
import Context
54 Context manager that temporarily changes working directory to where WeberPennTree assets are located.
56 WeberPennTree C++ code uses hardcoded relative paths like "plugins/weberpenntree/xml/WeberPennTreeLibrary.xml"
57 expecting assets relative to working directory. This manager temporarily changes to the build directory
58 where assets are actually located.
61 RuntimeError: If build directory or WeberPennTree assets are not found, indicating a build system error.
65 asset_manager = get_asset_manager()
66 working_dir = asset_manager._get_helios_build_path()
68 if working_dir
and working_dir.exists():
69 weberpenntree_assets = working_dir /
'plugins' /
'weberpenntree'
72 current_dir = Path(__file__).parent
73 packaged_build = current_dir /
'assets' /
'build'
75 if packaged_build.exists():
76 working_dir = packaged_build
77 weberpenntree_assets = working_dir /
'plugins' /
'weberpenntree'
80 repo_root = current_dir.parent
81 build_lib_dir = repo_root /
'pyhelios_build' /
'build' /
'lib'
82 working_dir = build_lib_dir.parent
83 weberpenntree_assets = working_dir /
'plugins' /
'weberpenntree'
85 if not build_lib_dir.exists():
87 f
"PyHelios build directory not found: {build_lib_dir}. "
88 f
"WeberPennTree requires native libraries to be built. "
89 f
"Run: python build_scripts/build_helios.py --plugins weberpenntree"
92 if not weberpenntree_assets.exists():
94 f
"WeberPennTree assets not found: {weberpenntree_assets}. "
95 f
"Build system failed to copy WeberPennTree assets. "
96 f
"Run: python build_scripts/build_helios.py --clean --plugins weberpenntree"
99 xml_file = weberpenntree_assets /
'xml' /
'WeberPennTreeLibrary.xml'
100 if not xml_file.exists():
102 f
"WeberPennTree XML library not found: {xml_file}. "
103 f
"Critical WeberPennTree asset missing from build. "
104 f
"Run: python build_scripts/build_helios.py --clean --plugins weberpenntree"
108 original_cwd = Path.cwd()
110 os.chdir(working_dir)
113 os.chdir(original_cwd)
123 PISTACHIO =
'Pistachio'
134 from .wrappers.UWeberPennTreeWrapper
import _WPT_FUNCTIONS_AVAILABLE
135 if not _WPT_FUNCTIONS_AVAILABLE:
136 raise NotImplementedError(
"WeberPennTree functions not available in current Helios library.")
140 print(
"Warning: WeberPennTree plugin not detected in current build")
141 print(
"Tree generation functionality may be limited or unavailable")
144 asset_manager = get_asset_manager()
145 build_dir = asset_manager._get_helios_build_path()
147 if not build_dir
or not build_dir.exists():
149 current_dir = Path(__file__).parent
150 packaged_build = current_dir /
'assets' /
'build'
152 if packaged_build.exists()
and (packaged_build /
'plugins' /
'weberpenntree').exists():
153 build_dir = packaged_build
156 repo_root = current_dir.parent
157 build_lib_dir = repo_root /
'pyhelios_build' /
'build' /
'lib'
158 build_dir = build_lib_dir.parent
160 if not build_dir.exists():
162 f
"PyHelios build directory not found: {build_dir}. "
163 f
"WeberPennTree requires native libraries to be built. "
164 f
"Run: python build_scripts/build_helios.py --plugins weberpenntree"
169 original_cwd = Path.cwd()
172 self.
wpt = wpt_wrapper.createWeberPennTreeWithBuildPluginRootDirectory(
173 context.getNativePtr(), str(build_dir)
176 os.chdir(original_cwd)
181 def __exit__(self, exc_type, exc_value, traceback):
182 wpt_wrapper.destroyWeberPennTree(self.
wpt)
188 def buildTree(self, wpt_type:WPTType, origin:vec3=
vec3(0, 0, 0), scale:float=1) -> int:
190 validate_vec3(origin,
"origin",
"buildTree")
191 validate_positive_value(scale,
"scale",
"buildTree")
193 if not self.
wpt or not isinstance(self.
wpt, ctypes._Pointer):
195 f
"WeberPennTree is not properly initialized. "
196 f
"This may indicate that the weberpenntree plugin is not available. "
197 f
"Check plugin status with context.print_plugin_status()"
204 return wpt_wrapper.buildTreeWithScale(self.
wpt, wpt_type.value, origin.to_list(), scale)
206 return wpt_wrapper.buildTree(self.
wpt, wpt_type.value, origin.to_list())
208 @validate_tree_uuid_params
210 if not self.
wpt or not isinstance(self.
wpt, ctypes._Pointer):
211 raise RuntimeError(
"WeberPennTree is not properly initialized. Check plugin availability.")
212 return wpt_wrapper.getTrunkUUIDs(self.
wpt, tree_id)
214 @validate_tree_uuid_params
216 if not self.
wpt or not isinstance(self.
wpt, ctypes._Pointer):
217 raise RuntimeError(
"WeberPennTree is not properly initialized. Check plugin availability.")
218 return wpt_wrapper.getBranchUUIDs(self.
wpt, tree_id)
220 @validate_tree_uuid_params
222 if not self.
wpt or not isinstance(self.
wpt, ctypes._Pointer):
223 raise RuntimeError(
"WeberPennTree is not properly initialized. Check plugin availability.")
224 return wpt_wrapper.getLeafUUIDs(self.
wpt, tree_id)
226 @validate_tree_uuid_params
228 if not self.
wpt or not isinstance(self.
wpt, ctypes._Pointer):
229 raise RuntimeError(
"WeberPennTree is not properly initialized. Check plugin availability.")
230 return wpt_wrapper.getAllUUIDs(self.
wpt, tree_id)
232 @validate_recursion_params
234 if not self.
wpt or not isinstance(self.
wpt, ctypes._Pointer):
235 raise RuntimeError(
"WeberPennTree is not properly initialized. Check plugin availability.")
236 wpt_wrapper.setBranchRecursionLevel(self.
wpt, level)
238 @validate_trunk_segment_params
240 if not self.
wpt or not isinstance(self.
wpt, ctypes._Pointer):
241 raise RuntimeError(
"WeberPennTree is not properly initialized. Check plugin availability.")
242 wpt_wrapper.setTrunkSegmentResolution(self.
wpt, trunk_segs)
244 @validate_branch_segment_params
246 if not self.
wpt or not isinstance(self.
wpt, ctypes._Pointer):
247 raise RuntimeError(
"WeberPennTree is not properly initialized. Check plugin availability.")
248 wpt_wrapper.setBranchSegmentResolution(self.
wpt, branch_segs)
250 @validate_leaf_subdivisions_params
252 if not self.
wpt or not isinstance(self.
wpt, ctypes._Pointer):
253 raise RuntimeError(
"WeberPennTree is not properly initialized. Check plugin availability.")
254 wpt_wrapper.setLeafSubdivisions(self.
wpt, leaf_segs_x, leaf_segs_y)
List[int] getAllUUIDs(self, int tree_id)
None setBranchSegmentResolution(self, int branch_segs)
List[int] getLeafUUIDs(self, int tree_id)
None setTrunkSegmentResolution(self, int trunk_segs)
List[int] getBranchUUIDs(self, int tree_id)
__init__(self, Context context)
int buildTree(self, WPTType wpt_type, vec3 origin=vec3(0, 0, 0), float scale=1)
List[int] getTrunkUUIDs(self, int tree_id)
None setLeafSubdivisions(self, int leaf_segs_x, int leaf_segs_y)
__exit__(self, exc_type, exc_value, traceback)
None setBranchRecursionLevel(self, int level)
is_weberpenntree_available()
Check if WeberPennTree plugin is available for use.
_weberpenntree_working_directory()
Context manager that temporarily changes working directory to where WeberPennTree assets are located.