Skip to content

Commit

Permalink
Improve line width support (#480)
Browse files Browse the repository at this point in the history
  • Loading branch information
randallfrank authored Nov 19, 2024
1 parent 51de884 commit 60c3a74
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 64 deletions.
12 changes: 9 additions & 3 deletions src/ansys/pyensight/core/utils/omniverse.py
Original file line number Diff line number Diff line change
Expand Up @@ -361,9 +361,15 @@ def update(self, temporal: bool = False, line_width: float = 0.0) -> None:
update_cmd += f"{prefix}timesteps=1"
prefix = "&"
if line_width != 0.0:
# only in 2025 R2 and beyond
if self._ensight._session.ensight_version_check("2025 R2", exception=False):
update_cmd += f"{prefix}line_width={line_width}"
add_linewidth = False
if isinstance(self._ensight, ModuleType):
add_linewidth = True
else:
# only in 2025 R2 and beyond
if self._ensight._session.ensight_version_check("2025 R2", exception=False):
add_linewidth = True
if add_linewidth:
update_cmd += f"{prefix}ANSYS_linewidth={line_width}"
prefix = "&"
self._check_modules()
if not self.is_running_omniverse():
Expand Down
30 changes: 19 additions & 11 deletions src/ansys/pyensight/core/utils/omniverse_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,7 @@ def __init__(
normalize_geometry: bool = False,
dsg_uri: str = "",
monitor_directory: str = "",
line_width: float = -0.0001,
use_lines: bool = False,
line_width: float = 0.0,
) -> None:
self._dsg_uri = dsg_uri
self._destination = destination
Expand All @@ -96,7 +95,6 @@ def __init__(
self._status_filename: str = ""
self._monitor_directory: str = monitor_directory
self._line_width = line_width
self._use_lines = use_lines

@property
def monitor_directory(self) -> Optional[str]:
Expand Down Expand Up @@ -173,6 +171,14 @@ def time_scale(self) -> float:
def time_scale(self, value: float) -> None:
self._time_scale = value

@property
def line_width(self) -> float:
return self._line_width

@line_width.setter
def line_width(self, line_width: float) -> None:
self._line_width = line_width

def run_server(self, one_shot: bool = False) -> None:
"""
Run a DSG to Omniverse server in process.
Expand All @@ -189,11 +195,11 @@ def run_server(self, one_shot: bool = False) -> None:

# Build the Omniverse connection
omni_link = ov_dsg_server.OmniverseWrapper(
destination=self._destination, line_width=self._line_width, use_lines=self._use_lines
destination=self._destination, line_width=self.line_width
)
logging.info("Omniverse connection established.")

# parse the DSG USI
# parse the DSG URI
parsed = urlparse(self.dsg_uri)
port = parsed.port
host = parsed.hostname
Expand Down Expand Up @@ -223,7 +229,10 @@ def run_server(self, one_shot: bool = False) -> None:

# until the link is dropped, continue
while not dsg_link.is_shutdown() and not self._shutdown:
# Reset the line width to the CLI default before each update
omni_link.line_width = self.line_width
dsg_link.handle_one_update()

if one_shot:
break

Expand Down Expand Up @@ -276,7 +285,7 @@ def run_monitor(self):

# Build the Omniverse connection
omni_link = ov_dsg_server.OmniverseWrapper(
destination=self._destination, line_width=self._line_width, use_lines=self._use_lines
destination=self._destination, line_width=self.line_width
)
logging.info("Omniverse connection established.")

Expand Down Expand Up @@ -347,6 +356,8 @@ def run_monitor(self):
logging.warning("Time values not currently supported.")
if len(files_to_process) > 1:
logging.warning("Multiple glb files not currently fully supported.")
# Reset the line width to the CLI default before each update
omni_link.line_width = self.line_width
# Upload the files
glb_link.start_uploads([timeline[0], timeline[-1]])
for glb_file, timestamp in zip(files_to_process, file_timestamps):
Expand Down Expand Up @@ -463,13 +474,12 @@ def run_monitor(self):
line_default = float(line_default)
except ValueError:
line_default = None
# Potential future default: -0.0001
parser.add_argument(
"--line_width",
metavar="line_width",
default=line_default,
type=float,
help=f"Width of lines: >0=absolute size. <0=fraction of diagonal. 0=wireframe. Default: {line_default}",
help=f"Width of lines: >0=absolute size. <0=fraction of diagonal. 0=none. Default: {line_default}",
)

# parse the command line
Expand All @@ -492,8 +502,7 @@ def run_monitor(self):
logging.basicConfig(**log_args) # type: ignore

# size of lines in data units or fraction of bounding box diagonal
use_lines = args.line_width is not None
line_width = -0.0001
line_width = 0.0
if args.line_width is not None:
line_width = args.line_width

Expand All @@ -508,7 +517,6 @@ def run_monitor(self):
vrmode=not args.include_camera,
temporal=args.temporal,
line_width=line_width,
use_lines=use_lines,
)

# run the server
Expand Down
82 changes: 41 additions & 41 deletions src/ansys/pyensight/core/utils/omniverse_dsg_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ def __init__(
self,
live_edit: bool = False,
destination: str = "",
line_width: float = -0.0001,
use_lines: bool = False,
line_width: float = 0.0,
) -> None:
self._cleaned_index = 0
self._cleaned_names: dict = {}
Expand All @@ -61,7 +60,6 @@ def __init__(
self.destination = destination

self._line_width = line_width
self._use_lines = use_lines

@property
def destination(self) -> str:
Expand All @@ -82,10 +80,6 @@ def line_width(self) -> float:
def line_width(self, line_width: float) -> None:
self._line_width = line_width

@property
def use_lines(self) -> bool:
return self._use_lines

def shutdown(self) -> None:
"""
Shutdown the connection to Omniverse cleanly.
Expand Down Expand Up @@ -727,7 +721,16 @@ def __init__(self, omni: OmniverseWrapper):
def add_group(self, id: int, view: bool = False) -> None:
super().add_group(id, view)
group = self.session.groups[id]

if not view:
# Capture changes in line/sphere sizes if it was not set from cli
width = self.get_dsg_cmd_attribute(group, "ANSYS_linewidth")
if width:
try:
self._omni.line_width = float(width)
except ValueError:
pass

parent_prim = self._group_prims[group.parent_id]
obj_type = self.get_dsg_cmd_attribute(group, "ENS_OBJ_TYPE")
matrix = self.group_matrix(group)
Expand Down Expand Up @@ -801,40 +804,37 @@ def finalize_part(self, part: Part) -> None:
first_timestep=(self.session.cur_timeline[0] == self.session.time_limits[0]),
mat_info=mat_info,
)
if self._omni.use_lines:
command, verts, tcoords, var_cmd = part.line_rep()
if command is not None:
# If there are no triangle (ideally if these are not hidden line
# edges), then use the base color for the part. If there are
# triangles, then assume these are hidden line edges and use the
# line_color.
line_color = color
if has_triangles:
line_color = [
part.cmd.line_color[0] * part.cmd.diffuse,
part.cmd.line_color[1] * part.cmd.diffuse,
part.cmd.line_color[2] * part.cmd.diffuse,
part.cmd.line_color[3],
]
# TODO: texture coordinates on lines are current invalid in OV
var_cmd = None
tcoords = None
# Generate the lines
_ = self._omni.create_dsg_lines(
name,
obj_id,
part.hash,
parent_prim,
verts,
tcoords,
matrix=matrix,
diffuse=line_color,
variable=var_cmd,
timeline=self.session.cur_timeline,
first_timestep=(
self.session.cur_timeline[0] == self.session.time_limits[0]
),
)
command, verts, tcoords, var_cmd = part.line_rep()
if command is not None:
# If there are no triangle (ideally if these are not hidden line
# edges), then use the base color for the part. If there are
# triangles, then assume these are hidden line edges and use the
# line_color.
line_color = color
if has_triangles:
line_color = [
part.cmd.line_color[0] * part.cmd.diffuse,
part.cmd.line_color[1] * part.cmd.diffuse,
part.cmd.line_color[2] * part.cmd.diffuse,
part.cmd.line_color[3],
]
# TODO: texture coordinates on lines are current invalid in OV
var_cmd = None
tcoords = None
# Generate the lines
_ = self._omni.create_dsg_lines(
name,
obj_id,
part.hash,
parent_prim,
verts,
tcoords,
matrix=matrix,
diffuse=line_color,
variable=var_cmd,
timeline=self.session.cur_timeline,
first_timestep=(self.session.cur_timeline[0] == self.session.time_limits[0]),
)

elif part.cmd.render == part.cmd.NODES:
command, verts, sizes, colors, var_cmd = part.point_rep()
Expand Down
33 changes: 25 additions & 8 deletions src/ansys/pyensight/core/utils/omniverse_glb_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,6 @@ def _parse_mesh(self, meshid: int, parentid: int, parentname: str) -> None:
"""
mesh = self._gltf.meshes[meshid]
for prim_idx, prim in enumerate(mesh.primitives):
# TODO: line width/point size
# POINTS, LINES, TRIANGLES, LINE_LOOP, LINE_STRIP, TRIANGLE_STRIP, TRIANGLE_FAN
mode = prim.mode
if mode not in (
Expand All @@ -147,9 +146,7 @@ def _parse_mesh(self, meshid: int, parentid: int, parentname: str) -> None:
self.warn(f"Unhandled connectivity detected: {mode}. Geometry skipped.")
continue
glb_materialid = prim.material

line_width = self._callback_handler._omni.line_width
# TODO: override from scene extension: ANSYS_linewidth

# GLB Prim -> DSG Part
part_name = f"{parentname}_prim{prim_idx}_"
Expand Down Expand Up @@ -583,7 +580,16 @@ def upload_file(self, glb_filename: str, timeline: List[float] = [0.0, 0.0]) ->
view_pb.fieldofview = camera.perspective.yfov
view_pb.aspectratio = camera.aspectratio.aspectRatio
self._handle_update_command(cmd)
for node_id in self._gltf.scenes[scene_idx].nodes:
# walk the scene nodes RJF
scene = self._gltf.scenes[scene_idx]
try:
if self._callback_handler._omni.line_width == 0.0:
width = float(scene.extensions["ANSYS_linewidth"]["linewidth"])
self._callback_handler._omni.line_width = width
except (KeyError, ValueError):
# in the case where the extension does not exist or is mal-formed
pass
for node_id in scene.nodes:
self._walk_node(node_id, view_pb.id)
self._finish_part()

Expand Down Expand Up @@ -621,11 +627,22 @@ def _build_scene_timeline(self, scene_idx: int, input_timeline: List[float]) ->
List[float]
The computed timeline.
"""
# if ANSYS_scene_time is used, time ranges will come from there
if "ANSYS_scene_time" in self._gltf.scenes[scene_idx].extensions:
return self._gltf.scenes[scene_idx].extensions["ANSYS_scene_time"]
# if there is only one scene, then use the input timeline
num_scenes = len(self._gltf.scenes)
# if ANSYS_scene_timevalue is used, time ranges will come from there
try:
t0 = self._gltf.scenes[scene_idx].extensions["ANSYS_scene_timevalue"]["timevalue"]
idx = scene_idx + 1
if idx >= num_scenes:
idx = scene_idx
t1 = self._gltf.scenes[idx].extensions["ANSYS_scene_timevalue"]["timevalue"]
else:
t1 = t0
return [t0, t1]
except KeyError:
# If we fail due to dictionary key issue, the extension does not exist or is
# improperly formatted.
pass
# if there is only one scene, then use the input timeline
if num_scenes == 1:
return input_timeline
# if the timeline has zero length, we make it the number of scenes
Expand Down
2 changes: 1 addition & 1 deletion tests/example_tests/test_usd_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def test_usd_export(tmpdir, pytestconfig: pytest.Config):
assert len(usd_files) == 1
base_usd = usd_files[0]
parts = glob.glob(os.path.join(data_dir, "Parts", "*.usd"))
assert len(parts) == 5
assert len(parts) >= 5
temp_stage = Usd.Stage.Open(usd_files[0])
# Save off the first stage to make it static and not get live updates for
# later comparison
Expand Down

0 comments on commit 60c3a74

Please sign in to comment.