luminarycloud 0.17.0__py3-none-any.whl → 0.18.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.
Files changed (54) hide show
  1. luminarycloud/_client/client.py +3 -0
  2. luminarycloud/_helpers/_create_geometry.py +134 -32
  3. luminarycloud/_helpers/cond.py +0 -1
  4. luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2.py +146 -123
  5. luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2.pyi +81 -8
  6. luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2_grpc.py +34 -0
  7. luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2_grpc.pyi +12 -0
  8. luminarycloud/_proto/api/v0/luminarycloud/inference/inference_pb2.py +8 -8
  9. luminarycloud/_proto/api/v0/luminarycloud/inference/inference_pb2.pyi +12 -7
  10. luminarycloud/_proto/api/v0/luminarycloud/pipelines/pipelines_pb2.py +246 -0
  11. luminarycloud/_proto/api/v0/luminarycloud/pipelines/pipelines_pb2.pyi +420 -0
  12. luminarycloud/_proto/api/v0/luminarycloud/pipelines/pipelines_pb2_grpc.py +240 -0
  13. luminarycloud/_proto/api/v0/luminarycloud/pipelines/pipelines_pb2_grpc.pyi +90 -0
  14. luminarycloud/_proto/api/v0/luminarycloud/project/project_pb2.py +54 -3
  15. luminarycloud/_proto/api/v0/luminarycloud/project/project_pb2.pyi +92 -1
  16. luminarycloud/_proto/api/v0/luminarycloud/project/project_pb2_grpc.py +132 -0
  17. luminarycloud/_proto/api/v0/luminarycloud/project/project_pb2_grpc.pyi +40 -0
  18. luminarycloud/_proto/api/v0/luminarycloud/simulation_template/simulation_template_pb2.py +48 -26
  19. luminarycloud/_proto/api/v0/luminarycloud/simulation_template/simulation_template_pb2.pyi +30 -2
  20. luminarycloud/_proto/api/v0/luminarycloud/simulation_template/simulation_template_pb2_grpc.py +36 -0
  21. luminarycloud/_proto/api/v0/luminarycloud/simulation_template/simulation_template_pb2_grpc.pyi +18 -0
  22. luminarycloud/_proto/client/simulation_pb2.py +261 -251
  23. luminarycloud/_proto/client/simulation_pb2.pyi +35 -2
  24. luminarycloud/_proto/frontend/output/output_pb2.py +24 -24
  25. luminarycloud/_proto/frontend/output/output_pb2.pyi +6 -3
  26. luminarycloud/_proto/geometry/geometry_pb2.py +63 -63
  27. luminarycloud/_proto/geometry/geometry_pb2.pyi +14 -4
  28. luminarycloud/_proto/inferenceservice/inferenceservice_pb2.py +10 -10
  29. luminarycloud/_proto/inferenceservice/inferenceservice_pb2.pyi +12 -7
  30. luminarycloud/enum/geometry_status.py +15 -8
  31. luminarycloud/enum/pipeline_job_status.py +23 -0
  32. luminarycloud/feature_modification.py +3 -1
  33. luminarycloud/geometry.py +12 -0
  34. luminarycloud/geometry_version.py +23 -0
  35. luminarycloud/params/enum/_enum_wrappers.py +29 -0
  36. luminarycloud/params/simulation/physics/fluid/boundary_conditions/inlet/fan_curve_inlet_.py +1 -1
  37. luminarycloud/params/simulation/physics/fluid/boundary_conditions/inlet/mach_inlet_.py +5 -1
  38. luminarycloud/params/simulation/physics/fluid/boundary_conditions/inlet/mass_flow_inlet_.py +5 -1
  39. luminarycloud/params/simulation/physics/fluid/boundary_conditions/inlet/total_pressure_inlet_.py +5 -1
  40. luminarycloud/params/simulation/physics/fluid/boundary_conditions/inlet/velocity_magnitude_inlet_.py +5 -1
  41. luminarycloud/physics_ai/inference.py +46 -30
  42. luminarycloud/pipelines/__init__.py +7 -0
  43. luminarycloud/pipelines/api.py +213 -0
  44. luminarycloud/project.py +65 -5
  45. luminarycloud/simulation_template.py +15 -6
  46. luminarycloud/vis/__init__.py +4 -0
  47. luminarycloud/vis/interactive_inference.py +153 -0
  48. luminarycloud/vis/interactive_scene.py +35 -16
  49. luminarycloud/vis/primitives.py +9 -0
  50. luminarycloud/vis/visualization.py +6 -0
  51. luminarycloud/volume_selection.py +3 -2
  52. {luminarycloud-0.17.0.dist-info → luminarycloud-0.18.0.dist-info}/METADATA +18 -18
  53. {luminarycloud-0.17.0.dist-info → luminarycloud-0.18.0.dist-info}/RECORD +54 -47
  54. {luminarycloud-0.17.0.dist-info → luminarycloud-0.18.0.dist-info}/WHEEL +0 -0
