luminarycloud 0.17.0__py3-none-any.whl → 0.18.1__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.
- luminarycloud/__init__.py +4 -0
- luminarycloud/_client/client.py +3 -0
- luminarycloud/_helpers/_create_geometry.py +134 -32
- luminarycloud/_helpers/cond.py +0 -1
- luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2.py +146 -123
- luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2.pyi +81 -8
- luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2_grpc.py +34 -0
- luminarycloud/_proto/api/v0/luminarycloud/geometry/geometry_pb2_grpc.pyi +12 -0
- luminarycloud/_proto/api/v0/luminarycloud/inference/inference_pb2.py +8 -8
- luminarycloud/_proto/api/v0/luminarycloud/inference/inference_pb2.pyi +12 -7
- luminarycloud/_proto/api/v0/luminarycloud/pipelines/pipelines_pb2.py +246 -0
- luminarycloud/_proto/api/v0/luminarycloud/pipelines/pipelines_pb2.pyi +420 -0
- luminarycloud/_proto/api/v0/luminarycloud/pipelines/pipelines_pb2_grpc.py +240 -0
- luminarycloud/_proto/api/v0/luminarycloud/pipelines/pipelines_pb2_grpc.pyi +90 -0
- luminarycloud/_proto/api/v0/luminarycloud/project/project_pb2.py +54 -3
- luminarycloud/_proto/api/v0/luminarycloud/project/project_pb2.pyi +92 -1
- luminarycloud/_proto/api/v0/luminarycloud/project/project_pb2_grpc.py +132 -0
- luminarycloud/_proto/api/v0/luminarycloud/project/project_pb2_grpc.pyi +40 -0
- luminarycloud/_proto/api/v0/luminarycloud/simulation/simulation_pb2.py +88 -55
- luminarycloud/_proto/api/v0/luminarycloud/simulation/simulation_pb2.pyi +108 -1
- luminarycloud/_proto/api/v0/luminarycloud/simulation/simulation_pb2_grpc.py +35 -0
- luminarycloud/_proto/api/v0/luminarycloud/simulation/simulation_pb2_grpc.pyi +16 -0
- luminarycloud/_proto/api/v0/luminarycloud/simulation_template/simulation_template_pb2.py +48 -26
- luminarycloud/_proto/api/v0/luminarycloud/simulation_template/simulation_template_pb2.pyi +30 -2
- luminarycloud/_proto/api/v0/luminarycloud/simulation_template/simulation_template_pb2_grpc.py +36 -0
- luminarycloud/_proto/api/v0/luminarycloud/simulation_template/simulation_template_pb2_grpc.pyi +18 -0
- luminarycloud/_proto/client/simulation_pb2.py +261 -251
- luminarycloud/_proto/client/simulation_pb2.pyi +40 -3
- luminarycloud/_proto/frontend/output/output_pb2.py +24 -24
- luminarycloud/_proto/frontend/output/output_pb2.pyi +6 -3
- luminarycloud/_proto/geometry/geometry_pb2.py +63 -63
- luminarycloud/_proto/geometry/geometry_pb2.pyi +14 -4
- luminarycloud/_proto/inferenceservice/inferenceservice_pb2.py +10 -10
- luminarycloud/_proto/inferenceservice/inferenceservice_pb2.pyi +12 -7
- luminarycloud/_proto/output/output_pb2.py +43 -36
- luminarycloud/_proto/output/output_pb2.pyi +28 -1
- luminarycloud/_proto/quantity/quantity_options_pb2.py +5 -4
- luminarycloud/_proto/quantity/quantity_options_pb2.pyi +4 -0
- luminarycloud/_proto/quantity/quantity_pb2.py +8 -8
- luminarycloud/enum/__init__.py +1 -0
- luminarycloud/enum/geometry_status.py +15 -8
- luminarycloud/enum/moment_convention_type.py +19 -0
- luminarycloud/enum/pipeline_job_status.py +23 -0
- luminarycloud/feature_modification.py +3 -1
- luminarycloud/geometry.py +12 -0
- luminarycloud/geometry_version.py +23 -0
- luminarycloud/outputs/output_definitions.py +5 -0
- luminarycloud/params/enum/_enum_wrappers.py +29 -0
- luminarycloud/params/simulation/monitor_plane_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/boundary_conditions/inlet/fan_curve_inlet_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/boundary_conditions/inlet/mach_inlet_.py +5 -1
- luminarycloud/params/simulation/physics/fluid/boundary_conditions/inlet/mass_flow_inlet_.py +5 -1
- luminarycloud/params/simulation/physics/fluid/boundary_conditions/inlet/total_pressure_inlet_.py +5 -1
- luminarycloud/params/simulation/physics/fluid/boundary_conditions/inlet/velocity_magnitude_inlet_.py +5 -1
- luminarycloud/physics_ai/inference.py +57 -30
- luminarycloud/pipelines/__init__.py +7 -0
- luminarycloud/pipelines/api.py +213 -0
- luminarycloud/pipelines/operators.py +4 -4
- luminarycloud/project.py +66 -6
- luminarycloud/simulation.py +6 -0
- luminarycloud/simulation_param.py +4 -2
- luminarycloud/simulation_queue.py +130 -0
- luminarycloud/simulation_template.py +21 -7
- luminarycloud/types/adfloat.py +3 -0
- luminarycloud/vis/__init__.py +4 -0
- luminarycloud/vis/interactive_inference.py +153 -0
- luminarycloud/vis/interactive_scene.py +49 -17
- luminarycloud/vis/primitives.py +9 -0
- luminarycloud/vis/visualization.py +22 -0
- luminarycloud/volume_selection.py +3 -2
- {luminarycloud-0.17.0.dist-info → luminarycloud-0.18.1.dist-info}/METADATA +18 -18
- {luminarycloud-0.17.0.dist-info → luminarycloud-0.18.1.dist-info}/RECORD +73 -64
- {luminarycloud-0.17.0.dist-info → luminarycloud-0.18.1.dist-info}/WHEEL +0 -0
|
@@ -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
|
+
)
|
|
@@ -2,19 +2,23 @@
|
|
|
2
2
|
from .display import DisplayAttributes
|
|
3
3
|
from .filters import SurfaceStreamlines, Filter, SurfaceLIC
|
|
4
4
|
from .._client import get_default_client
|
|
5
|
-
from luminarycloud.enum.vis_enums import EntityType, SceneMode
|
|
5
|
+
from luminarycloud.enum.vis_enums import EntityType, SceneMode, FieldComponent
|
|
6
6
|
from typing import TYPE_CHECKING, cast, Union
|
|
7
|
+
import luminarycloud.enum.quantity_type as quantity_type
|
|
7
8
|
|
|
8
9
|
from .._proto.api.v0.luminarycloud.vis import vis_pb2
|
|
9
10
|
|
|
10
11
|
if TYPE_CHECKING:
|
|
11
|
-
from .visualization import Scene, LookAtCamera, ColorMap
|
|
12
|
+
from .visualization import Scene, LookAtCamera, DirectionalCamera, ColorMap
|
|
12
13
|
from ..geometry import Geometry
|
|
13
14
|
from ..mesh import Mesh
|
|
14
15
|
from ..solution import Solution
|
|
15
16
|
|
|
16
17
|
try:
|
|
17
18
|
import luminarycloud_jupyter as lcj
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from luminarycloud_jupyter import InteractiveLCVisWidget, LCVisPlaneWidget
|
|
18
22
|
except ImportError:
|
|
19
23
|
lcj = None
|
|
20
24
|
|
|
@@ -105,7 +109,7 @@ class InteractiveScene:
|
|
|
105
109
|
# is moved there. Matt: this would be a lot of work. The current vis service
|
|
106
110
|
# call used in the backend doesn't use the streaming version. Further, there is
|
|
107
111
|
# a lot of extra code to manage the streaming callbacks.
|
|
108
|
-
self.widget.set_workspace_state(
|
|
112
|
+
self.widget.set_workspace_state(resp, isComparator)
|
|
109
113
|
|
|
110
114
|
# Sync display attributes and visibilities for surfaces
|
|
111
115
|
self.set_display_attributes(_SOURCE_FILTER_ID, scene.global_display_attrs)
|
|
@@ -115,21 +119,16 @@ class InteractiveScene:
|
|
|
115
119
|
for f in scene._filters:
|
|
116
120
|
self.set_display_attributes(f.id, f.display_attrs)
|
|
117
121
|
|
|
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
122
|
# Set any color maps we have. TODO(matt): only a few attributes are connected atm.
|
|
127
123
|
for color_map in scene._color_maps:
|
|
128
124
|
self.set_color_map(color_map)
|
|
129
125
|
|
|
126
|
+
# Apply the first camera, if any, in the scene
|
|
130
127
|
# If we don't have an initial camera to use, reset the camera after loading
|
|
131
128
|
# the workspace state
|
|
132
|
-
if
|
|
129
|
+
if len(scene._cameras) > 0:
|
|
130
|
+
self.set_camera(scene._cameras[0])
|
|
131
|
+
else:
|
|
133
132
|
self.reset_camera()
|
|
134
133
|
|
|
135
134
|
def set_surface_visibility(self, surface_id: str, visible: bool) -> None:
|
|
@@ -139,18 +138,45 @@ class InteractiveScene:
|
|
|
139
138
|
self.widget.set_surface_color(surface_id, color)
|
|
140
139
|
|
|
141
140
|
def set_display_attributes(self, object_id: str, attrs: DisplayAttributes) -> None:
|
|
141
|
+
# In the other parts of the code we ignore the field component for scalar. Since
|
|
142
|
+
# we are sending this directly to lcvis, we need to make sure that we use the x
|
|
143
|
+
# component.
|
|
144
|
+
if not quantity_type._is_vector(attrs.field.quantity):
|
|
145
|
+
# It won't matter if we change the object that is passed in.
|
|
146
|
+
attrs.field.component = FieldComponent.X
|
|
142
147
|
self.widget.set_display_attributes(object_id, attrs)
|
|
143
148
|
|
|
144
149
|
def reset_camera(self) -> None:
|
|
145
150
|
self.widget.reset_camera()
|
|
146
151
|
|
|
147
|
-
def set_camera(self, camera: "LookAtCamera") -> None:
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
+
def set_camera(self, camera: "LookAtCamera | DirectionalCamera") -> None:
|
|
153
|
+
# Import here to avoid circular import issue
|
|
154
|
+
from .visualization import LookAtCamera
|
|
155
|
+
|
|
156
|
+
# Clear any prev camera state
|
|
157
|
+
self.widget.camera_position = []
|
|
158
|
+
self.widget.camera_look_at = []
|
|
159
|
+
self.widget.camera_up = []
|
|
160
|
+
self.widget.camera_pan = []
|
|
161
|
+
if isinstance(camera, LookAtCamera):
|
|
162
|
+
self.widget.camera_position = [
|
|
163
|
+
camera.position[0],
|
|
164
|
+
camera.position[1],
|
|
165
|
+
camera.position[2],
|
|
166
|
+
]
|
|
167
|
+
self.widget.camera_look_at = [camera.look_at[0], camera.look_at[1], camera.look_at[2]]
|
|
168
|
+
self.widget.camera_up = [camera.up[0], camera.up[1], camera.up[2]]
|
|
169
|
+
self.widget.camera_pan = [camera.pan_x, camera.pan_y, 0]
|
|
170
|
+
else:
|
|
171
|
+
self.widget.set_camera_orientation(camera.direction)
|
|
152
172
|
|
|
153
173
|
def set_color_map(self, color_map: "ColorMap") -> None:
|
|
174
|
+
# In the other parts of the code we ignore the field component for
|
|
175
|
+
# scalar. Since we are sending this directly to lcvis, we need to make
|
|
176
|
+
# sure that we use the X comp.
|
|
177
|
+
if not quantity_type._is_vector(color_map.field.quantity):
|
|
178
|
+
# It won't matter if we change the object that is passed in.
|
|
179
|
+
color_map.field.component = FieldComponent.X
|
|
154
180
|
self.widget.set_color_map(color_map)
|
|
155
181
|
|
|
156
182
|
def get_camera(self) -> "LookAtCamera":
|
|
@@ -170,6 +196,12 @@ class InteractiveScene:
|
|
|
170
196
|
def set_triad_visible(self, visible: bool) -> None:
|
|
171
197
|
self.widget.set_triad_visible(visible)
|
|
172
198
|
|
|
199
|
+
def add_plane_widget(self) -> "LCVisPlaneWidget":
|
|
200
|
+
return self.widget.add_plane_widget()
|
|
201
|
+
|
|
202
|
+
def delete_widget(self, widget: "InteractiveLCVisWidget") -> None:
|
|
203
|
+
self.widget.delete_widget(widget)
|
|
204
|
+
|
|
173
205
|
def compare(self, entity: Union["Geometry", "Mesh", "Solution"]) -> None:
|
|
174
206
|
# The entity can be a Geometry, Mesh, or Solution and is checked by the
|
|
175
207
|
# clone method. This can raise error if the scenes are incompatiable.
|
luminarycloud/vis/primitives.py
CHANGED
|
@@ -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
|
"""
|
|
@@ -256,6 +262,22 @@ class RenderOutput:
|
|
|
256
262
|
raise TimeoutError
|
|
257
263
|
sleep(max(0, min(interval_seconds, deadline - time())))
|
|
258
264
|
|
|
265
|
+
def interact(self, scene_mode: SceneMode = SceneMode.SIDE_PANEL) -> InteractiveScene:
|
|
266
|
+
"""
|
|
267
|
+
Start an interactive display of the scene used to create this output,
|
|
268
|
+
when running inside LuminaryCloud's AI Notebook environment or Jupyter
|
|
269
|
+
Lab. The returned object must be displayed in the notebook to display
|
|
270
|
+
the interactive visualization. This requires that the luminarycloud
|
|
271
|
+
package was installed with the optional jupyter feature.
|
|
272
|
+
"""
|
|
273
|
+
self._fail_if_deleted()
|
|
274
|
+
self.refresh()
|
|
275
|
+
if self.status != RenderStatusType.COMPLETED:
|
|
276
|
+
raise Exception("interact: status not complete.")
|
|
277
|
+
|
|
278
|
+
scene = _reconstruct(self._extract_id, self._project_id)
|
|
279
|
+
return scene.interact(scene_mode=scene_mode)
|
|
280
|
+
|
|
259
281
|
def download_images(self) -> List[Tuple[("io.BytesIO"), str]]:
|
|
260
282
|
"""
|
|
261
283
|
Downloads the resulting jpeg images into binary buffers. This is useful
|
|
@@ -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
|
|
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.
|
|
3
|
+
Version: 0.18.1
|
|
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.
|
|
11
|
-
Requires-Dist: googleapis-common-protos~=1.
|
|
12
|
-
Requires-Dist: grpcio-status~=1.
|
|
13
|
-
Requires-Dist: grpcio-tools~=1.
|
|
14
|
-
Requires-Dist: grpcio~=1.
|
|
15
|
-
Requires-Dist: importlib-metadata~=
|
|
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.
|
|
20
|
-
Requires-Dist: opentelemetry-instrumentation~=0.
|
|
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.
|
|
24
|
-
Requires-Dist: protobuf~=
|
|
25
|
-
Requires-Dist: pyjwt~=2.
|
|
26
|
-
Requires-Dist: python-dotenv~=1.
|
|
27
|
-
Requires-Dist: pyyaml~=6.0
|
|
28
|
-
Requires-Dist: requests~=2.
|
|
29
|
-
Requires-Dist: waitress~=3.0
|
|
30
|
-
Requires-Dist: werkzeug~=3.
|
|
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
|