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.
- 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_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 +35 -2
- 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/enum/geometry_status.py +15 -8
- 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/params/enum/_enum_wrappers.py +29 -0
- 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 +46 -30
- luminarycloud/pipelines/__init__.py +7 -0
- luminarycloud/pipelines/api.py +213 -0
- luminarycloud/project.py +65 -5
- luminarycloud/simulation_template.py +15 -6
- luminarycloud/vis/__init__.py +4 -0
- luminarycloud/vis/interactive_inference.py +153 -0
- luminarycloud/vis/interactive_scene.py +35 -16
- luminarycloud/vis/primitives.py +9 -0
- luminarycloud/vis/visualization.py +6 -0
- luminarycloud/volume_selection.py +3 -2
- {luminarycloud-0.17.0.dist-info → luminarycloud-0.18.0.dist-info}/METADATA +18 -18
- {luminarycloud-0.17.0.dist-info → luminarycloud-0.18.0.dist-info}/RECORD +54 -47
- {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
|
|
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
|
|
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 +=
|
|
484
|
-
code +=
|
|
485
|
-
code +=
|
|
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"
|
luminarycloud/vis/__init__.py
CHANGED
|
@@ -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(
|
|
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
|
|
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
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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.
|
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
|
"""
|
|
@@ -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.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.
|
|
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
|