luminarycloud/project.py CHANGED
@@ -6,7 +6,7 @@ import re
6
6
  import uuid
7
7
  from datetime import datetime
8
8
  from os import PathLike, path
9
- from typing import TYPE_CHECKING, Any, Dict, Optional, Union
9
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union, Literal
10
10
 
11
11
  import concurrent
12
12
 
@@ -121,22 +121,22 @@ class Project(ProtoWrapperBase):
121
121
 
122
122
  def create_geometry(
123
123
  self,
124
- cad_file_path: PathLike | str,
124
+ cad_file_path: PathLike | str | List[PathLike | str],
125
125
  *,
126
126
  name: Optional[str] = None,
127
127
  scaling: Optional[float] = None,
128
128
  wait: bool = False,
129
129
  ) -> "Geometry":
130
130
  """
131
- Create a new geometry in the project by uploading a supported CAD file.
131
+ Create a new geometry in the project by uploading supported CAD file(s).
132
132
 
133
133
  For more information on supported formats and best practices, see:
134
134
  https://docs.luminarycloud.com/en/articles/9274255-upload-cad
135
135
 
136
136
  Parameters
137
137
  ----------
138
- cad_file_path : PathLike or str
139
- Path or URL to the CAD file to upload.
138
+ cad_file_path : PathLike | str | List[PathLike | str]
139
+ Path(s) or URL to the CAD file(s) to upload.
140
140
 
141
141
  Other Parameters
142
142
  ----------------
@@ -662,6 +662,66 @@ class Project(ProtoWrapperBase):
662
662
  return None
663
663
  return get_named_variable_set(NamedVariableSetID(res.active_named_variable_set_id))
664
664
 
665
+ def share(self, email: str, role: Literal["viewer", "editor"]) -> None:
666
+ """
667
+ Share the project with a user identified by their email address. This function also allows
668
+ changing the role of the user in the project if the project has already been shared with
669
+ the input user.
670
+
671
+ Parameters
672
+ ----------
673
+ email : str
674
+ Email address of the user to share the project with. It must be an email whose domain
675
+ is registered within the allowed domains settings of your company account.
676
+ role : Literal["viewer", "editor"]
677
+ The role to assign to the user in the project. Must be either "viewer" or "editor".
678
+ """
679
+ if not re.match(r"[^@]+@[^@]+\.[^@]+", email):
680
+ raise ValueError(f"Invalid email address: {email}")
681
+ if role not in ["viewer", "editor"]:
682
+ raise ValueError(f"Invalid role: {role}. Must be 'viewer' or 'editor'.")
683
+ roleModel = (
684
+ projectpb.ShareProjectRequest.USER_ROLE_VIEWER
685
+ if role == "viewer"
686
+ else projectpb.ShareProjectRequest.USER_ROLE_EDITOR
687
+ )
688
+ req = projectpb.ShareProjectRequest(id=self.id, email=email, role=roleModel)
689
+ get_default_client().ShareProject(req)
690
+
691
+ def unshare(self, email: str) -> None:
692
+ """
693
+ Unshare the project with a user identified by their email address.
694
+
695
+ Parameters
696
+ ----------
697
+ email : str
698
+ Email address of the user to unshare the project with. It must be an email whose domain
699
+ must be registered within the allowed domains settings of your company account.
700
+ """
701
+ if not re.match(r"[^@]+@[^@]+\.[^@]+", email):
702
+ raise ValueError(f"Invalid email address: {email}")
703
+ req = projectpb.UnshareProjectRequest(id=self.id, email=email)
704
+ get_default_client().UnshareProject(req)
705
+
706
+ def share_with_support(self, message: str = "") -> None:
707
+ """
708
+ Share the project with Luminary Cloud support.
709
+
710
+ Parameters
711
+ ----------
712
+ message : str, optional
713
+ Message to include with the support share request.
714
+ """
715
+ req = projectpb.ShareProjectWithSupportRequest(id=self.id, message=message)
716
+ get_default_client().ShareProjectWithSupport(req)
717
+
718
+ def unshare_with_support(self) -> None:
719
+ """
720
+ Unshare the project with Luminary Cloud support.
721
+ """
722
+ req = projectpb.UnshareProjectWithSupportRequest(id=self.id)
723
+ get_default_client().UnshareProjectWithSupport(req)
724
+
665
725
 
666
726
  def add_named_variables_from_csv(project: Project, csv_path: str) -> list[NamedVariableSet]:
667
727
  """
