From 2eefb6fca395dc2ee7fe03b0d483ad82ff22cf3e Mon Sep 17 00:00:00 2001 From: bbean Date: Fri, 9 Aug 2024 18:42:36 -0600 Subject: [PATCH 1/9] move class-specific draw methods out of View3d and into Vxyz and Pxyz --- opencsp/common/lib/csp/Facet.py | 12 ++++++------ opencsp/common/lib/csp/FacetEnsemble.py | 6 +++--- opencsp/common/lib/csp/HeliostatAbstract.py | 8 ++++---- opencsp/common/lib/csp/MirrorAbstract.py | 2 +- opencsp/common/lib/csp/MirrorPoint.py | 4 ++-- opencsp/common/lib/csp/SolarField.py | 2 +- opencsp/common/lib/geometry/Intersection.py | 5 +++-- opencsp/common/lib/geometry/Pxyz.py | 19 +++++++++++++++++++ opencsp/common/lib/geometry/Vxyz.py | 19 +++++++++++++++++++ opencsp/common/lib/render/View3d.py | 14 -------------- 10 files changed, 58 insertions(+), 33 deletions(-) diff --git a/opencsp/common/lib/csp/Facet.py b/opencsp/common/lib/csp/Facet.py index bd9753aac..942cee32f 100644 --- a/opencsp/common/lib/csp/Facet.py +++ b/opencsp/common/lib/csp/Facet.py @@ -158,7 +158,7 @@ def draw(self, view: View3d, facet_style: RenderControlFacet = None, transform: # Centroid. if facet_style.draw_centroid: - view.draw_single_Pxyz(origin, style=facet_style.centroid_style) + Pxyz(origin).draw_point(view, style=facet_style.centroid_style) # Outline. if facet_style.draw_outline: @@ -169,15 +169,15 @@ def draw(self, view: View3d, facet_style: RenderControlFacet = None, transform: # view.draw_xyz_list(corners, close=True, style=facet_style.outline_style) left, right, bottom, top = self.axis_aligned_bounding_box border = Pxyz([[left, left, right, right], [top, bottom, bottom, top], [0, 0, 0, 0]]) - view.draw_Vxyz(transform.apply(border), close=True, style=facet_style.outline_style) + transform.apply(border).draw_list(view, close=True, style=facet_style.outline_style) # Surface normal. if facet_style.draw_surface_normal: # Construct ray. surface_normal_ray = transform.apply(UP * facet_style.surface_normal_length) # Draw ray and its base. - view.draw_single_Pxyz(origin, style=facet_style.surface_normal_base_style) - view.draw_Vxyz(Vxyz.merge([origin, surface_normal_ray]), style=facet_style.surface_normal_style) + Pxyz(origin).draw_point(view, style=facet_style.surface_normal_base_style) + Vxyz.merge([origin, surface_normal_ray]).draw_list(view, style=facet_style.surface_normal_style) # # Surface normal drawn at corners. # # (Not the surface normal at the corner. Facet curvature is not shown.) @@ -209,7 +209,7 @@ def draw(self, view: View3d, facet_style: RenderControlFacet = None, transform: # pass # end function - ### POINTING FUNCTION METHODS + # POINTING FUNCTION METHODS # TODO TJL: Pointing Function methods are not tested with the updated base classes. # There will need to be an addition to `Facet` that allows users to specify the ways # a facet mounts the mirror it contains. Defining some function might @@ -271,4 +271,4 @@ def pointing_function(rotation: Rotation) -> TransformXYZ: return facet - ### END POINTING FUNCTION METHODS + # END POINTING FUNCTION METHODS diff --git a/opencsp/common/lib/csp/FacetEnsemble.py b/opencsp/common/lib/csp/FacetEnsemble.py index ef138ab3c..3d91aa4f0 100644 --- a/opencsp/common/lib/csp/FacetEnsemble.py +++ b/opencsp/common/lib/csp/FacetEnsemble.py @@ -182,17 +182,17 @@ def draw( # origin of the facet ensemble if facet_ensemble_style.draw_centroid: - view.draw_single_Pxyz(origin) + Pxyz(origin).draw_point(view) # pointing vector of the facet ensemble if facet_ensemble_style.draw_normal_vector: - view.draw_Vxyz(Vxyz.merge([origin, normal_vector]), style=facet_ensemble_style.normal_vector_style) + Vxyz.merge([origin, normal_vector]).draw_list(view, style=facet_ensemble_style.normal_vector_style) if facet_ensemble_style.draw_outline: left, right, top, bottom = self.axis_aligned_bounding_box corners = Pxyz([[left, left, right, right], [top, bottom, bottom, top], [0, 0, 0, 0]]) corners_moved = transform.apply(corners) - view.draw_Vxyz(corners_moved, close=True, style=facet_ensemble_style.outline_style) + corners_moved.draw_list(view, close=True, style=facet_ensemble_style.outline_style) # debug function def set_facet_transform_list(self, transformations: list[TransformXYZ]): diff --git a/opencsp/common/lib/csp/HeliostatAbstract.py b/opencsp/common/lib/csp/HeliostatAbstract.py index 9eec7645f..781fbc50f 100644 --- a/opencsp/common/lib/csp/HeliostatAbstract.py +++ b/opencsp/common/lib/csp/HeliostatAbstract.py @@ -309,7 +309,7 @@ def draw(self, view: View3d, heliostat_style: RenderControlHeliostat = None, tra # Centroid. if heliostat_style.draw_centroid: - view.draw_single_Pxyz(origin, style=heliostat_style.centroid_style) + Pxyz(origin).draw_point(view, style=heliostat_style.centroid_style) # # Outline. # if heliostat_style.draw_outline: @@ -318,7 +318,7 @@ def draw(self, view: View3d, heliostat_style: RenderControlHeliostat = None, tra # [top, bottom, bottom, top], # [0, 0, 0, 0]]) # corners_moved = transform.apply(corners) - # view.draw_Vxyz(corners_moved, close=True, style=heliostat_style.outline_style) + # corners_moved.draw_list(view, close=True, style=heliostat_style.outline_style) # # Surface normal. # if heliostat_style.draw_surface_normal: @@ -326,7 +326,7 @@ def draw(self, view: View3d, heliostat_style: RenderControlHeliostat = None, tra # self.facet_ensemble. # surface_normal_ray = transform.apply(UP * heliostat_style.corner_normal_length) # # Draw ray and its base. - # view.draw_Vxyz(Vxyz.merge([origin, surface_normal_ray]), + # Vxyz.merge([origin, surface_normal_ray]).draw_list(view, # close=False, # style=heliostat_style.surface_normal_style) @@ -339,7 +339,7 @@ def draw(self, view: View3d, heliostat_style: RenderControlHeliostat = None, tra if heliostat_style.post != 0: DOWN = Vxyz([0, 0, -heliostat_style.post]) direction = transform.apply(DOWN) - view.draw_Vxyz(Vxyz.merge([origin + DOWN, origin])) + Vxyz.merge([origin + DOWN, origin]).draw_list(view) # Name. if heliostat_style.draw_name: diff --git a/opencsp/common/lib/csp/MirrorAbstract.py b/opencsp/common/lib/csp/MirrorAbstract.py index c47bfca84..a2b9647fe 100644 --- a/opencsp/common/lib/csp/MirrorAbstract.py +++ b/opencsp/common/lib/csp/MirrorAbstract.py @@ -307,7 +307,7 @@ def draw( # Draw surface boundary if mirror_style.point_styles is not None: mirror_style.point_styles.markersize = 0 - view.draw_Vxyz(edge_values_lifted, style=mirror_style.point_styles) + edge_values_lifted.draw_list(view, style=mirror_style.point_styles) # Draw surface normals if mirror_style.surface_normals: diff --git a/opencsp/common/lib/csp/MirrorPoint.py b/opencsp/common/lib/csp/MirrorPoint.py index afea7799f..a3f66dc73 100644 --- a/opencsp/common/lib/csp/MirrorPoint.py +++ b/opencsp/common/lib/csp/MirrorPoint.py @@ -176,7 +176,7 @@ def draw(self, view: View3d, mirror_style: RenderControlMirror, transform: Trans p_space = self.location_in_space(domain) # Draw sample points - view.draw_single_Pxyz(p_space, style=mirror_style.point_styles) + p_space.draw_point(view, style=mirror_style.point_styles) # Calculate z height of boundary to draw (lowest z value) min_val = min(self.surface_displacement_at(domain)) @@ -189,7 +189,7 @@ def draw(self, view: View3d, mirror_style: RenderControlMirror, transform: Trans if mirror_style.point_styles is not None: edge_style = mirror_style.point_styles edge_style.markersize = 0 - view.draw_Vxyz(edge_values_lifted, style=edge_style) + edge_values_lifted.draw_list(view, style=edge_style) # Draw surface normals if mirror_style.surface_normals: diff --git a/opencsp/common/lib/csp/SolarField.py b/opencsp/common/lib/csp/SolarField.py index afbaeeb39..6c28cb13a 100644 --- a/opencsp/common/lib/csp/SolarField.py +++ b/opencsp/common/lib/csp/SolarField.py @@ -318,7 +318,7 @@ def draw( # Draw Origin if solar_field_style.draw_origin: - view.draw_single_Pxyz(origin) + Pxyz(origin).draw_point(view) # Heliostats. if solar_field_style.draw_heliostats: diff --git a/opencsp/common/lib/geometry/Intersection.py b/opencsp/common/lib/geometry/Intersection.py index 6cbb1b10d..ee6041b0a 100644 --- a/opencsp/common/lib/geometry/Intersection.py +++ b/opencsp/common/lib/geometry/Intersection.py @@ -284,8 +284,9 @@ def _Pxy_to_flux_map(points: Pxy, bins: int, resolution_type: str = "pixelX") -> def draw(self, view: View3d, style: RenderControlPointSeq = None): if style is None: style = RenderControlPointSeq() - view.draw_single_Pxyz(self.intersection_points, style) + self.intersection_points.draw_point(view, style) def draw_subset(self, view: View3d, count: int, points_style: RenderControlPointSeq = None): for i in np.floor(np.linspace(0, len(self.intersection_points) - 1, count)): - view.draw_single_Pxyz(self.intersection_points[int(i)]) + p = Pxyz(self.intersection_points[int(i)]) + p.draw_point(view) diff --git a/opencsp/common/lib/geometry/Pxyz.py b/opencsp/common/lib/geometry/Pxyz.py index bb4844d3a..c647954d3 100644 --- a/opencsp/common/lib/geometry/Pxyz.py +++ b/opencsp/common/lib/geometry/Pxyz.py @@ -1,4 +1,7 @@ from opencsp.common.lib.geometry.Vxyz import Vxyz +import opencsp.common.lib.render.View3d as v3d +import opencsp.common.lib.render_control.RenderControlFigureRecord as rcfr +import opencsp.common.lib.render_control.RenderControlPointSeq as rcps class Pxyz(Vxyz): @@ -20,3 +23,19 @@ def as_Vxyz(self): @classmethod def empty(cls): return Pxyz([[], [], []]) + + def draw_point( + self, + figure: rcfr.RenderControlFigureRecord | v3d.View3d, + style: rcps.RenderControlPointSeq = None, + labels: list[str] = None, + ): + """Calls figure.draw_xyz(p) for all points in this instance, and with + the default arguments in place for any None's.""" + if style is None: + style = rcps.default(markersize=2) + if labels is None: + labels = [None] * len(self) + view = figure if isinstance(figure, v3d.View3d) else figure.view + for x, y, z, label in zip(self.x, self.y, self.z, labels): + view.draw_xyz((x, y, z), style, label) diff --git a/opencsp/common/lib/geometry/Vxyz.py b/opencsp/common/lib/geometry/Vxyz.py index a7edbc903..d50da7767 100644 --- a/opencsp/common/lib/geometry/Vxyz.py +++ b/opencsp/common/lib/geometry/Vxyz.py @@ -8,6 +8,9 @@ from scipy.spatial.transform import Rotation from opencsp.common.lib.geometry.Vxy import Vxy +import opencsp.common.lib.render.View3d as v3d +import opencsp.common.lib.render_control.RenderControlFigureRecord as rcfr +import opencsp.common.lib.render_control.RenderControlPointSeq as rcps class Vxyz: @@ -494,3 +497,19 @@ def origin(cls): # for x, y in zip(xs, ys): # zs.append(func(x, y)) # return cls([xs, ys, zs]) + + def draw_list( + self, + figure: rcfr.RenderControlFigureRecord | v3d.View3d, + close: bool = None, + style: rcps.RenderControlPointSeq = None, + label: str = None, + ) -> None: + """Calls figure.draw_xyz_list(self.data.T) with the default arguments in place for any None's.""" + kwargs = dict() + for key, val in [('close', close), ('style', style), ('label', label)]: + if val is not None: + kwargs[key] = val + + view = figure if isinstance(figure, v3d.View3d) else figure.view + view.draw_xyz_list(self.data.T, **kwargs) diff --git a/opencsp/common/lib/render/View3d.py b/opencsp/common/lib/render/View3d.py index 77300da01..91941e355 100644 --- a/opencsp/common/lib/render/View3d.py +++ b/opencsp/common/lib/render/View3d.py @@ -23,8 +23,6 @@ from PIL import Image import scipy.ndimage -from opencsp.common.lib.geometry.Pxyz import Pxyz -from opencsp.common.lib.geometry.Vxyz import Vxyz import opencsp.common.lib.render.axis_3d as ax3d import opencsp.common.lib.render.view_spec as vs import opencsp.common.lib.render_control.RenderControlPointSeq as rcps @@ -648,14 +646,6 @@ def draw_xyz( self.draw_xyz_list(lval, style=style, label=label) - def draw_single_Pxyz(self, p: Pxyz, style: rcps.RenderControlPointSeq = None, labels: list[str] = None): - if labels == None: - labels = [None] * len(p) - if style == None: - style = rcps.default(markersize=2) - for x, y, z, label in zip(p.x, p.y, p.z, labels): - self.draw_xyz((x, y, z), style, label) - def draw_xyz_list( self, input_xyz_list: Iterable[tuple[float, float, float]], @@ -769,10 +759,6 @@ def draw_xyz_list( + "' encountered.", ) - def draw_Vxyz(self, V: Vxyz, close=False, style=None, label=None) -> None: - """Alternative to View3d.drawxyz_list that used the Vxyz class instead""" - self.draw_xyz_list(list(V.data.T), close, style, label) - # TODO TJL: only implemented for 3d views, should extend def draw_xyz_surface( self, From b58f3c1cb92f8f77ab9f2bbc53030d58b8ccc5c8 Mon Sep 17 00:00:00 2001 From: bbean Date: Fri, 9 Aug 2024 20:07:39 -0600 Subject: [PATCH 2/9] missed some references --- example/raytrace/example_RayTraceOutput.py | 2 +- example/solarfield/example_SolarFieldOutput.py | 2 +- opencsp/common/lib/test/test_FluxMaps.py | 6 +++--- opencsp/common/lib/test/test_RayTraceOutput.py | 2 +- opencsp/common/lib/test/test_SolarFieldOutput.py | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/example/raytrace/example_RayTraceOutput.py b/example/raytrace/example_RayTraceOutput.py index 5829344d7..2ed3ba84d 100644 --- a/example/raytrace/example_RayTraceOutput.py +++ b/example/raytrace/example_RayTraceOutput.py @@ -646,7 +646,7 @@ def _draw_helper(view: View3d) -> None: pointing_vector = st.tracking_surface_normal_xyz( heliostat_origin, aimpoint_xyz, lln.NSTTF_ORIGIN, when_ymdhmsz ) - view.draw_Vxyz(Vxyz.merge([heliostat_origin, heliostat_origin + pointing_vector * 10])) + Vxyz.merge([heliostat_origin, heliostat_origin + pointing_vector * 10]).draw_list(view) # debug self.show_save_and_check_figure(fig_record) diff --git a/example/solarfield/example_SolarFieldOutput.py b/example/solarfield/example_SolarFieldOutput.py index 516dbb08f..b1139389c 100644 --- a/example/solarfield/example_SolarFieldOutput.py +++ b/example/solarfield/example_SolarFieldOutput.py @@ -503,7 +503,7 @@ def example_solar_field_h_outlines(self) -> None: code_tag=self.code_tag, ) solar_field.draw(fig_record.view, solar_field_style) - fig_record.view.draw_Vxyz(aimpoint_xyz, style=rcps.marker(color='tab:orange'), label='aimpoint_xyz') + aimpoint_xyz.draw_list(fig_record.view, style=rcps.marker(color='tab:orange'), label='aimpoint_xyz') # Output. self.show_save_and_check_figure(fig_record) diff --git a/opencsp/common/lib/test/test_FluxMaps.py b/opencsp/common/lib/test/test_FluxMaps.py index f0c661b9e..543ff00a9 100644 --- a/opencsp/common/lib/test/test_FluxMaps.py +++ b/opencsp/common/lib/test/test_FluxMaps.py @@ -175,9 +175,9 @@ def square(z: float): mirror.draw(fig_record.view, mirror_style) trace_style = rcrt.init_current_lengths(current_len=6) trace.draw(fig_record.view, trace_style) - fig_record.view.draw_Vxyz(square(4), close=True, style=rcps.RenderControlPointSeq(color='b', marker=',')) - fig_record.view.draw_Vxyz(square(5), close=True, style=rcps.RenderControlPointSeq(color='g', marker=',')) - fig_record.view.draw_Vxyz(square(6), close=True, style=rcps.RenderControlPointSeq(color='r', marker=',')) + square(4).draw_list(fig_record.view, close=True, style=rcps.RenderControlPointSeq(color='b', marker=',')) + square(5).draw_list(fig_record.view, close=True, style=rcps.RenderControlPointSeq(color='g', marker=',')) + square(6).draw_list(fig_record.view, close=True, style=rcps.RenderControlPointSeq(color='r', marker=',')) self.show_save_and_check_figure(fig_record) # Draw z=4 diff --git a/opencsp/common/lib/test/test_RayTraceOutput.py b/opencsp/common/lib/test/test_RayTraceOutput.py index bfd8f66c7..7217018c3 100644 --- a/opencsp/common/lib/test/test_RayTraceOutput.py +++ b/opencsp/common/lib/test/test_RayTraceOutput.py @@ -449,7 +449,7 @@ def _draw_helper(view: View3d) -> None: pointing_vector = st.tracking_surface_normal_xyz( heliostat_origin, aimpoint_xyz, lln.NSTTF_ORIGIN, when_ymdhmsz ) - view.draw_Vxyz(Vxyz.merge([heliostat_origin, heliostat_origin + pointing_vector * 10])) + Vxyz.merge([heliostat_origin, heliostat_origin + pointing_vector * 10]).draw_list(view) # debug self.show_save_and_check_figure(fig_record) diff --git a/opencsp/common/lib/test/test_SolarFieldOutput.py b/opencsp/common/lib/test/test_SolarFieldOutput.py index d323f3bb0..87ab43cb9 100644 --- a/opencsp/common/lib/test/test_SolarFieldOutput.py +++ b/opencsp/common/lib/test/test_SolarFieldOutput.py @@ -503,7 +503,7 @@ def old_solar_field_h_outlines(self) -> None: code_tag=self.code_tag, ) solar_field.draw(fig_record.view, solar_field_style) - fig_record.view.draw_Vxyz(aimpoint_xyz, style=rcps.marker(color='tab:orange'), label='aimpoint_xyz') + aimpoint_xyz.draw_list(fig_record.view, style=rcps.marker(color='tab:orange'), label='aimpoint_xyz') # Output. self.show_save_and_check_figure(fig_record) From c23cbdcd7cfb248f3779f7d054339e8a17dd9e28 Mon Sep 17 00:00:00 2001 From: bbean Date: Mon, 12 Aug 2024 13:29:10 -0600 Subject: [PATCH 3/9] modify draw_xyz_list and draw_pq_list to be compatible with numpy arrays --- opencsp/common/lib/render/View3d.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/opencsp/common/lib/render/View3d.py b/opencsp/common/lib/render/View3d.py index 91941e355..039963cd9 100644 --- a/opencsp/common/lib/render/View3d.py +++ b/opencsp/common/lib/render/View3d.py @@ -666,7 +666,7 @@ def draw_xyz_list( if len(input_xyz_list) > 0: # Construct the point list to draw, including closing the polygon if desired. if close and (len(input_xyz_list) > 2): - xyz_list = input_xyz_list.copy() + xyz_list = [xyz for xyz in input_xyz_list] xyz_list.append(input_xyz_list[0]) else: xyz_list = input_xyz_list @@ -1070,7 +1070,7 @@ def draw_pq_list( # Construct the point list to draw, including closing the polygon if desired. if close and (len(input_pq_list) > 2): - pq_list = input_pq_list.copy() + pq_list = [pq for pq in input_pq_list] pq_list.append(input_pq_list[0]) else: pq_list = input_pq_list From f331879119e0de72045b4b5ac6bba48e28ef606f Mon Sep 17 00:00:00 2001 From: bbean Date: Mon, 12 Aug 2024 14:18:50 -0600 Subject: [PATCH 4/9] missed more references --- example/mirror/example_MirrorOutput.py | 2 +- example/raytrace/example_RayTraceOutput.py | 4 ++-- example/solarfield/example_SolarFieldOutput.py | 10 +++++----- opencsp/common/lib/test/test_MirrorOutput.py | 2 +- opencsp/common/lib/test/test_RayTraceOutput.py | 4 ++-- opencsp/common/lib/test/test_SolarFieldOutput.py | 10 +++++----- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/example/mirror/example_MirrorOutput.py b/example/mirror/example_MirrorOutput.py index 618b886dc..99c16c1cc 100644 --- a/example/mirror/example_MirrorOutput.py +++ b/example/mirror/example_MirrorOutput.py @@ -404,7 +404,7 @@ def example_solar_field(self) -> None: # trace = rt.trace_scene(scene, Resolution.center()) # trace.draw(fig_record.view, rcrt.RenderControlRayTrace(light_path_control)) - # fig_record.view.draw_single_Pxyz(aimpoint) + # aimpoint.draw_point(fig_record.view) # Output. self.show_save_and_check_figure(fig_record) diff --git a/example/raytrace/example_RayTraceOutput.py b/example/raytrace/example_RayTraceOutput.py index 2ed3ba84d..fa4ca862c 100644 --- a/example/raytrace/example_RayTraceOutput.py +++ b/example/raytrace/example_RayTraceOutput.py @@ -639,7 +639,7 @@ def fn_5w1(x, y): def _draw_helper(view: View3d) -> None: sf1.draw(view, solar_field_style) trace.draw(view, trace_control) - view.draw_single_Pxyz(aimpoint_xyz, style=rcps.marker(color='tab:orange')) + aimpoint_xyz.draw_point(view, style=rcps.marker(color='tab:orange')) # debug heliostat_origin = sf1.heliostats[0].self_to_global_tranformation.apply(Pxyz.origin()) @@ -770,7 +770,7 @@ def example_partial_field_trace(self) -> None: trace = rt.trace_scene(scene, Resolution.center(), verbose=False) trace.draw(view, RenderControlRayTrace(RenderControlLightPath(15, 200))) - view.draw_single_Pxyz(aimpoint_xyz, rcps.RenderControlPointSeq(color='orange', marker='.')) + aimpoint_xyz.draw_point(view, rcps.RenderControlPointSeq(color='orange', marker='.')) self.show_save_and_check_figure(fig_record) diff --git a/example/solarfield/example_SolarFieldOutput.py b/example/solarfield/example_SolarFieldOutput.py index b1139389c..1a611b946 100644 --- a/example/solarfield/example_SolarFieldOutput.py +++ b/example/solarfield/example_SolarFieldOutput.py @@ -763,7 +763,7 @@ def example_heliostat_vector_field(self) -> None: comments=comments, code_tag=self.code_tag, ) - fig_record.view.draw_single_Pxyz(aimpoint_xyz, style=rcps.marker(color='tab:orange'), labels='aimpoint_xyz') + aimpoint_xyz.draw_point(fig_record.view, style=rcps.marker(color='tab:orange'), labels='aimpoint_xyz') solar_field.draw(fig_record.view, solar_field_style) self.show_save_and_check_figure(fig_record) @@ -781,7 +781,7 @@ def example_heliostat_vector_field(self) -> None: comments=comments, code_tag=self.code_tag, ) - fig_record.view.draw_single_Pxyz(aimpoint_xyz, style=rcps.marker(color='tab:orange'), labels='aimpoint_xyz') + aimpoint_xyz.draw_point(fig_record.view, style=rcps.marker(color='tab:orange'), labels='aimpoint_xyz') solar_field.draw(fig_record.view, solar_field_style) self.show_save_and_check_figure(fig_record) @@ -799,7 +799,7 @@ def example_heliostat_vector_field(self) -> None: comments=comments, code_tag=self.code_tag, ) - fig_record.view.draw_single_Pxyz(aimpoint_xyz, style=rcps.marker(color='tab:orange'), labels='aimpoint_xyz') + aimpoint_xyz.draw_point(fig_record.view, style=rcps.marker(color='tab:orange'), labels='aimpoint_xyz') solar_field.draw(fig_record.view, solar_field_style) self.show_save_and_check_figure(fig_record) @@ -817,7 +817,7 @@ def example_heliostat_vector_field(self) -> None: comments=comments, code_tag=self.code_tag, ) - fig_record.view.draw_single_Pxyz(aimpoint_xyz, style=rcps.marker(color='tab:orange'), labels='aimpoint_xyz') + aimpoint_xyz.draw_point(fig_record.view, style=rcps.marker(color='tab:orange'), labels='aimpoint_xyz') solar_field.draw(fig_record.view, solar_field_style) self.show_save_and_check_figure(fig_record) @@ -868,7 +868,7 @@ def example_dense_vector_field(self) -> None: code_tag=self.code_tag, ) solar_field.draw(fig_record.view, solar_field_style) - fig_record.view.draw_single_Pxyz(aimpoint_xyz, style=rcps.marker(color='tab:orange'), labels='aimpoint_xyz') + aimpoint_xyz.draw_point(fig_record.view, style=rcps.marker(color='tab:orange'), labels='aimpoint_xyz') # Draw dense vector field. grid_xy = solar_field.heliostat_field_regular_grid_xy(40, 20) diff --git a/opencsp/common/lib/test/test_MirrorOutput.py b/opencsp/common/lib/test/test_MirrorOutput.py index 983a59d01..01070b419 100644 --- a/opencsp/common/lib/test/test_MirrorOutput.py +++ b/opencsp/common/lib/test/test_MirrorOutput.py @@ -331,7 +331,7 @@ def test_solar_field(self) -> None: # trace = rt.trace_scene(scene, Resolution.center()) # trace.draw(fig_record.view, rcrt.RenderControlRayTrace(light_path_control)) - # fig_record.view.draw_single_Pxyz(aimpoint) + # aimpoint.draw_point(fig_record.view) # Output. self.show_save_and_check_figure(fig_record) diff --git a/opencsp/common/lib/test/test_RayTraceOutput.py b/opencsp/common/lib/test/test_RayTraceOutput.py index 7217018c3..cbaa06c36 100644 --- a/opencsp/common/lib/test/test_RayTraceOutput.py +++ b/opencsp/common/lib/test/test_RayTraceOutput.py @@ -442,7 +442,7 @@ def fn_5w1(x, y): def _draw_helper(view: View3d) -> None: sf1.draw(view, solar_field_style) trace.draw(view, trace_control) - view.draw_single_Pxyz(aimpoint_xyz, style=rcps.marker(color='tab:orange')) + aimpoint_xyz.draw_point(view, style=rcps.marker(color='tab:orange')) # debug heliostat_origin = sf1.heliostats[0].self_to_global_tranformation.apply(Pxyz.origin()) @@ -573,7 +573,7 @@ def test_partial_field_trace(self) -> None: trace = rt.trace_scene(scene, Resolution.center(), verbose=False) trace.draw(view, RenderControlRayTrace(RenderControlLightPath(15, 200))) - view.draw_single_Pxyz(aimpoint_xyz, rcps.RenderControlPointSeq(color='orange', marker='.')) + aimpoint_xyz.draw_point(view, rcps.RenderControlPointSeq(color='orange', marker='.')) self.show_save_and_check_figure(fig_record) diff --git a/opencsp/common/lib/test/test_SolarFieldOutput.py b/opencsp/common/lib/test/test_SolarFieldOutput.py index 87ab43cb9..5373b1cbb 100644 --- a/opencsp/common/lib/test/test_SolarFieldOutput.py +++ b/opencsp/common/lib/test/test_SolarFieldOutput.py @@ -753,7 +753,7 @@ def test_heliostat_vector_field(self) -> None: # fig_record = fm.setup_figure_for_3d_data(self.figure_control, self.axis_control_m, vs.view_spec_3d(), number_in_name=False, # input_prefix=self.figure_prefix(13), # Figure numbers needed because titles may be identical. Hard-code number because test order is unpredictable. # title=title, caption=caption, comments=comments, code_tag=self.code_tag) - # fig_record.view.draw_single_Pxyz(aimpoint_xyz, style=rcps.marker(color='tab:orange'), labels='aimpoint_xyz') + # aimpoint_xyz.draw_point(fig_record.view, style=rcps.marker(color='tab:orange'), labels='aimpoint_xyz') # solar_field.draw(fig_record.view, solar_field_style) # self.show_save_and_check_figure(fig_record) @@ -761,7 +761,7 @@ def test_heliostat_vector_field(self) -> None: # fig_record = fm.setup_figure_for_3d_data(self.figure_control, self.axis_control_m, vs.view_spec_xy(), number_in_name=False, # input_prefix=self.figure_prefix(14), # Figure numbers needed because titles may be identical. Hard-code number because test order is unpredictable. # title=title, caption=caption, comments=comments, code_tag=self.code_tag) - # fig_record.view.draw_single_Pxyz(aimpoint_xyz, style=rcps.marker(color='tab:orange'), labels='aimpoint_xyz') + # aimpoint_xyz.draw_point(fig_record.view, style=rcps.marker(color='tab:orange'), labels='aimpoint_xyz') # solar_field.draw(fig_record.view, solar_field_style) # self.show_save_and_check_figure(fig_record) @@ -779,7 +779,7 @@ def test_heliostat_vector_field(self) -> None: comments=comments, code_tag=self.code_tag, ) - fig_record.view.draw_single_Pxyz(aimpoint_xyz, style=rcps.marker(color='tab:orange'), labels='aimpoint_xyz') + aimpoint_xyz.draw_point(fig_record.view, style=rcps.marker(color='tab:orange'), labels='aimpoint_xyz') solar_field.draw(fig_record.view, solar_field_style) self.show_save_and_check_figure(fig_record) @@ -787,7 +787,7 @@ def test_heliostat_vector_field(self) -> None: # fig_record = fm.setup_figure_for_3d_data(self.figure_control, self.axis_control_m, vs.view_spec_yz(), number_in_name=False, # input_prefix=self.figure_prefix(16), # Figure numbers needed because titles may be identical. Hard-code number because test order is unpredictable. # title=title, caption=caption, comments=comments, code_tag=self.code_tag) - # fig_record.view.draw_single_Pxyz(aimpoint_xyz, style=rcps.marker(color='tab:orange'), labels='aimpoint_xyz') + # aimpoint_xyz.draw_point(fig_record.view, style=rcps.marker(color='tab:orange'), labels='aimpoint_xyz') # solar_field.draw(fig_record.view, solar_field_style) # self.show_save_and_check_figure(fig_record) @@ -838,7 +838,7 @@ def test_dense_vector_field(self) -> None: code_tag=self.code_tag, ) solar_field.draw(fig_record.view, solar_field_style) - fig_record.view.draw_single_Pxyz(aimpoint_xyz, style=rcps.marker(color='tab:orange'), labels='aimpoint_xyz') + aimpoint_xyz.draw_point(fig_record.view, style=rcps.marker(color='tab:orange'), labels='aimpoint_xyz') # Draw dense vector field. grid_xy = solar_field.heliostat_field_regular_grid_xy(40, 20) From 0a45ef02525b5d49e923106b08696b02d4208523 Mon Sep 17 00:00:00 2001 From: bbean Date: Fri, 6 Sep 2024 10:45:49 -0600 Subject: [PATCH 5/9] moved method Pxyz.draw_point() -> Vxyz.draw_points(), moved method Vxyz.draw_list() -> Vxyz.draw_line() --- example/raytrace/example_RayTraceOutput.py | 6 ++--- .../solarfield/example_SolarFieldOutput.py | 12 +++++----- opencsp/common/lib/csp/Facet.py | 8 +++---- opencsp/common/lib/csp/FacetEnsemble.py | 6 ++--- opencsp/common/lib/csp/HeliostatAbstract.py | 4 ++-- opencsp/common/lib/csp/MirrorAbstract.py | 2 +- opencsp/common/lib/csp/MirrorPoint.py | 4 ++-- opencsp/common/lib/csp/SolarField.py | 2 +- opencsp/common/lib/geometry/Intersection.py | 4 ++-- opencsp/common/lib/geometry/Pxyz.py | 16 ------------- opencsp/common/lib/geometry/Vxyz.py | 24 +++++++++++++++++-- opencsp/common/lib/render/View3d.py | 19 +++++++++++---- opencsp/common/lib/test/test_FluxMaps.py | 6 ++--- .../common/lib/test/test_RayTraceOutput.py | 6 ++--- .../common/lib/test/test_SolarFieldOutput.py | 6 ++--- 15 files changed, 70 insertions(+), 55 deletions(-) diff --git a/example/raytrace/example_RayTraceOutput.py b/example/raytrace/example_RayTraceOutput.py index fa4ca862c..71b0a1de0 100644 --- a/example/raytrace/example_RayTraceOutput.py +++ b/example/raytrace/example_RayTraceOutput.py @@ -639,14 +639,14 @@ def fn_5w1(x, y): def _draw_helper(view: View3d) -> None: sf1.draw(view, solar_field_style) trace.draw(view, trace_control) - aimpoint_xyz.draw_point(view, style=rcps.marker(color='tab:orange')) + aimpoint_xyz.draw_points(view, style=rcps.marker(color='tab:orange')) # debug heliostat_origin = sf1.heliostats[0].self_to_global_tranformation.apply(Pxyz.origin()) pointing_vector = st.tracking_surface_normal_xyz( heliostat_origin, aimpoint_xyz, lln.NSTTF_ORIGIN, when_ymdhmsz ) - Vxyz.merge([heliostat_origin, heliostat_origin + pointing_vector * 10]).draw_list(view) + Vxyz.merge([heliostat_origin, heliostat_origin + pointing_vector * 10]).draw_line(view) # debug self.show_save_and_check_figure(fig_record) @@ -770,7 +770,7 @@ def example_partial_field_trace(self) -> None: trace = rt.trace_scene(scene, Resolution.center(), verbose=False) trace.draw(view, RenderControlRayTrace(RenderControlLightPath(15, 200))) - aimpoint_xyz.draw_point(view, rcps.RenderControlPointSeq(color='orange', marker='.')) + aimpoint_xyz.draw_points(view, rcps.RenderControlPointSeq(color='orange', marker='.')) self.show_save_and_check_figure(fig_record) diff --git a/example/solarfield/example_SolarFieldOutput.py b/example/solarfield/example_SolarFieldOutput.py index 1a611b946..551c88f63 100644 --- a/example/solarfield/example_SolarFieldOutput.py +++ b/example/solarfield/example_SolarFieldOutput.py @@ -503,7 +503,7 @@ def example_solar_field_h_outlines(self) -> None: code_tag=self.code_tag, ) solar_field.draw(fig_record.view, solar_field_style) - aimpoint_xyz.draw_list(fig_record.view, style=rcps.marker(color='tab:orange'), label='aimpoint_xyz') + aimpoint_xyz.draw_line(fig_record.view, style=rcps.marker(color='tab:orange'), label='aimpoint_xyz') # Output. self.show_save_and_check_figure(fig_record) @@ -763,7 +763,7 @@ def example_heliostat_vector_field(self) -> None: comments=comments, code_tag=self.code_tag, ) - aimpoint_xyz.draw_point(fig_record.view, style=rcps.marker(color='tab:orange'), labels='aimpoint_xyz') + aimpoint_xyz.draw_points(fig_record.view, style=rcps.marker(color='tab:orange'), labels='aimpoint_xyz') solar_field.draw(fig_record.view, solar_field_style) self.show_save_and_check_figure(fig_record) @@ -781,7 +781,7 @@ def example_heliostat_vector_field(self) -> None: comments=comments, code_tag=self.code_tag, ) - aimpoint_xyz.draw_point(fig_record.view, style=rcps.marker(color='tab:orange'), labels='aimpoint_xyz') + aimpoint_xyz.draw_points(fig_record.view, style=rcps.marker(color='tab:orange'), labels='aimpoint_xyz') solar_field.draw(fig_record.view, solar_field_style) self.show_save_and_check_figure(fig_record) @@ -799,7 +799,7 @@ def example_heliostat_vector_field(self) -> None: comments=comments, code_tag=self.code_tag, ) - aimpoint_xyz.draw_point(fig_record.view, style=rcps.marker(color='tab:orange'), labels='aimpoint_xyz') + aimpoint_xyz.draw_points(fig_record.view, style=rcps.marker(color='tab:orange'), labels='aimpoint_xyz') solar_field.draw(fig_record.view, solar_field_style) self.show_save_and_check_figure(fig_record) @@ -817,7 +817,7 @@ def example_heliostat_vector_field(self) -> None: comments=comments, code_tag=self.code_tag, ) - aimpoint_xyz.draw_point(fig_record.view, style=rcps.marker(color='tab:orange'), labels='aimpoint_xyz') + aimpoint_xyz.draw_points(fig_record.view, style=rcps.marker(color='tab:orange'), labels='aimpoint_xyz') solar_field.draw(fig_record.view, solar_field_style) self.show_save_and_check_figure(fig_record) @@ -868,7 +868,7 @@ def example_dense_vector_field(self) -> None: code_tag=self.code_tag, ) solar_field.draw(fig_record.view, solar_field_style) - aimpoint_xyz.draw_point(fig_record.view, style=rcps.marker(color='tab:orange'), labels='aimpoint_xyz') + aimpoint_xyz.draw_points(fig_record.view, style=rcps.marker(color='tab:orange'), labels='aimpoint_xyz') # Draw dense vector field. grid_xy = solar_field.heliostat_field_regular_grid_xy(40, 20) diff --git a/opencsp/common/lib/csp/Facet.py b/opencsp/common/lib/csp/Facet.py index 942cee32f..a6e044b11 100644 --- a/opencsp/common/lib/csp/Facet.py +++ b/opencsp/common/lib/csp/Facet.py @@ -158,7 +158,7 @@ def draw(self, view: View3d, facet_style: RenderControlFacet = None, transform: # Centroid. if facet_style.draw_centroid: - Pxyz(origin).draw_point(view, style=facet_style.centroid_style) + origin.draw_points(view, style=facet_style.centroid_style) # Outline. if facet_style.draw_outline: @@ -169,15 +169,15 @@ def draw(self, view: View3d, facet_style: RenderControlFacet = None, transform: # view.draw_xyz_list(corners, close=True, style=facet_style.outline_style) left, right, bottom, top = self.axis_aligned_bounding_box border = Pxyz([[left, left, right, right], [top, bottom, bottom, top], [0, 0, 0, 0]]) - transform.apply(border).draw_list(view, close=True, style=facet_style.outline_style) + transform.apply(border).draw_line(view, close=True, style=facet_style.outline_style) # Surface normal. if facet_style.draw_surface_normal: # Construct ray. surface_normal_ray = transform.apply(UP * facet_style.surface_normal_length) # Draw ray and its base. - Pxyz(origin).draw_point(view, style=facet_style.surface_normal_base_style) - Vxyz.merge([origin, surface_normal_ray]).draw_list(view, style=facet_style.surface_normal_style) + origin.draw_points(view, style=facet_style.surface_normal_base_style) + Vxyz.merge([origin, surface_normal_ray]).draw_line(view, style=facet_style.surface_normal_style) # # Surface normal drawn at corners. # # (Not the surface normal at the corner. Facet curvature is not shown.) diff --git a/opencsp/common/lib/csp/FacetEnsemble.py b/opencsp/common/lib/csp/FacetEnsemble.py index 3d91aa4f0..17eabb5f2 100644 --- a/opencsp/common/lib/csp/FacetEnsemble.py +++ b/opencsp/common/lib/csp/FacetEnsemble.py @@ -182,17 +182,17 @@ def draw( # origin of the facet ensemble if facet_ensemble_style.draw_centroid: - Pxyz(origin).draw_point(view) + origin.draw_points(view) # pointing vector of the facet ensemble if facet_ensemble_style.draw_normal_vector: - Vxyz.merge([origin, normal_vector]).draw_list(view, style=facet_ensemble_style.normal_vector_style) + Vxyz.merge([origin, normal_vector]).draw_line(view, style=facet_ensemble_style.normal_vector_style) if facet_ensemble_style.draw_outline: left, right, top, bottom = self.axis_aligned_bounding_box corners = Pxyz([[left, left, right, right], [top, bottom, bottom, top], [0, 0, 0, 0]]) corners_moved = transform.apply(corners) - corners_moved.draw_list(view, close=True, style=facet_ensemble_style.outline_style) + corners_moved.draw_line(view, close=True, style=facet_ensemble_style.outline_style) # debug function def set_facet_transform_list(self, transformations: list[TransformXYZ]): diff --git a/opencsp/common/lib/csp/HeliostatAbstract.py b/opencsp/common/lib/csp/HeliostatAbstract.py index 781fbc50f..147278fd8 100644 --- a/opencsp/common/lib/csp/HeliostatAbstract.py +++ b/opencsp/common/lib/csp/HeliostatAbstract.py @@ -309,7 +309,7 @@ def draw(self, view: View3d, heliostat_style: RenderControlHeliostat = None, tra # Centroid. if heliostat_style.draw_centroid: - Pxyz(origin).draw_point(view, style=heliostat_style.centroid_style) + origin.draw_points(view, style=heliostat_style.centroid_style) # # Outline. # if heliostat_style.draw_outline: @@ -339,7 +339,7 @@ def draw(self, view: View3d, heliostat_style: RenderControlHeliostat = None, tra if heliostat_style.post != 0: DOWN = Vxyz([0, 0, -heliostat_style.post]) direction = transform.apply(DOWN) - Vxyz.merge([origin + DOWN, origin]).draw_list(view) + Vxyz.merge([origin + DOWN, origin]).draw_line(view) # Name. if heliostat_style.draw_name: diff --git a/opencsp/common/lib/csp/MirrorAbstract.py b/opencsp/common/lib/csp/MirrorAbstract.py index a2b9647fe..1c029c3a6 100644 --- a/opencsp/common/lib/csp/MirrorAbstract.py +++ b/opencsp/common/lib/csp/MirrorAbstract.py @@ -307,7 +307,7 @@ def draw( # Draw surface boundary if mirror_style.point_styles is not None: mirror_style.point_styles.markersize = 0 - edge_values_lifted.draw_list(view, style=mirror_style.point_styles) + edge_values_lifted.draw_line(view, style=mirror_style.point_styles) # Draw surface normals if mirror_style.surface_normals: diff --git a/opencsp/common/lib/csp/MirrorPoint.py b/opencsp/common/lib/csp/MirrorPoint.py index a3f66dc73..f4656f98e 100644 --- a/opencsp/common/lib/csp/MirrorPoint.py +++ b/opencsp/common/lib/csp/MirrorPoint.py @@ -176,7 +176,7 @@ def draw(self, view: View3d, mirror_style: RenderControlMirror, transform: Trans p_space = self.location_in_space(domain) # Draw sample points - p_space.draw_point(view, style=mirror_style.point_styles) + p_space.draw_points(view, style=mirror_style.point_styles) # Calculate z height of boundary to draw (lowest z value) min_val = min(self.surface_displacement_at(domain)) @@ -189,7 +189,7 @@ def draw(self, view: View3d, mirror_style: RenderControlMirror, transform: Trans if mirror_style.point_styles is not None: edge_style = mirror_style.point_styles edge_style.markersize = 0 - edge_values_lifted.draw_list(view, style=edge_style) + edge_values_lifted.draw_line(view, style=edge_style) # Draw surface normals if mirror_style.surface_normals: diff --git a/opencsp/common/lib/csp/SolarField.py b/opencsp/common/lib/csp/SolarField.py index 6c28cb13a..bcf464856 100644 --- a/opencsp/common/lib/csp/SolarField.py +++ b/opencsp/common/lib/csp/SolarField.py @@ -318,7 +318,7 @@ def draw( # Draw Origin if solar_field_style.draw_origin: - Pxyz(origin).draw_point(view) + origin.draw_points(view) # Heliostats. if solar_field_style.draw_heliostats: diff --git a/opencsp/common/lib/geometry/Intersection.py b/opencsp/common/lib/geometry/Intersection.py index ee6041b0a..2870c3a48 100644 --- a/opencsp/common/lib/geometry/Intersection.py +++ b/opencsp/common/lib/geometry/Intersection.py @@ -284,9 +284,9 @@ def _Pxy_to_flux_map(points: Pxy, bins: int, resolution_type: str = "pixelX") -> def draw(self, view: View3d, style: RenderControlPointSeq = None): if style is None: style = RenderControlPointSeq() - self.intersection_points.draw_point(view, style) + self.intersection_points.draw_points(view, style) def draw_subset(self, view: View3d, count: int, points_style: RenderControlPointSeq = None): for i in np.floor(np.linspace(0, len(self.intersection_points) - 1, count)): p = Pxyz(self.intersection_points[int(i)]) - p.draw_point(view) + p.draw_points(view) diff --git a/opencsp/common/lib/geometry/Pxyz.py b/opencsp/common/lib/geometry/Pxyz.py index c647954d3..3aa72ae2f 100644 --- a/opencsp/common/lib/geometry/Pxyz.py +++ b/opencsp/common/lib/geometry/Pxyz.py @@ -23,19 +23,3 @@ def as_Vxyz(self): @classmethod def empty(cls): return Pxyz([[], [], []]) - - def draw_point( - self, - figure: rcfr.RenderControlFigureRecord | v3d.View3d, - style: rcps.RenderControlPointSeq = None, - labels: list[str] = None, - ): - """Calls figure.draw_xyz(p) for all points in this instance, and with - the default arguments in place for any None's.""" - if style is None: - style = rcps.default(markersize=2) - if labels is None: - labels = [None] * len(self) - view = figure if isinstance(figure, v3d.View3d) else figure.view - for x, y, z, label in zip(self.x, self.y, self.z, labels): - view.draw_xyz((x, y, z), style, label) diff --git a/opencsp/common/lib/geometry/Vxyz.py b/opencsp/common/lib/geometry/Vxyz.py index d50da7767..16b90f25f 100644 --- a/opencsp/common/lib/geometry/Vxyz.py +++ b/opencsp/common/lib/geometry/Vxyz.py @@ -498,14 +498,18 @@ def origin(cls): # zs.append(func(x, y)) # return cls([xs, ys, zs]) - def draw_list( + def draw_line( self, figure: rcfr.RenderControlFigureRecord | v3d.View3d, close: bool = None, style: rcps.RenderControlPointSeq = None, label: str = None, ) -> None: - """Calls figure.draw_xyz_list(self.data.T) with the default arguments in place for any None's.""" + """ + Calls figure.draw_xyz_list(self.data.T) to draw all xyz points in a + single series. Uses the default arguments for draw_xyz_list in place of + any None's. + """ kwargs = dict() for key, val in [('close', close), ('style', style), ('label', label)]: if val is not None: @@ -513,3 +517,19 @@ def draw_list( view = figure if isinstance(figure, v3d.View3d) else figure.view view.draw_xyz_list(self.data.T, **kwargs) + + def draw_points( + self, + figure: rcfr.RenderControlFigureRecord | v3d.View3d, + style: rcps.RenderControlPointSeq = None, + labels: list[str] = None, + ): + """ + Calls figure.draw_xyz(p) to draw all xyz points in this instance + individually. Uses the default arguments in place for any None's. + """ + if labels is None: + labels = [None] * len(self) + view = figure if isinstance(figure, v3d.View3d) else figure.view + for x, y, z, label in zip(self.x, self.y, self.z, labels): + view.draw_xyz((x, y, z), style, label) diff --git a/opencsp/common/lib/render/View3d.py b/opencsp/common/lib/render/View3d.py index 039963cd9..b079514c6 100644 --- a/opencsp/common/lib/render/View3d.py +++ b/opencsp/common/lib/render/View3d.py @@ -653,13 +653,24 @@ def draw_xyz_list( style: rcps.RenderControlPointSeq = None, label: str = None, ) -> None: - """Draw lines or closed polygons. + """ + Draw lines or closed polygons. Parameters ---------- - input_xyz_list: List of xyz three vectors (eg [[0,0,0], [1,1,1]]) - close: Draw as a closed polygon (ignored if input_xyz_list < 3 points)""" - + input_xyz_list : Iterable[tuple[float, float, float]] + List of xyz three vectors (eg [[0,0,0], [1,1,1]]). The vectors must + be indexable, such as with input_xyz_list[0][0]. + close : bool, optional + True to draw as a closed polygon (ignored if input_xyz_list < 3 + points), or False to draw as given. By default False. + style : rcps.RenderControlPointSeq, optional + The style with which to render, or None for + RenderControlPointSeq.default(). By default None. + label : str, optional + The label to assign to this plot to be used in the legend, or None + for no label. By default None. + """ if style == None: style = rcps.default() diff --git a/opencsp/common/lib/test/test_FluxMaps.py b/opencsp/common/lib/test/test_FluxMaps.py index 543ff00a9..a2fe6cd9c 100644 --- a/opencsp/common/lib/test/test_FluxMaps.py +++ b/opencsp/common/lib/test/test_FluxMaps.py @@ -175,9 +175,9 @@ def square(z: float): mirror.draw(fig_record.view, mirror_style) trace_style = rcrt.init_current_lengths(current_len=6) trace.draw(fig_record.view, trace_style) - square(4).draw_list(fig_record.view, close=True, style=rcps.RenderControlPointSeq(color='b', marker=',')) - square(5).draw_list(fig_record.view, close=True, style=rcps.RenderControlPointSeq(color='g', marker=',')) - square(6).draw_list(fig_record.view, close=True, style=rcps.RenderControlPointSeq(color='r', marker=',')) + square(4).draw_line(fig_record.view, close=True, style=rcps.RenderControlPointSeq(color='b', marker=',')) + square(5).draw_line(fig_record.view, close=True, style=rcps.RenderControlPointSeq(color='g', marker=',')) + square(6).draw_line(fig_record.view, close=True, style=rcps.RenderControlPointSeq(color='r', marker=',')) self.show_save_and_check_figure(fig_record) # Draw z=4 diff --git a/opencsp/common/lib/test/test_RayTraceOutput.py b/opencsp/common/lib/test/test_RayTraceOutput.py index cbaa06c36..241de57cf 100644 --- a/opencsp/common/lib/test/test_RayTraceOutput.py +++ b/opencsp/common/lib/test/test_RayTraceOutput.py @@ -442,14 +442,14 @@ def fn_5w1(x, y): def _draw_helper(view: View3d) -> None: sf1.draw(view, solar_field_style) trace.draw(view, trace_control) - aimpoint_xyz.draw_point(view, style=rcps.marker(color='tab:orange')) + aimpoint_xyz.draw_points(view, style=rcps.marker(color='tab:orange')) # debug heliostat_origin = sf1.heliostats[0].self_to_global_tranformation.apply(Pxyz.origin()) pointing_vector = st.tracking_surface_normal_xyz( heliostat_origin, aimpoint_xyz, lln.NSTTF_ORIGIN, when_ymdhmsz ) - Vxyz.merge([heliostat_origin, heliostat_origin + pointing_vector * 10]).draw_list(view) + Vxyz.merge([heliostat_origin, heliostat_origin + pointing_vector * 10]).draw_line(view) # debug self.show_save_and_check_figure(fig_record) @@ -573,7 +573,7 @@ def test_partial_field_trace(self) -> None: trace = rt.trace_scene(scene, Resolution.center(), verbose=False) trace.draw(view, RenderControlRayTrace(RenderControlLightPath(15, 200))) - aimpoint_xyz.draw_point(view, rcps.RenderControlPointSeq(color='orange', marker='.')) + aimpoint_xyz.draw_points(view, rcps.RenderControlPointSeq(color='orange', marker='.')) self.show_save_and_check_figure(fig_record) diff --git a/opencsp/common/lib/test/test_SolarFieldOutput.py b/opencsp/common/lib/test/test_SolarFieldOutput.py index 5373b1cbb..6fc5948d1 100644 --- a/opencsp/common/lib/test/test_SolarFieldOutput.py +++ b/opencsp/common/lib/test/test_SolarFieldOutput.py @@ -503,7 +503,7 @@ def old_solar_field_h_outlines(self) -> None: code_tag=self.code_tag, ) solar_field.draw(fig_record.view, solar_field_style) - aimpoint_xyz.draw_list(fig_record.view, style=rcps.marker(color='tab:orange'), label='aimpoint_xyz') + aimpoint_xyz.draw_line(fig_record.view, style=rcps.marker(color='tab:orange'), label='aimpoint_xyz') # Output. self.show_save_and_check_figure(fig_record) @@ -779,7 +779,7 @@ def test_heliostat_vector_field(self) -> None: comments=comments, code_tag=self.code_tag, ) - aimpoint_xyz.draw_point(fig_record.view, style=rcps.marker(color='tab:orange'), labels='aimpoint_xyz') + aimpoint_xyz.draw_points(fig_record.view, style=rcps.marker(color='tab:orange'), labels='aimpoint_xyz') solar_field.draw(fig_record.view, solar_field_style) self.show_save_and_check_figure(fig_record) @@ -838,7 +838,7 @@ def test_dense_vector_field(self) -> None: code_tag=self.code_tag, ) solar_field.draw(fig_record.view, solar_field_style) - aimpoint_xyz.draw_point(fig_record.view, style=rcps.marker(color='tab:orange'), labels='aimpoint_xyz') + aimpoint_xyz.draw_points(fig_record.view, style=rcps.marker(color='tab:orange'), labels='aimpoint_xyz') # Draw dense vector field. grid_xy = solar_field.heliostat_field_regular_grid_xy(40, 20) From 8a02415f32561e7a7f0f1721a70bfde4920ac883 Mon Sep 17 00:00:00 2001 From: bbean Date: Fri, 6 Sep 2024 14:08:24 -0600 Subject: [PATCH 6/9] fix test_MirrorOutput --- opencsp/common/lib/csp/FacetEnsemble.py | 10 +++------- opencsp/common/lib/test/test_MirrorOutput.py | 6 +----- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/opencsp/common/lib/csp/FacetEnsemble.py b/opencsp/common/lib/csp/FacetEnsemble.py index 17eabb5f2..93fdf33a8 100644 --- a/opencsp/common/lib/csp/FacetEnsemble.py +++ b/opencsp/common/lib/csp/FacetEnsemble.py @@ -1,6 +1,5 @@ """Rigid ensemble of facets""" -import copy from typing import Callable from warnings import warn import numpy as np @@ -10,17 +9,14 @@ from opencsp.common.lib.csp.VisualizeOrthorectifiedSlopeAbstract import VisualizeOrthorectifiedSlopeAbstract from opencsp.common.lib.csp.Facet import Facet from opencsp.common.lib.csp.RayTraceable import RayTraceable -from opencsp.common.lib.geometry.LoopXY import LoopXY from opencsp.common.lib.geometry.Pxy import Pxy from opencsp.common.lib.geometry.Pxyz import Pxyz -from opencsp.common.lib.geometry.RegionXY import RegionXY, Resolution +from opencsp.common.lib.geometry.RegionXY import Resolution from opencsp.common.lib.geometry.TransformXYZ import TransformXYZ from opencsp.common.lib.geometry.Vxyz import Vxyz from opencsp.common.lib.render.View3d import View3d -from opencsp.common.lib.render_control.RenderControlFacet import RenderControlFacet from opencsp.common.lib.render_control.RenderControlFacetEnsemble import RenderControlFacetEnsemble -from opencsp.common.lib.render_control.RenderControlMirror import RenderControlMirror -from opencsp.common.lib.render_control.RenderControlPointSeq import RenderControlPointSeq +import opencsp.common.lib.render_control.RenderControlPointSeq as rcps class FacetEnsemble(RayTraceable, VisualizeOrthorectifiedSlopeAbstract, OpticOrientationAbstract): @@ -182,7 +178,7 @@ def draw( # origin of the facet ensemble if facet_ensemble_style.draw_centroid: - origin.draw_points(view) + origin.draw_points(view, style=rcps.marker()) # pointing vector of the facet ensemble if facet_ensemble_style.draw_normal_vector: diff --git a/opencsp/common/lib/test/test_MirrorOutput.py b/opencsp/common/lib/test/test_MirrorOutput.py index 01070b419..1548cc4b5 100644 --- a/opencsp/common/lib/test/test_MirrorOutput.py +++ b/opencsp/common/lib/test/test_MirrorOutput.py @@ -6,7 +6,6 @@ import datetime from typing import Callable -import matplotlib import numpy as np import pytz from scipy.spatial.transform import Rotation @@ -14,7 +13,7 @@ from opencsp.common.lib.csp.LightSourceSun import LightSourceSun from opencsp.common.lib.csp.Scene import Scene from opencsp.common.lib.geometry.Pxyz import Pxyz -from opencsp.common.lib.geometry.RegionXY import RegionXY, Resolution +from opencsp.common.lib.geometry.RegionXY import RegionXY from opencsp.common.lib.geometry.Pxy import Pxy from opencsp.common.lib.geometry.TransformXYZ import TransformXYZ @@ -22,13 +21,11 @@ import opencsp.common.lib.opencsp_path.data_path_for_test as dpft import opencsp.common.lib.render.figure_management as fm import opencsp.common.lib.render.view_spec as vs -import opencsp.common.lib.render_control.RenderControlEnsemble as rce import opencsp.common.lib.render_control.RenderControlFacet as rcf import opencsp.common.lib.render_control.RenderControlHeliostat as rch import opencsp.common.lib.render_control.RenderControlMirror as rcm import opencsp.common.lib.render_control.RenderControlSolarField as rcsf import opencsp.common.lib.render_control.RenderControlLightPath as rclp -import opencsp.common.lib.render_control.RenderControlRayTrace as rcrt import opencsp.common.lib.test.TestOutput as to import opencsp.common.lib.tool.log_tools as lt import opencsp.common.lib.tool.string_tools as st @@ -39,7 +36,6 @@ from opencsp.common.lib.csp.SolarField import SolarField import opencsp.common.lib.render_control.RenderControlFacetEnsemble as rcfe import opencsp.common.lib.geo.lon_lat_nsttf as lln -import opencsp.common.lib.csp.RayTrace as rt PI = np.pi From 762d1c3c84d7ce73ca730802f112e653f3e25926 Mon Sep 17 00:00:00 2001 From: bbean Date: Mon, 9 Sep 2024 11:08:28 -0600 Subject: [PATCH 7/9] fix test_SolarFieldOutput.test_dense_vector_field() by changing FacetEnsemble origin size --- opencsp/common/lib/csp/FacetEnsemble.py | 2 +- opencsp/common/lib/test/test_SolarFieldOutput.py | 13 ------------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/opencsp/common/lib/csp/FacetEnsemble.py b/opencsp/common/lib/csp/FacetEnsemble.py index 93fdf33a8..9eb9cda07 100644 --- a/opencsp/common/lib/csp/FacetEnsemble.py +++ b/opencsp/common/lib/csp/FacetEnsemble.py @@ -178,7 +178,7 @@ def draw( # origin of the facet ensemble if facet_ensemble_style.draw_centroid: - origin.draw_points(view, style=rcps.marker()) + origin.draw_points(view, style=rcps.marker(markersize=1)) # pointing vector of the facet ensemble if facet_ensemble_style.draw_normal_vector: diff --git a/opencsp/common/lib/test/test_SolarFieldOutput.py b/opencsp/common/lib/test/test_SolarFieldOutput.py index 6fc5948d1..b3dd17956 100644 --- a/opencsp/common/lib/test/test_SolarFieldOutput.py +++ b/opencsp/common/lib/test/test_SolarFieldOutput.py @@ -5,39 +5,26 @@ """ -import os -from datetime import datetime - -import matplotlib.pyplot as plt import numpy as np import opencsp.common.lib.csp.SolarField as sf import opencsp.common.lib.csp.sun_track as sun_track # "st" is taken by string_tools. import opencsp.common.lib.geo.lon_lat_nsttf as lln import opencsp.common.lib.opencsp_path.data_path_for_test as dpft -import opencsp.common.lib.opencsp_path.opencsp_root_path as orp import opencsp.common.lib.render.figure_management as fm import opencsp.common.lib.render.view_spec as vs -import opencsp.common.lib.render_control.RenderControlAxis as rca -import opencsp.common.lib.render_control.RenderControlEnsemble as rce import opencsp.common.lib.render_control.RenderControlFacet as rcf import opencsp.common.lib.render_control.RenderControlFacetEnsemble as rcfe -import opencsp.common.lib.render_control.RenderControlFigure as rcfg import opencsp.common.lib.render_control.RenderControlHeliostat as rch import opencsp.common.lib.render_control.RenderControlPointSeq as rcps import opencsp.common.lib.render_control.RenderControlSolarField as rcsf -import opencsp.common.lib.test.support_test as stest import opencsp.common.lib.test.TestOutput as to -import opencsp.common.lib.tool.file_tools as ft import opencsp.common.lib.tool.log_tools as lt from opencsp.common.lib.csp.HeliostatAzEl import HeliostatAzEl from opencsp.common.lib.csp.HeliostatConfiguration import HeliostatConfiguration from opencsp.common.lib.csp.SolarField import SolarField from opencsp.common.lib.geometry.Pxyz import Pxyz from opencsp.common.lib.geometry.Vxyz import Vxyz -from opencsp.common.lib.render_control.RenderControlAxis import RenderControlAxis -from opencsp.common.lib.render_control.RenderControlFigure import RenderControlFigure -from opencsp.common.lib.render_control.RenderControlFigureRecord import RenderControlFigureRecord UP = Vxyz([0, 0, 1]) NORTH = Vxyz([0, 1, 0]) From 870d059cdfa97ba4c53e814c279233afe8d214ab Mon Sep 17 00:00:00 2001 From: bbean Date: Mon, 21 Oct 2024 11:19:18 -0600 Subject: [PATCH 8/9] black --- opencsp/common/lib/csp/Facet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opencsp/common/lib/csp/Facet.py b/opencsp/common/lib/csp/Facet.py index 43cdf1b3c..c76cea258 100644 --- a/opencsp/common/lib/csp/Facet.py +++ b/opencsp/common/lib/csp/Facet.py @@ -204,4 +204,4 @@ def draw(self, view: View3d, facet_style: RenderControlFacet = None, transform: view.draw_xyz_text(origin.data.T[0], self.name, style=facet_style.name_style) if facet_style.draw_mirror_curvature: - self.mirror.draw(view, facet_style.mirror_styles, transform * self.mirror._self_to_parent_transform) \ No newline at end of file + self.mirror.draw(view, facet_style.mirror_styles, transform * self.mirror._self_to_parent_transform) From 2f1dfee0d8d90eb6c9df1575b15f7b1a389a73fd Mon Sep 17 00:00:00 2001 From: bbean Date: Tue, 5 Nov 2024 09:46:45 -0700 Subject: [PATCH 9/9] better comments and type hints, removed commented out code --- opencsp/common/lib/geometry/Vxyz.py | 217 +++++++++++++++++++--------- opencsp/common/lib/render/View3d.py | 8 +- 2 files changed, 155 insertions(+), 70 deletions(-) diff --git a/opencsp/common/lib/geometry/Vxyz.py b/opencsp/common/lib/geometry/Vxyz.py index 16b90f25f..d7971879b 100644 --- a/opencsp/common/lib/geometry/Vxyz.py +++ b/opencsp/common/lib/geometry/Vxyz.py @@ -14,12 +14,24 @@ class Vxyz: + """ + 3D vector class to represent 3D points/vectors. Contains N 3D vectors where len == N. + + The values for the contained vectors can be retrieved with + :py:meth:`data`(), or individual vectors can be retrieved with the indexing + or x/y/z methods. For example, the following can both be used to get the first contained vector:: + + vec = v3.Vxyz([[0, 1, 2], [3, 4, 5], [6, 7, 8]]) + v0 = Vxyz([vec.x()[0], vec.y()[0], vec.z()[0]]) + # v0 == Vxyz([0, 3, 6]) + v0 = vec[0] + # v0 == Vxyz([0, 3, 6]) + """ + def __init__( self, data: Union[np.ndarray, tuple[float, float, float], tuple[list, list, list], Vxy, "Vxyz"], dtype=float ): """ - 3D vector class to represent 3D points/vectors. - To represent a single vector:: x = 1 @@ -74,14 +86,14 @@ def __init__( self._data = np.array(data, dtype=dtype).reshape((3, -1)) @property - def data(self): + def data(self) -> np.ndarray: """ An array with shape (3, N), where N is the number of 3D vectors in this instance. """ return self._data @property - def dtype(self): + def dtype(self) -> np.dtype: return self._data.dtype @property @@ -96,11 +108,28 @@ def y(self) -> np.ndarray: def z(self) -> np.ndarray: return self._data[2, :] - def as_Vxyz(self): + def as_Vxyz(self) -> "Vxyz": + """ + Returns + ------- + Vxyz + This instance + """ return self @classmethod - def _from_data(cls, data, dtype=float): + def _from_data(cls, data, dtype=None) -> "Vxyz": + """ + Builds a new instance with the given data and data type. + + Parameters + ---------- + data : array-like | list-like + The data to build this class with. Acceptable input types are + enumerated in the constructor type hints. + dtype : Literal[float] | Literal[int], optional + The data type used for representing the input data, by default float + """ return cls(data, dtype) @classmethod @@ -118,27 +147,31 @@ def _check_is_Vxyz(self, v_in): Checks if input data is instance of Vxyz for methods that require this type. + Raises + ------ + TypeError: + If the input v_in is not a Vxyz type object. """ if not isinstance(v_in, Vxyz): raise TypeError(f'Input operand must be {Vxyz}, not {type(v_in)}') def __add__(self, v_in): """ - Element wise addition. Operand 1 type must be Vxyz + Element wise addition. Operand 1 type must be Vxyz. Returns a new Vxyz. """ self._check_is_Vxyz(v_in) return self._from_data(self._data + v_in.data) def __sub__(self, v_in): """ - Element wise subtraction. Operand 1 type must be Vxyz + Element wise subtraction. Operand 1 type must be Vxyz. Returns a new Vxyz. """ self._check_is_Vxyz(v_in) return self._from_data(self._data - v_in.data) def __mul__(self, data_in): """ - Element wise addition. Operand 1 type must be int, float, or Vxyz. + Element wise multiplication. Operand 1 type must be int, float, or Vxyz. Returns a new Vxyz. """ if type(data_in) in [int, float, np.float32, np.float64, np.int32, np.int64]: return self._from_data(self._data * data_in) @@ -167,13 +200,17 @@ def __neg__(self): def _magnitude_with_zero_check(self) -> np.ndarray: """ - Returns ndarray of normalized vector data. + Returns magnitude of each vector as a new array. Returns ------- ndarray - 1d vector of normalized data + 1d vector of normalized data. Shape is (n). + Raises + ------ + ValueError: + If the magnitude of any of the contained vectors is 0. """ mag = self.magnitude() @@ -182,14 +219,14 @@ def _magnitude_with_zero_check(self) -> np.ndarray: return mag - def normalize(self): + def normalize(self) -> "Vxyz": """ - Returns copy of normalized vector. + Creates a copy of this instance and normalizes it. Returns ------- Vxyz - Normalized vector. + Normalized vector copy with the same shape. """ V_out = self._from_data(self._data.copy()) @@ -205,20 +242,20 @@ def normalize_in_place(self) -> None: def magnitude(self) -> npt.NDArray[np.float_]: """ - Returns magnitude of each vector. + Returns magnitude of each vector as a new array. Returns ------- np.ndarray - Length n ndarray of vector magnitudes. + Vector magnitudes copy. Shape is (n). """ return np.sqrt(np.sum(self._data**2, 0)) - def rotate(self, R: Rotation): + def rotate(self, R: Rotation) -> "Vxyz": """ - Returns a copy of the rotated vector rotated about the coordinate - system origin. + Returns a copy of the rotated vector rotated about the coordinate system + origin. The rotation is applied to each of the contained 3d coordinates. Parameters ---------- @@ -228,33 +265,35 @@ def rotate(self, R: Rotation): Returns ------- Vxyz - Rotated vector. + Rotated vector copy. """ # Check inputs if not isinstance(R, Rotation): - raise TypeError(f'Rotaion must be type {Rotation}, not {type(R)}') + raise TypeError(f'Rotation must be type {Rotation}, not {type(R)}') V_out = self._from_data(self._data.copy()) V_out.rotate_in_place(R) return V_out - def rotate_about(self, R: Rotation, V_pivot): + def rotate_about(self, R: Rotation, V_pivot: "Vxyz") -> "Vxyz": """ Returns a copy of the rotated vector rotated about the given pivot - point. + point. The rotation is applied to each of the contained 3d coordinates. Parameters ---------- R : Rotation Rotation object to apply to vector. V_pivot : Vxyz - Pivot point to rotate about. + Pivot point to rotate about. Must broadcast with the size of this + instance (must have length 1 or N, where N is the length of this + instance). Returns ------- Vxyz - Rotated vector. + Rotated vector copy. """ # Check inputs @@ -269,7 +308,8 @@ def rotate_about(self, R: Rotation, V_pivot): def rotate_in_place(self, R: Rotation) -> None: """ Rotates vector about the coordinate system origin. Replaces data in Vxyz - object with rotated data. + object with rotated data. The rotation is applied to each of the + contained 3d coordinates. Parameters ---------- @@ -287,17 +327,20 @@ def rotate_in_place(self, R: Rotation) -> None: self._data = R.apply(self._data.T).T - def rotate_about_in_place(self, R: Rotation, V_pivot) -> None: + def rotate_about_in_place(self, R: Rotation, V_pivot: "Vxyz") -> None: """ Rotates about the given pivot point. Replaces data in Vxyz object with - rotated data. + rotated data. The rotation is applied to each of the contained 3d + coordinates. Parameters ---------- R : Rotation Rotation object to apply to vector. V_pivot : Vxyz - Pivot point to rotate about. + Pivot point to rotate about. Must broadcast with the size of this + instance (must have length 1 or N, where N is the length of this + instance). Returns ------- @@ -316,7 +359,7 @@ def rotate_about_in_place(self, R: Rotation, V_pivot) -> None: # Recenter pivot point self._data += V_pivot.data - def dot(self, V) -> np.ndarray: + def dot(self, V: "Vxyz") -> np.ndarray: """ Calculated dot product. Size of input data must broadcast with size of data. @@ -324,12 +367,14 @@ def dot(self, V) -> np.ndarray: Parameters ---------- V : Vxyz - Input vector. + Input vector to compute the dot product with. Must broadcast with + the size of this instance (must have length 1 or N, where N is the + length of this instance). Returns ------- np.ndarray - Length n array of dot product values. + Array of dot product values with shape (N). """ # Check inputs @@ -337,7 +382,7 @@ def dot(self, V) -> np.ndarray: return (self._data * V.data).sum(axis=0) - def cross(self, V): + def cross(self, V: "Vxyz") -> "Vxyz": """ Calculates cross product. Operands 0 and 1 must have data sizes that can broadcast together. @@ -345,12 +390,15 @@ def cross(self, V): Parameters ---------- V : Vxyz - Input vector. + Input vector to computer the cross product with. Must broadcast with + the size of this instance (must have length 1 or N, where N is the + length of this instance). Returns ------- Vxyz - Cross product. + Cross product copy with shape (P), where O is the length of the + input V and P is the greater of N and O. """ # Check inputs @@ -361,14 +409,26 @@ def cross(self, V): # Calculate return self._from_data(np.cross(self._data.T, V.data.T).T) - def align_to(self, V) -> Rotation: + def align_to(self, V: "Vxyz") -> Rotation: """ Calculate shortest rotation that aligns current vector to input vector. + Both vectors must have length 1. The returned rotation can be applied to + the current vector so that it then aligns with the input vector. For + example:: + + vec = Vxyz([1, 2, 3]) + R = vec.align_to(Vxyz([1, 0, 0])) + vec_r = vec.rotate(R) + vec_r_n = vec_r.normalize() + + # vec.magnitude() == 3.74165739 + # vec_r == [ 3.74165739, -4.44089210e-16, -2.22044605e-16 ] + # vec_r_n == [ 1.00000000, -1.18687834e-16, -5.93439169e-17 ] Parameters ---------- V : Vxyz - 3D vector to align current vector to. + 3D vector to align current vector to. Must have length 1. Returns ------- @@ -394,7 +454,9 @@ def align_to(self, V) -> Rotation: return Rotation.from_matrix(Rmat) def concatenate(self, V: 'Vxyz') -> 'Vxyz': - """Concatenates Vxyz to end of current vector. + """ + Concatenates Vxyz to the end of current vector. Returns a copy as the + new vector. Parameters ---------- @@ -404,7 +466,8 @@ def concatenate(self, V: 'Vxyz') -> 'Vxyz': Returns ------- Vxyz - Concatenated vector + Concatenated vector copy. Shape is (3,N+O), where O is the length of + the input vector V. """ x = np.concatenate((self.x, V.x)) y = np.concatenate((self.y, V.y)) @@ -416,27 +479,32 @@ def copy(self) -> 'Vxyz': return Vxyz(self.data.copy()) def projXY(self) -> Vxy: - """Returns the x and y components of self as a Vxy + """Returns the x and y components of self as a Vxy. + + The components are not a view via indexing but rather a copy. - The components are deep copied. + Returns + ------- + Vxy + Output XY points as a new vector. Shape is (2,N). """ return Vxy([self.x.copy(), self.y.copy()]) @classmethod def from_lifted_points(cls, v: Vxy, func: Callable) -> 'Vxyz': - """Returns Vxyz from a Vxy and a function of form: z = func(x, y) + """Returns Vxyz from a Vxy and a function of form: z = func(x, y). Parameters ---------- v : Vxy - X/Y points + X/Y points with shape (2,N). func : Callable Z coordinate function of form z = func(x, y) Returns ------- Vxyz - Output XYZ points + Output XYZ points as a new vector. Shape is (3,N). """ zs = [] for x, y in zip(v.x, v.y): @@ -455,12 +523,12 @@ def empty(cls): return Vxyz([[], [], []]) def hasnan(self): - """returns True if there is a nan in self\n - Note: this method exists because: - ```python - >>> isinstance(np.nan, numbers.Number) - True - ```""" + """Returns True if there is a single NaN in the current vector. + + Note: this method exists because of the unintuitive behavior of isinstance in Python:: + + isinstance(np.nan, numbers.Number) # True + """ return np.isnan(self.data).any() @classmethod @@ -486,18 +554,6 @@ def merge(cls, V_list: list['Vxyz']): def origin(cls): return cls([0, 0, 0]) - # @classmethod - # def lift(cls, v: Vxy.Vxy, func: Callable): - # """Takes in a Vxy and and Callable that takes in 2 arguments. - # Returns the Vxyz where the z values correspond to the outputs of the x and y values.""" - - # xs = copy.deepcopy(v.x) - # ys = copy.deepcopy(v.y) - # zs = [] - # for x, y in zip(xs, ys): - # zs.append(func(x, y)) - # return cls([xs, ys, zs]) - def draw_line( self, figure: rcfr.RenderControlFigureRecord | v3d.View3d, @@ -507,8 +563,22 @@ def draw_line( ) -> None: """ Calls figure.draw_xyz_list(self.data.T) to draw all xyz points in a - single series. Uses the default arguments for draw_xyz_list in place of - any None's. + single series. Uses the default arguments for + :py:meth:`View3d.draw_xyz_list` in place of any None arguments. + + Parameters + ---------- + figure : rcfr.RenderControlFigureRecord | v3d.View3d + The figure to draw to. + close : bool, optional + True to add the first point again at the end of the plot, thereby + drawing this set of points as a closed polygon. None or False to not + add another point at the end (draw_xyz_list's default) + style : rcps.RenderControlPointSeq, optional + The style to use for the points and lines, or None for + :py:method:`RenderControlPointSequence.default`(). + label : str, optional + A string used to label this plot in the legend, or None for no label. """ kwargs = dict() for key, val in [('close', close), ('style', style), ('label', label)]: @@ -523,10 +593,25 @@ def draw_points( figure: rcfr.RenderControlFigureRecord | v3d.View3d, style: rcps.RenderControlPointSeq = None, labels: list[str] = None, - ): + ) -> None: """ Calls figure.draw_xyz(p) to draw all xyz points in this instance - individually. Uses the default arguments in place for any None's. + individually. Uses the default arguments for :py:meth:`View3d.draw_xyz` + in place of any None arguments. + + Parameters + ---------- + figure : rcfr.RenderControlFigureRecord | v3d.View3d + The figure to draw to. + close : bool, optional + True to add the first point again at the end of the plot, thereby + drawing this set of points as a closed polygon. None or False to not + add another point at the end (draw_xyz_list's default). + style : rcps.RenderControlPointSeq, optional + The style to use for the points and lines, or None for + :py:method:`RenderControlPointSequence.default`(). + label : str, optional + A string used to label this plot in the legend, or None for no label. """ if labels is None: labels = [None] * len(self) diff --git a/opencsp/common/lib/render/View3d.py b/opencsp/common/lib/render/View3d.py index e1d70d060..674a6dfc7 100644 --- a/opencsp/common/lib/render/View3d.py +++ b/opencsp/common/lib/render/View3d.py @@ -705,8 +705,8 @@ def draw_xyz( """ Plots one or more points. - This is similar to draw_xyz_list, except that it accepts the point - locations in a different format. Example usage:: + This is similar to :py:meth:`draw_xyz_list`, except that it accepts the + point locations in a different format. Example usage:: # viewspec xy or pq draw_xyz((0, 1)) @@ -770,8 +770,8 @@ def draw_xyz_list( the end of the input list as a new last value. Ignored if input_xyz_list < 3 points. Default is False. style: RenderControlPointSeq | None, optional - The style with which to render the input, or None for - rcps.default(). Default is None. + The style with which to render the input, or None for rcps.default() + (blue, marker '.', line style '-'). Default is None. label: str | None, optional The label used for this graph in the legend, or None to be excluded from the legend. Default is None.