@@ -17,7 +17,7 @@ from ._proto.api.v0.luminarycloud.simulation_template import (
17
17
  )
18
18
  from ._proto.client import simulation_pb2 as clientpb
19
19
  from ._wrapper import ProtoWrapper, ProtoWrapperBase
20
- from .types import SimulationTemplateID
20
+ from .types import SimulationTemplateID, ProjectID
21
21
  from .tables import RectilinearTable
22
22
  from .simulation_param import SimulationParam
23
23
  from .outputs import (
@@ -59,7 +59,9 @@ class SimulationTemplate(ProtoWrapperBase):
59
59
  id: SimulationTemplateID
60
60
  "Simulation template ID."
61
61
  name: str
62
- "Simulation name."
62
+ "Simulation template name."
63
+ project_id: ProjectID
64
+ "Project this simulation template belongs to."
63
65
 
64
66
  _proto: simtemplatepb.SimulationTemplate
65
67
 
@@ -71,6 +73,13 @@ class SimulationTemplate(ProtoWrapperBase):
71
73
  def update_time(self) -> datetime:
72
74
  return timestamp_to_datetime(self._proto.update_time)
73
75
 
76
+ def sync_to_ui(self) -> None:
77
+ """
78
+ Sets this simulation template as the one that is used for the "Setup" tab in the UI.
79
+ """
80
+ req = simtemplatepb.SyncSimulationTemplateToUIRequest(id=self.id)
81
+ get_default_client().SyncSimulationTemplateToUI(req)
82
+
74
83
  def update(
75
84
  self,
76
85
  *,
@@ -480,10 +489,10 @@ class SimulationTemplate(ProtoWrapperBase):
480
489
  code = code.replace("SimulationParam", "SimulationTemplate")
481
490
  code += "\n\n\n"
482
491
  code += '# Create a new simulation template or modify the one that is synced with the UI "Setup" tab.\n'
483
- code += "# TODO(USER): Replace ... with project ID, or create a project.\n"
484
- code += 'project = luminarycloud.get_project("...")\n'
485
- code += f"# template = project.create_simulation_template(name={self.name})\n"
486
- code += "template = project.list_simulation_templates()[0] # Setup template\n\n"
492
+ code += f'project = luminarycloud.get_project("{self.project_id}")\n'
493
+ code += f"template = project.create_simulation_template(name={self.name})\n"
494
+ code += '# TODO(USER): To modify the "Setup" template, uncomment the line below and comment out the line above.\n'
495
+ code += "# template = project.list_simulation_templates()[0] # Setup template\n\n"
487
496
 
488
497
  if parameters._table_references:
489
498
  code += "# Upload tabular data.\n"
@@ -48,3 +48,7 @@ from .display import (
48
48
  from .interactive_scene import (
49
49
  InteractiveScene as InteractiveScene,
50
50
  )
51
+
52
+ from .interactive_inference import (
53
+ InteractiveInference as InteractiveInference,
54
+ )
@@ -0,0 +1,153 @@
1
+ # Copyright 2025 Luminary Cloud, Inc. All Rights Reserved.
2
+ import json
3
+ from .._proto.api.v0.luminarycloud.vis import vis_pb2
4
+ from .display import DisplayAttributes
5
+ from luminarycloud.enum.vis_enums import SceneMode, VisQuantity, Representation, FieldComponent
6
+ from luminarycloud.vis import Field
7
+ from typing import TYPE_CHECKING
8
+ import luminarycloud.enum.quantity_type as quantity_type
9
+ from .interactive_scene import InteractiveScene
10
+
11
+ from .._proto.api.v0.luminarycloud.vis import vis_pb2
12
+
13
+ if TYPE_CHECKING:
14
+ from .visualization import Scene, LookAtCamera, DirectionalCamera, ColorMap
15
+ from ..geometry import Geometry
16
+ from ..mesh import Mesh
17
+ from ..solution import Solution
18
+
19
+ try:
20
+ import luminarycloud_jupyter as lcj
21
+
22
+ if TYPE_CHECKING:
23
+ from luminarycloud_jupyter import InteractiveLCVisWidget, LCVisPlaneWidget
24
+ except ImportError:
25
+ lcj = None
26
+
27
+
28
+ _SOURCE_FILTER_ID = "___LC_SOURCE_FILTER___"
29
+
30
+ # Global workspace state configuration
31
+ # We use this to provide a constructed context to the LCVis widget so that we can visualize
32
+ # the surface inference results.
33
+ WORKSPACE_STATE_CONFIG = {
34
+ "connections": {_SOURCE_FILTER_ID: []},
35
+ "filters": [
36
+ {
37
+ "id": _SOURCE_FILTER_ID,
38
+ "name": "LcMeshSource",
39
+ "params": {
40
+ "fvm_params": "",
41
+ "url": "dummy",
42
+ },
43
+ }
44
+ ],
45
+ "workspace_params": {"edge_mode": "boundary"},
46
+ }
47
+
48
+
49
+ class InteractiveInference(InteractiveScene):
50
+ """
51
+ The InteractiveInference acts as the bridge between the RenderData and
52
+ the Jupyter widget, handling checking if we have the widget package
53
+ before passing calls to the widget to handle it being an optional
54
+ dependency
55
+ """
56
+
57
+ def __init__(self, signed_url: str, mode: SceneMode = SceneMode.INLINE) -> None:
58
+ # Initialize the widget directly without calling parent constructor
59
+ # since we don't have a real Scene object and override set_scene anyway
60
+ if not lcj:
61
+ raise ImportError(
62
+ "Interactive visualization requires luminarycloud[jupyter] to be installed"
63
+ )
64
+ self.widget = lcj.LCVisWidget(scene_mode=mode)
65
+ self._scene = None # Not used in InteractiveInference
66
+
67
+ # Do inference-specific setup
68
+ self.set_signed_url(signed_url, False)
69
+ # Known quantities written by inference
70
+ self._valid_quantities = [
71
+ VisQuantity.NONE,
72
+ VisQuantity.PRESSURE,
73
+ VisQuantity.WALL_SHEAR_STRESS,
74
+ ]
75
+
76
+ def set_signed_url(self, signed_url: str, isComparator: bool) -> None:
77
+ # TODO(matt): we could make isCompartor an index so we could compare
78
+ # more than two scenes at once.
79
+
80
+ # Import here to avoid circular import issue
81
+ from .visualization import LookAtCamera
82
+
83
+ WORKSPACE_STATE_CONFIG["filters"][0]["params"]["url"] = signed_url
84
+ resp = vis_pb2.GetRenderDataUrlsResponse()
85
+ resp.urls.filter_ids.append(_SOURCE_FILTER_ID)
86
+ file = resp.urls.data_files.add()
87
+ file.signed_url = signed_url
88
+ resp.urls.data_files.append(file)
89
+ resp.workspace_state = json.dumps(WORKSPACE_STATE_CONFIG)
90
+
91
+ self.widget.set_workspace_state(resp, isComparator)
92
+
93
+ self.reset_camera()
94
+ self.widget.set_flat_shading(True)
95
+
96
+ def get_field_range(self, quantity: VisQuantity) -> list[float]:
97
+ """
98
+ Get the field range for a given quantity.
99
+ """
100
+ if quantity not in self._valid_quantities:
101
+ raise ValueError(
102
+ f"Invalid field quantity: {quantity}. Valid quantities are: {self._valid_quantities}"
103
+ )
104
+ return self.widget.get_field_range(quantity)
105
+
106
+ def set_display_field(self, field: Field) -> None:
107
+ if field.quantity not in self._valid_quantities:
108
+ raise ValueError(
109
+ f"Invalid field quantity: {field.quantity}. Valid quantities are: {self._valid_quantities}"
110
+ )
111
+ if not quantity_type._is_vector(field.quantity):
112
+ # We normally handle this on the backend, but we are sending this directly
113
+ # to lcvis, so we need to make sure scalars are set to the X component
114
+ field.component = FieldComponent.X
115
+ attrs = DisplayAttributes()
116
+ attrs.field = field
117
+ attrs.visible = True
118
+ attrs.representation = Representation.SURFACE
119
+ self.widget.set_display_attributes(_SOURCE_FILTER_ID, attrs)
120
+
121
+ def get_field_ranges(self) -> dict[str, list[float]]:
122
+ """
123
+ Get the field ranges from the widget.
124
+ """
125
+ return self.widget.field_data_map
126
+
127
+ def set_color_map(self, color_map: "ColorMap") -> None:
128
+ if color_map.field.quantity not in self._valid_quantities:
129
+ raise ValueError(
130
+ f"Invalid field quantity: {color_map.field.quantity}. Valid quantities are: {self._valid_quantities}"
131
+ )
132
+ if not quantity_type._is_vector(color_map.field.quantity):
133
+ # We normally handle this on the backend, but we are sending this directly
134
+ # to lcvis, so we need to make sure scalars are set to the X component
135
+ color_map.field.component = FieldComponent.X
136
+ super().set_color_map(color_map)
137
+
138
+ def set_scene(self, scene: "Scene", isComparator: bool = False) -> None:
139
+ """
140
+ InteractiveInference does not support setting scenes.
141
+ Use set_signed_url() instead to load inference data.
142
+ """
143
+ raise NotImplementedError(
144
+ "InteractiveInference does not support set_scene(). Use set_signed_url() to load inference data."
145
+ )
146
+
147
+ def compare(self, entity) -> None:
148
+ """
149
+ InteractiveInference does not support scene comparison.
150
+ """
151
+ raise NotImplementedError(
152
+ "InteractiveInference does not support compare(). This feature is only available for InteractiveScene."
153
+ )
@@ -8,13 +8,16 @@ from typing import TYPE_CHECKING, cast, Union
8
8
  from .._proto.api.v0.luminarycloud.vis import vis_pb2
9
9
 
10
10
  if TYPE_CHECKING:
11
- from .visualization import Scene, LookAtCamera, ColorMap
11
+ from .visualization import Scene, LookAtCamera, DirectionalCamera, ColorMap
12
12
  from ..geometry import Geometry
13
13
  from ..mesh import Mesh
14
14
  from ..solution import Solution
15
15
 
16
16
  try:
17
17
  import luminarycloud_jupyter as lcj
18
+
19
+ if TYPE_CHECKING:
20
+ from luminarycloud_jupyter import InteractiveLCVisWidget, LCVisPlaneWidget
18
21
  except ImportError:
19
22
  lcj = None
20
23
 
@@ -105,7 +108,7 @@ class InteractiveScene:
105
108
  # is moved there. Matt: this would be a lot of work. The current vis service
106
109
  # call used in the backend doesn't use the streaming version. Further, there is
107
110
  # a lot of extra code to manage the streaming callbacks.
108
- self.widget.set_workspace_state(scene, resp, isComparator)
111
+ self.widget.set_workspace_state(resp, isComparator)
109
112
 
110
113
  # Sync display attributes and visibilities for surfaces
111
114
  self.set_display_attributes(_SOURCE_FILTER_ID, scene.global_display_attrs)
@@ -115,21 +118,16 @@ class InteractiveScene:
115
118
  for f in scene._filters:
116
119
  self.set_display_attributes(f.id, f.display_attrs)
117
120
 
118
- # Find and apply the first look at camera, if any, in the scene
119
- set_camera = False
120
- for c in scene._cameras:
121
- if not isinstance(c, LookAtCamera):
122
- continue
123
- set_camera = True
124
- self.set_camera(c)
125
-
126
121
  # Set any color maps we have. TODO(matt): only a few attributes are connected atm.
127
122
  for color_map in scene._color_maps:
128
123
  self.set_color_map(color_map)
129
124
 
125
+ # Apply the first camera, if any, in the scene
130
126
  # If we don't have an initial camera to use, reset the camera after loading
131
127
  # the workspace state
132
- if not set_camera:
128
+ if len(scene._cameras) > 0:
129
+ self.set_camera(scene._cameras[0])
130
+ else:
133
131
  self.reset_camera()
134
132
 
135
133
  def set_surface_visibility(self, surface_id: str, visible: bool) -> None:
@@ -144,11 +142,26 @@ class InteractiveScene:
144
142
  def reset_camera(self) -> None:
145
143
  self.widget.reset_camera()
146
144
 
147
- def set_camera(self, camera: "LookAtCamera") -> None:
148
- self.widget.camera_pan = [camera.pan_x, camera.pan_y]
149
- self.widget.camera_position = [camera.position[0], camera.position[1], camera.position[2]]
150
- self.widget.camera_look_at = [camera.look_at[0], camera.look_at[1], camera.look_at[2]]
151
- self.widget.camera_up = [camera.up[0], camera.up[1], camera.up[2]]
145
+ def set_camera(self, camera: "LookAtCamera | DirectionalCamera") -> None:
146
+ # Import here to avoid circular import issue
147
+ from .visualization import LookAtCamera
148
+
149
+ # Clear any prev camera state
150
+ self.widget.camera_position = []
151
+ self.widget.camera_look_at = []
152
+ self.widget.camera_up = []
153
+ self.widget.camera_pan = []
154
+ if isinstance(camera, LookAtCamera):
155
+ self.widget.camera_position = [
156
+ camera.position[0],
157
+ camera.position[1],
158
+ camera.position[2],
159
+ ]
160
+ self.widget.camera_look_at = [camera.look_at[0], camera.look_at[1], camera.look_at[2]]
161
+ self.widget.camera_up = [camera.up[0], camera.up[1], camera.up[2]]
162
+ self.widget.camera_pan = [camera.pan_x, camera.pan_y, 0]
163
+ else:
164
+ self.widget.set_camera_orientation(camera.direction)
152
165
 
153
166
  def set_color_map(self, color_map: "ColorMap") -> None:
154
167
  self.widget.set_color_map(color_map)
@@ -170,6 +183,12 @@ class InteractiveScene:
170
183
  def set_triad_visible(self, visible: bool) -> None:
171
184
  self.widget.set_triad_visible(visible)
172
185
 
186
+ def add_plane_widget(self) -> "LCVisPlaneWidget":
187
+ return self.widget.add_plane_widget()
188
+
189
+ def delete_widget(self, widget: "InteractiveLCVisWidget") -> None:
190
+ self.widget.delete_widget(widget)
191
+
173
192
  def compare(self, entity: Union["Geometry", "Mesh", "Solution"]) -> None:
174
193
  # The entity can be a Geometry, Mesh, or Solution and is checked by the
175
194
  # clone method. This can raise error if the scenes are incompatiable.
@@ -33,6 +33,9 @@ class Plane(CodeRepr):
33
33
  self.normal = Vector3()
34
34
  self.normal._from_proto(proto.normal)
35
35
 
36
+ def __repr__(self) -> str:
37
+ return self._to_code_helper(obj_name="plane")
38
+
36
39
 
37
40
  @dc.dataclass
38
41
  class Box(CodeRepr):
@@ -78,6 +81,9 @@ class Box(CodeRepr):
78
81
  self.angles.y = proto.angles.y * (180 / math.pi)
79
82
  self.angles.z = proto.angles.z * (180 / math.pi)
80
83
 
84
+ def __repr__(self) -> str:
85
+ return self._to_code_helper(obj_name="box")
86
+
81
87
 
82
88
  @dc.dataclass
83
89
  class AABB(CodeRepr):
@@ -114,3 +120,6 @@ class AABB(CodeRepr):
114
120
  max_point = Vector3()
115
121
  max_point._from_proto(proto.max)
116
122
  self.max = max_point
123
+
124
+ def __repr__(self) -> str:
125
+ return self._to_code_helper(obj_name="aabb")
@@ -99,6 +99,9 @@ class DirectionalCamera(CodeRepr):
99
99
  self.height = static_ani.resolution.height
100
100
  self.projection = CameraProjection(static_ani.camera.projection)
101
101
 
102
+ def __repr__(self) -> str:
103
+ return self._to_code_helper(obj_name="camera")
104
+
102
105
 
103
106
  @dc.dataclass
104
107
  class LookAtCamera(CodeRepr):
@@ -158,6 +161,9 @@ class LookAtCamera(CodeRepr):
158
161
  self.height = static_ani.resolution.height
159
162
  self.projection = CameraProjection(static_ani.camera.projection)
160
163
 
164
+ def __repr__(self) -> str:
165
+ return self._to_code_helper(obj_name="camera")
166
+
161
167
 
162
168
  class RenderOutput:
163
169
  """
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
 
4
4
  import logging
5
5
  from os import PathLike
6
- from typing import Iterable, Iterator, Optional, TYPE_CHECKING
6
+ from typing import TYPE_CHECKING, Iterable, Iterator, Optional
7
7
  from uuid import uuid4
8
8
 
9
9
  from luminarycloud._helpers import util
@@ -12,6 +12,7 @@ from luminarycloud._proto.api.v0.luminarycloud.geometry import (
12
12
  geometry_pb2 as geometrypb,
13
13
  )
14
14
  from luminarycloud._proto.upload import upload_pb2 as uploadpb
15
+ from luminarycloud.types.adfloat import _from_ad_proto, _to_ad_proto
15
16
 
16
17
  from ._client import get_default_client
17
18
  from ._proto.base import base_pb2 as basepb
@@ -157,7 +158,7 @@ class VolumeSelection:
157
158
  feature.import_geometry.CopyFrom(
158
159
  gpb.ImportGeometry(
159
160
  geometry_url=cad_url,
160
- scaling=scaling,
161
+ scaling=_to_ad_proto(scaling),
161
162
  )
162
163
  )
163
164
  self.__create_feature(feature)
@@ -1,34 +1,34 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: luminarycloud
3
- Version: 0.17.0
3
+ Version: 0.18.0
4
4
  Summary: Luminary Cloud SDK
5
5
  Project-URL: Homepage, https://www.luminarycloud.com/
6
6
  Project-URL: Documentation, https://app.luminarycloud.com/docs/api/
7
7
  Author-email: "Luminary Cloud Inc." <support@luminarycloud.com>
8
8
  Keywords: Luminary Cloud,SDK
9
9
  Requires-Python: >=3.10
10
- Requires-Dist: google-crc32c~=1.6.0
11
- Requires-Dist: googleapis-common-protos~=1.58
12
- Requires-Dist: grpcio-status~=1.51
13
- Requires-Dist: grpcio-tools~=1.51
14
- Requires-Dist: grpcio~=1.51
15
- Requires-Dist: importlib-metadata~=6.0.0
10
+ Requires-Dist: google-crc32c~=1.7
11
+ Requires-Dist: googleapis-common-protos~=1.70
12
+ Requires-Dist: grpcio-status~=1.65
13
+ Requires-Dist: grpcio-tools~=1.65
14
+ Requires-Dist: grpcio~=1.65
15
+ Requires-Dist: importlib-metadata~=8.7
16
16
  Requires-Dist: opentelemetry-api~=1.25
17
17
  Requires-Dist: opentelemetry-exporter-otlp-proto-common~=1.25
18
18
  Requires-Dist: opentelemetry-exporter-otlp-proto-http~=1.25
19
- Requires-Dist: opentelemetry-instrumentation-grpc~=0.46b0
20
- Requires-Dist: opentelemetry-instrumentation~=0.46b0
19
+ Requires-Dist: opentelemetry-instrumentation-grpc~=0.55b1
20
+ Requires-Dist: opentelemetry-instrumentation~=0.55b1
21
21
  Requires-Dist: opentelemetry-proto~=1.25
22
22
  Requires-Dist: opentelemetry-sdk~=1.25
23
- Requires-Dist: opentelemetry-semantic-conventions~=0.46b0
24
- Requires-Dist: protobuf~=4.21
25
- Requires-Dist: pyjwt~=2.6
26
- Requires-Dist: python-dotenv~=1.0.0
27
- Requires-Dist: pyyaml~=6.0.2
28
- Requires-Dist: requests~=2.28
29
- Requires-Dist: waitress~=3.0.1
30
- Requires-Dist: werkzeug~=3.0.1
31
- Requires-Dist: zstandard
23
+ Requires-Dist: opentelemetry-semantic-conventions~=0.55b1
24
+ Requires-Dist: protobuf~=5.29
25
+ Requires-Dist: pyjwt~=2.10
26
+ Requires-Dist: python-dotenv~=1.1
27
+ Requires-Dist: pyyaml~=6.0
28
+ Requires-Dist: requests~=2.32
29
+ Requires-Dist: waitress~=3.0
30
+ Requires-Dist: werkzeug~=3.1
31
+ Requires-Dist: zstandard~=0.23
32
32
  Provides-Extra: jupyter
33
33
  Requires-Dist: luminarycloud-jupyter; extra == 'jupyter'
34
34
  Description-Content-Type: text/markdown