luminarycloud 0.15.5__py3-none-any.whl → 0.16.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/_client/client.py +5 -0
- luminarycloud/_helpers/__init__.py +1 -0
- luminarycloud/_helpers/_code_representation.py +21 -4
- luminarycloud/_helpers/download.py +67 -1
- luminarycloud/_proto/api/v0/luminarycloud/inference/inference_pb2.py +9 -9
- luminarycloud/_proto/api/v0/luminarycloud/inference/inference_pb2.pyi +7 -4
- luminarycloud/_proto/api/v0/luminarycloud/physics_ai/physics_ai_pb2.py +45 -21
- luminarycloud/_proto/api/v0/luminarycloud/physics_ai/physics_ai_pb2.pyi +65 -0
- luminarycloud/_proto/api/v0/luminarycloud/physics_ai/physics_ai_pb2_grpc.py +34 -0
- luminarycloud/_proto/api/v0/luminarycloud/physics_ai/physics_ai_pb2_grpc.pyi +12 -0
- luminarycloud/_proto/api/v0/luminarycloud/thirdpartyintegration/onshape/onshape_pb2.py +194 -7
- luminarycloud/_proto/api/v0/luminarycloud/thirdpartyintegration/onshape/onshape_pb2.pyi +407 -5
- luminarycloud/_proto/api/v0/luminarycloud/thirdpartyintegration/onshape/onshape_pb2_grpc.py +171 -0
- luminarycloud/_proto/api/v0/luminarycloud/thirdpartyintegration/onshape/onshape_pb2_grpc.pyi +64 -0
- luminarycloud/_proto/api/v0/luminarycloud/upload/upload_pb2.py +4 -2
- luminarycloud/_proto/api/v0/luminarycloud/upload/upload_pb2_grpc.py +34 -0
- luminarycloud/_proto/api/v0/luminarycloud/upload/upload_pb2_grpc.pyi +12 -0
- luminarycloud/_proto/api/v0/luminarycloud/vis/vis_pb2.py +128 -107
- luminarycloud/_proto/api/v0/luminarycloud/vis/vis_pb2.pyi +48 -3
- luminarycloud/_proto/assistant/assistant_pb2.py +82 -61
- luminarycloud/_proto/assistant/assistant_pb2.pyi +40 -0
- luminarycloud/_proto/assistant/assistant_pb2_grpc.py +34 -0
- luminarycloud/_proto/assistant/assistant_pb2_grpc.pyi +12 -0
- luminarycloud/_proto/base/base_pb2.py +7 -6
- luminarycloud/_proto/base/base_pb2.pyi +4 -0
- luminarycloud/_proto/client/simulation_pb2.py +351 -351
- luminarycloud/_proto/client/simulation_pb2.pyi +105 -97
- luminarycloud/_proto/geometry/geometry_pb2.py +68 -68
- luminarycloud/_proto/geometry/geometry_pb2.pyi +15 -7
- luminarycloud/_proto/hexmesh/hexmesh_pb2.py +40 -15
- luminarycloud/_proto/hexmesh/hexmesh_pb2.pyi +58 -1
- luminarycloud/_proto/inferenceservice/inferenceservice_pb2.py +11 -11
- luminarycloud/_proto/inferenceservice/inferenceservice_pb2.pyi +12 -4
- luminarycloud/_proto/lcstatus/codes_pb2.py +3 -2
- luminarycloud/_proto/lcstatus/codes_pb2.pyi +4 -0
- luminarycloud/_proto/quantity/quantity_pb2.py +11 -2
- luminarycloud/_proto/quantity/quantity_pb2.pyi +6 -0
- luminarycloud/_proto/table/table_pb2.pyi +4 -2
- luminarycloud/_proto/upload/upload_pb2.py +27 -7
- luminarycloud/_proto/upload/upload_pb2.pyi +31 -0
- luminarycloud/enum/quantity_type.py +19 -0
- luminarycloud/enum/tables.py +1 -0
- luminarycloud/enum/vis_enums.py +20 -0
- luminarycloud/feature_modification.py +6 -7
- luminarycloud/geometry.py +24 -0
- luminarycloud/geometry_version.py +23 -0
- luminarycloud/mesh.py +8 -1
- luminarycloud/params/simulation/adjoint_.py +4 -4
- luminarycloud/params/simulation/material/material_fluid_.py +1 -1
- luminarycloud/params/simulation/material/material_solid_.py +1 -1
- luminarycloud/params/simulation/output_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/initialization/fluid_existing_solution_.py +28 -0
- luminarycloud/params/simulation/physics/fluid/solution_controls/fluid_relaxation_method/fluid_implicit_relaxation/robust_startup/__init__.py +1 -0
- luminarycloud/params/simulation/physics/fluid/solution_controls/fluid_relaxation_method/fluid_implicit_relaxation/robust_startup/robust_startup_auto_.py +30 -0
- luminarycloud/params/simulation/physics/fluid/solution_controls/fluid_relaxation_method/fluid_implicit_relaxation/robust_startup/robust_startup_on_.py +1 -1
- luminarycloud/params/simulation/physics/fluid/solution_controls/fluid_relaxation_method/fluid_implicit_relaxation_.py +6 -2
- luminarycloud/params/simulation/physics/fluid/solution_controls_fluid_.py +4 -0
- luminarycloud/params/simulation/simulation_param_.py +6 -0
- luminarycloud/physics_ai/__init__.py +4 -0
- luminarycloud/physics_ai/inference.py +140 -4
- luminarycloud/physics_ai/solution.py +60 -0
- luminarycloud/project.py +9 -7
- luminarycloud/simulation_param.py +29 -15
- luminarycloud/simulation_template.py +14 -10
- luminarycloud/tables.py +11 -12
- luminarycloud/thirdparty/__init__.py +12 -0
- luminarycloud/thirdparty/onshape.py +170 -0
- luminarycloud/vis/__init__.py +2 -0
- luminarycloud/vis/data_extraction.py +44 -6
- luminarycloud/vis/display.py +26 -11
- luminarycloud/vis/filters.py +226 -67
- luminarycloud/vis/primitives.py +3 -2
- luminarycloud/vis/visualization.py +198 -41
- luminarycloud/volume_selection.py +2 -2
- {luminarycloud-0.15.5.dist-info → luminarycloud-0.16.1.dist-info}/METADATA +6 -6
- {luminarycloud-0.15.5.dist-info → luminarycloud-0.16.1.dist-info}/RECORD +77 -73
- {luminarycloud-0.15.5.dist-info → luminarycloud-0.16.1.dist-info}/WHEEL +0 -0
luminarycloud/vis/filters.py
CHANGED
|
@@ -15,9 +15,10 @@ from .display import Field, DisplayAttributes
|
|
|
15
15
|
from typing import List, Any, cast
|
|
16
16
|
from .primitives import Box, Plane
|
|
17
17
|
from .vis_util import generate_id
|
|
18
|
+
from .._helpers._code_representation import CodeRepr
|
|
18
19
|
|
|
19
20
|
|
|
20
|
-
class Filter(ABC):
|
|
21
|
+
class Filter(ABC, CodeRepr):
|
|
21
22
|
"""
|
|
22
23
|
This is the base class for all filters. Each derived filter class
|
|
23
24
|
is responsible for providing a _to_proto method to convert to a filter
|
|
@@ -95,7 +96,7 @@ class Slice(Filter):
|
|
|
95
96
|
Specifies this filter's appearance.
|
|
96
97
|
"""
|
|
97
98
|
|
|
98
|
-
def __init__(self, name: str) -> None:
|
|
99
|
+
def __init__(self, name: str = "") -> None:
|
|
99
100
|
super().__init__(generate_id("slice-"))
|
|
100
101
|
self._plane = Plane()
|
|
101
102
|
self._project_vectors: bool = False
|
|
@@ -166,7 +167,7 @@ class Isosurface(Filter):
|
|
|
166
167
|
Specifies this filter's appearance.
|
|
167
168
|
"""
|
|
168
169
|
|
|
169
|
-
def __init__(self, name: str) -> None:
|
|
170
|
+
def __init__(self, name: str = "") -> None:
|
|
170
171
|
super().__init__(generate_id("contour-"))
|
|
171
172
|
self.isovalues: List[float] = []
|
|
172
173
|
self.field: Field = Field()
|
|
@@ -233,11 +234,9 @@ class PlaneClip(Filter):
|
|
|
233
234
|
are removed. Default: False
|
|
234
235
|
"""
|
|
235
236
|
|
|
236
|
-
def __init__(self, name: str) -> None:
|
|
237
|
+
def __init__(self, name: str = "") -> None:
|
|
237
238
|
super().__init__(generate_id("planeClip-"))
|
|
238
239
|
self._plane: Plane = Plane()
|
|
239
|
-
# TODO(matt): We could make this a prop to that is unsettable. Or we could
|
|
240
|
-
# not use ids and force that the filter names are unique.
|
|
241
240
|
self.name = name
|
|
242
241
|
self.inverted: bool = False
|
|
243
242
|
|
|
@@ -296,7 +295,7 @@ class BoxClip(Filter):
|
|
|
296
295
|
Default : False
|
|
297
296
|
"""
|
|
298
297
|
|
|
299
|
-
def __init__(self, name: str) -> None:
|
|
298
|
+
def __init__(self, name: str = "") -> None:
|
|
300
299
|
super().__init__(generate_id("boxClip-"))
|
|
301
300
|
self._box: Box = Box()
|
|
302
301
|
self.name = name
|
|
@@ -358,17 +357,11 @@ class VectorGlyphs(Filter):
|
|
|
358
357
|
|
|
359
358
|
"""
|
|
360
359
|
|
|
361
|
-
def __init__(self, name: str) -> None:
|
|
360
|
+
def __init__(self, name: str = "") -> None:
|
|
362
361
|
super().__init__(generate_id("vector-"))
|
|
363
362
|
self.name: str = name
|
|
364
|
-
# TODO(matt): we should be able to help set some reasonable defaults bases
|
|
365
|
-
# on the mesh size (i.e., number of points) and bounds (the default glyph size).
|
|
366
|
-
# The scene has accesss to this theoretically. Perhaps the scene class can be used
|
|
367
|
-
# as a factory.
|
|
368
363
|
self._sampling_rate: int = 500
|
|
369
|
-
self.
|
|
370
|
-
# TODO(matt): we should only allow vectors somehow
|
|
371
|
-
self.field.quantity = VisQuantity.VELOCITY
|
|
364
|
+
self.quantity: VisQuantity = VisQuantity.VELOCITY
|
|
372
365
|
|
|
373
366
|
@property
|
|
374
367
|
def sampling_rate(self) -> int:
|
|
@@ -403,11 +396,11 @@ class FixedSizeVectorGlyphs(VectorGlyphs):
|
|
|
403
396
|
The size in world units (meters) of the glyphs.
|
|
404
397
|
display_attrs (DisplayAttributes)
|
|
405
398
|
Specifies this filters appearance.
|
|
406
|
-
|
|
399
|
+
quantity: VisQuantity
|
|
407
400
|
The vector field to use for glyph generation. Default: Velocity
|
|
408
401
|
"""
|
|
409
402
|
|
|
410
|
-
def __init__(self, name: str) -> None:
|
|
403
|
+
def __init__(self, name: str = "") -> None:
|
|
411
404
|
super().__init__(name)
|
|
412
405
|
self.size: float = 1.0
|
|
413
406
|
|
|
@@ -418,10 +411,10 @@ class FixedSizeVectorGlyphs(VectorGlyphs):
|
|
|
418
411
|
vis_filter.glyph.fixed_size_glyphs = self.size
|
|
419
412
|
vis_filter.glyph.n_glyphs = self.sampling_rate
|
|
420
413
|
vis_filter.glyph.sampling_mode = vis_pb2.GLYPH_SAMPLING_MODE_EVERY_NTH
|
|
421
|
-
if not quantity_type._is_vector(self.
|
|
414
|
+
if not quantity_type._is_vector(self.quantity):
|
|
422
415
|
raise ValueError("FixedSizeVectorGlyphs: field must be a vector type")
|
|
423
|
-
vis_filter.glyph.field.quantity_typ = self.
|
|
424
|
-
vis_filter.glyph.field.component =
|
|
416
|
+
vis_filter.glyph.field.quantity_typ = self.quantity.value
|
|
417
|
+
vis_filter.glyph.field.component = vis_pb2.Field.COMPONENT_UNSPECIFIED
|
|
425
418
|
return vis_filter
|
|
426
419
|
|
|
427
420
|
def _from_proto(self, filter: vis_pb2.Filter) -> None:
|
|
@@ -436,8 +429,7 @@ class FixedSizeVectorGlyphs(VectorGlyphs):
|
|
|
436
429
|
self.n_glyphs = filter.glyph.n_glyphs
|
|
437
430
|
self.size = filter.glyph.fixed_size_glyphs
|
|
438
431
|
self.sampling_rate = filter.glyph.n_glyphs
|
|
439
|
-
self.
|
|
440
|
-
self.field.component = FieldComponent(filter.glyph.field.component)
|
|
432
|
+
self.quantity = VisQuantity(filter.glyph.field.quantity_typ)
|
|
441
433
|
|
|
442
434
|
|
|
443
435
|
class ScaledVectorGlyphs(VectorGlyphs):
|
|
@@ -464,11 +456,11 @@ class ScaledVectorGlyphs(VectorGlyphs):
|
|
|
464
456
|
0.5 and the scale is 2 then the resulting world space size is 1 meter. Default: 1.
|
|
465
457
|
display_attrs (DisplayAttributes)
|
|
466
458
|
Specifies this filters appearance.
|
|
467
|
-
|
|
459
|
+
quantity: VisQuantity
|
|
468
460
|
The vector field to use for glyph generation. Default: Velocity
|
|
469
461
|
"""
|
|
470
462
|
|
|
471
|
-
def __init__(self, name: str) -> None:
|
|
463
|
+
def __init__(self, name: str = "") -> None:
|
|
472
464
|
super().__init__(name)
|
|
473
465
|
self.scale: float = 1.0
|
|
474
466
|
|
|
@@ -479,10 +471,10 @@ class ScaledVectorGlyphs(VectorGlyphs):
|
|
|
479
471
|
vis_filter.glyph.glyph_scale_size = self.scale
|
|
480
472
|
vis_filter.glyph.n_glyphs = self.sampling_rate
|
|
481
473
|
vis_filter.glyph.sampling_mode = vis_pb2.GLYPH_SAMPLING_MODE_EVERY_NTH
|
|
482
|
-
if not quantity_type._is_vector(self.
|
|
474
|
+
if not quantity_type._is_vector(self.quantity):
|
|
483
475
|
raise ValueError("ScaledVectorGyph: field must be a vector type")
|
|
484
|
-
vis_filter.glyph.field.quantity_typ = self.
|
|
485
|
-
vis_filter.glyph.field.component =
|
|
476
|
+
vis_filter.glyph.field.quantity_typ = self.quantity.value
|
|
477
|
+
vis_filter.glyph.field.component = vis_pb2.Field.COMPONENT_UNSPECIFIED
|
|
486
478
|
return vis_filter
|
|
487
479
|
|
|
488
480
|
def _from_proto(self, filter: vis_pb2.Filter) -> None:
|
|
@@ -497,8 +489,7 @@ class ScaledVectorGlyphs(VectorGlyphs):
|
|
|
497
489
|
self.n_glyphs = filter.glyph.n_glyphs
|
|
498
490
|
self.scale = filter.glyph.glyph_scale_size
|
|
499
491
|
self.sampling_rate = filter.glyph.n_glyphs
|
|
500
|
-
self.
|
|
501
|
-
self.field.component = FieldComponent(filter.glyph.field.component)
|
|
492
|
+
self.quantity = VisQuantity(filter.glyph.field.quantity_typ)
|
|
502
493
|
|
|
503
494
|
|
|
504
495
|
class Threshold(Filter):
|
|
@@ -533,7 +524,7 @@ class Threshold(Filter):
|
|
|
533
524
|
Specifies this filter's appearance.
|
|
534
525
|
"""
|
|
535
526
|
|
|
536
|
-
def __init__(self, name: str) -> None:
|
|
527
|
+
def __init__(self, name: str = "") -> None:
|
|
537
528
|
super().__init__(generate_id("threshold-"))
|
|
538
529
|
self.field: Field = Field()
|
|
539
530
|
self.min_value: float = 0.0
|
|
@@ -610,14 +601,13 @@ class Streamlines(Filter):
|
|
|
610
601
|
Specifies this filter's appearance.
|
|
611
602
|
"""
|
|
612
603
|
|
|
613
|
-
def __init__(self, name: str) -> None:
|
|
604
|
+
def __init__(self, name: str = "") -> None:
|
|
614
605
|
super().__init__(generate_id("streamlines-"))
|
|
615
606
|
self.name: str = name
|
|
616
607
|
self.n_streamlines: int = 100
|
|
617
608
|
self.max_length: float = 10
|
|
618
609
|
self.direction: StreamlineDirection = StreamlineDirection.FORWARD
|
|
619
|
-
self.
|
|
620
|
-
self.field.quantity = VisQuantity.VELOCITY
|
|
610
|
+
self.quantity: VisQuantity = VisQuantity.VELOCITY
|
|
621
611
|
|
|
622
612
|
|
|
623
613
|
class RakeStreamlines(Streamlines):
|
|
@@ -640,8 +630,8 @@ class RakeStreamlines(Streamlines):
|
|
|
640
630
|
The number of seed particles to place on the rake. Default: 100
|
|
641
631
|
max_length: float
|
|
642
632
|
The maximum path length of the particle in meters. Default: 10
|
|
643
|
-
|
|
644
|
-
The vector field to
|
|
633
|
+
quantity: VisQuantity
|
|
634
|
+
The vector field to use for the particle advection. Default: Velocity
|
|
645
635
|
start: Vector3Like
|
|
646
636
|
The start point of the rake. Default: [0,0,0].
|
|
647
637
|
end: Vector3Like
|
|
@@ -652,10 +642,10 @@ class RakeStreamlines(Streamlines):
|
|
|
652
642
|
Specifies this filter's appearance.
|
|
653
643
|
"""
|
|
654
644
|
|
|
655
|
-
def __init__(self, name: str) -> None:
|
|
645
|
+
def __init__(self, name: str = "") -> None:
|
|
656
646
|
super().__init__(name)
|
|
657
|
-
self.start: Vector3Like =
|
|
658
|
-
self.end: Vector3Like =
|
|
647
|
+
self.start: Vector3Like = Vector3(x=0, y=0, z=0)
|
|
648
|
+
self.end: Vector3Like = Vector3(x=1, y=0, z=0)
|
|
659
649
|
|
|
660
650
|
def _to_proto(self) -> vis_pb2.Filter:
|
|
661
651
|
# Type checking
|
|
@@ -663,8 +653,8 @@ class RakeStreamlines(Streamlines):
|
|
|
663
653
|
raise TypeError(f"Expected 'int', got {type(self.n_streamlines).__name__}")
|
|
664
654
|
if not isinstance(self.max_length, (float, int)):
|
|
665
655
|
raise TypeError(f"Expected 'float or int', got {type(self.max_length).__name__}")
|
|
666
|
-
if not isinstance(self.
|
|
667
|
-
raise TypeError(f"Expected '
|
|
656
|
+
if not isinstance(self.quantity, VisQuantity):
|
|
657
|
+
raise TypeError(f"Expected 'VisQuantity', got {type(self.quantity).__name__}")
|
|
668
658
|
|
|
669
659
|
vis_filter = vis_pb2.Filter()
|
|
670
660
|
vis_filter.id = self.id
|
|
@@ -673,9 +663,9 @@ class RakeStreamlines(Streamlines):
|
|
|
673
663
|
vis_filter.streamlines.max_length = self.max_length
|
|
674
664
|
vis_filter.streamlines.rake.start.CopyFrom(_to_vector3(self.start)._to_proto())
|
|
675
665
|
vis_filter.streamlines.rake.end.CopyFrom(_to_vector3(self.end)._to_proto())
|
|
676
|
-
if not quantity_type._is_vector(self.
|
|
666
|
+
if not quantity_type._is_vector(self.quantity):
|
|
677
667
|
raise ValueError("RakeStreamlines: field must be a vector type")
|
|
678
|
-
vis_filter.streamlines.field.quantity_typ = self.
|
|
668
|
+
vis_filter.streamlines.field.quantity_typ = self.quantity.value
|
|
679
669
|
return vis_filter
|
|
680
670
|
|
|
681
671
|
def _from_proto(self, filter: vis_pb2.Filter) -> None:
|
|
@@ -694,7 +684,131 @@ class RakeStreamlines(Streamlines):
|
|
|
694
684
|
self.start._from_proto(filter.streamlines.rake.start)
|
|
695
685
|
self.end = Vector3()
|
|
696
686
|
self.end._from_proto(filter.streamlines.rake.end)
|
|
697
|
-
self.
|
|
687
|
+
self.quantity = VisQuantity(filter.streamlines.field.quantity_typ)
|
|
688
|
+
|
|
689
|
+
|
|
690
|
+
class GridStreamlines(Streamlines):
|
|
691
|
+
"""
|
|
692
|
+
Streamlines is a vector field visualization technique that integrates
|
|
693
|
+
massless particles through a vector field forming curves. Streamlines are
|
|
694
|
+
used to visualize and analyze fluid flow patterns (e.g., the velocity
|
|
695
|
+
field), helping to understand how the fluid moves. Streamlines
|
|
696
|
+
can be use used to visualize any vector field contained in the solution.
|
|
697
|
+
|
|
698
|
+
GridStreamlines generates seed particles arranged in a 2D grid pattern
|
|
699
|
+
inside the volume. GridStreamlines only work with volume data.
|
|
700
|
+
|
|
701
|
+
The grid is defined by a center point and two vectors that define the u
|
|
702
|
+
(rake_direction) and v (seed_direction) directions of the grid. It's
|
|
703
|
+
recommended that the rake_direction and seed_direction vectors are
|
|
704
|
+
orthogonal to each other, but it's not required. Rakes (sets of seed
|
|
705
|
+
particles) are generated in the rake_direction. Seed particles are
|
|
706
|
+
distributed along the seed_direction. The rake spacing controls the distance
|
|
707
|
+
between the rakes, and the seed spacing controls the distance between the
|
|
708
|
+
seed particles along the rake.
|
|
709
|
+
|
|
710
|
+
For example, if the rake vector is [1,0,0] and the seed vector is [0,1,0],
|
|
711
|
+
then the rakes will be generated in the x direction and the seed particles
|
|
712
|
+
will be generated in the y direction. Lets say we want to create a grid of
|
|
713
|
+
of 4x4 particles that is 8 meters wide and 2 meters tall. The rake spacing
|
|
714
|
+
would be 2 meters / 4 = 0.5 meters, and the seed spacing would be 8 meters /
|
|
715
|
+
4 = 2 meters.
|
|
716
|
+
|
|
717
|
+
.. warning:: This feature is experimental and may change or be removed in the future.
|
|
718
|
+
|
|
719
|
+
Attributes:
|
|
720
|
+
-----------
|
|
721
|
+
max_length: float
|
|
722
|
+
The maximum path length of the particle in meters. Default: 10
|
|
723
|
+
quantity: VisQuantity
|
|
724
|
+
The vector field to use for the particle advection. Default: Velocity
|
|
725
|
+
rake_direction: Vector3Like
|
|
726
|
+
The vector defining the u direction of the grid along which the rakes are placed.
|
|
727
|
+
Default: [1,0,0].
|
|
728
|
+
seed_direction: Vector3Like
|
|
729
|
+
The vector defining the v direction of the grid along which the seed particles are placed.
|
|
730
|
+
Default: [0,1,0].
|
|
731
|
+
center: Vector3Like
|
|
732
|
+
The center point of the grid. Default: [0,0,0].
|
|
733
|
+
rake_res: int
|
|
734
|
+
The number of rake lines to generate in the u direction. Default: 2.
|
|
735
|
+
seed_res: int
|
|
736
|
+
The number of seed particles to generate in the v direction. Default: 10.
|
|
737
|
+
rake_spacing: float
|
|
738
|
+
The spacing between the rake lines in meters. Default: 0.5.
|
|
739
|
+
seed_spacing: float
|
|
740
|
+
The spacing between the seed particles in meters. Default: 0.1.
|
|
741
|
+
name : str
|
|
742
|
+
A user provided name for the filter.
|
|
743
|
+
display_attrs : DisplayAttributes
|
|
744
|
+
Specifies this filter's appearance.
|
|
745
|
+
"""
|
|
746
|
+
|
|
747
|
+
def __init__(self, name: str = "") -> None:
|
|
748
|
+
super().__init__(name)
|
|
749
|
+
self.rake_direction: Vector3Like = Vector3(x=1, y=0, z=0)
|
|
750
|
+
self.seed_direction: Vector3Like = Vector3(x=0, y=1, z=0)
|
|
751
|
+
self.center: Vector3Like = Vector3(x=0, y=0, z=0)
|
|
752
|
+
self.rake_res: int = 2
|
|
753
|
+
self.seed_res: int = 10
|
|
754
|
+
self.rake_spacing: float = 0.5
|
|
755
|
+
self.seed_spacing: float = 0.1
|
|
756
|
+
|
|
757
|
+
def _to_proto(self) -> vis_pb2.Filter:
|
|
758
|
+
# Type checking
|
|
759
|
+
if not isinstance(self.max_length, (float, int)):
|
|
760
|
+
raise TypeError(f"Expected 'float or int', got {type(self.max_length).__name__}")
|
|
761
|
+
if not isinstance(self.quantity, VisQuantity):
|
|
762
|
+
raise TypeError(f"Expected 'VisQuantity', got {type(self.quantity).__name__}")
|
|
763
|
+
if not isinstance(self.rake_res, int):
|
|
764
|
+
raise TypeError(f"Expected 'int', got {type(self.rake_res).__name__}")
|
|
765
|
+
if not isinstance(self.seed_res, int):
|
|
766
|
+
raise TypeError(f"Expected 'int', got {type(self.seed_res).__name__}")
|
|
767
|
+
if not isinstance(self.rake_spacing, (float, int)):
|
|
768
|
+
raise TypeError(f"Expected 'float or int', got {type(self.rake_spacing).__name__}")
|
|
769
|
+
if not isinstance(self.seed_spacing, (float, int)):
|
|
770
|
+
raise TypeError(f"Expected 'float or int', got {type(self.seed_spacing).__name__}")
|
|
771
|
+
|
|
772
|
+
vis_filter = vis_pb2.Filter()
|
|
773
|
+
vis_filter.id = self.id
|
|
774
|
+
vis_filter.name = self.name
|
|
775
|
+
vis_filter.streamlines.max_length = self.max_length
|
|
776
|
+
vis_filter.streamlines.grid.center.CopyFrom(_to_vector3(self.center)._to_proto())
|
|
777
|
+
vis_filter.streamlines.grid.u_vec.CopyFrom(_to_vector3(self.rake_direction)._to_proto())
|
|
778
|
+
vis_filter.streamlines.grid.v_vec.CopyFrom(_to_vector3(self.seed_direction)._to_proto())
|
|
779
|
+
vis_filter.streamlines.grid.rake_res = self.rake_res
|
|
780
|
+
vis_filter.streamlines.grid.seed_res = self.seed_res
|
|
781
|
+
vis_filter.streamlines.grid.rake_spacing = self.rake_spacing
|
|
782
|
+
vis_filter.streamlines.grid.seed_spacing = self.seed_spacing
|
|
783
|
+
|
|
784
|
+
if not quantity_type._is_vector(self.quantity):
|
|
785
|
+
raise ValueError("GridStreamlines: field must be a vector type")
|
|
786
|
+
vis_filter.streamlines.field.quantity_typ = self.quantity.value
|
|
787
|
+
return vis_filter
|
|
788
|
+
|
|
789
|
+
def _from_proto(self, filter: vis_pb2.Filter) -> None:
|
|
790
|
+
typ = filter.WhichOneof("value")
|
|
791
|
+
if typ != "streamlines":
|
|
792
|
+
raise TypeError(f"Expected 'streamlines', got {typ}")
|
|
793
|
+
s_typ = filter.streamlines.WhichOneof("seed_type")
|
|
794
|
+
if s_typ != "grid":
|
|
795
|
+
raise TypeError(f"Expected 'grid streamlines', got {s_typ}")
|
|
796
|
+
self.id = filter.id
|
|
797
|
+
self.name = filter.name
|
|
798
|
+
self.n_streamlines = filter.streamlines.n_streamlines
|
|
799
|
+
self.max_length = filter.streamlines.max_length
|
|
800
|
+
|
|
801
|
+
self.center = Vector3()
|
|
802
|
+
self.center._from_proto(filter.streamlines.grid.center)
|
|
803
|
+
self.rake_direction = Vector3()
|
|
804
|
+
self.rake_direction._from_proto(filter.streamlines.grid.u_vec)
|
|
805
|
+
self.seed_direction = Vector3()
|
|
806
|
+
self.seed_direction._from_proto(filter.streamlines.grid.v_vec)
|
|
807
|
+
self.rake_res = filter.streamlines.grid.rake_res
|
|
808
|
+
self.seed_res = filter.streamlines.grid.seed_res
|
|
809
|
+
self.rake_spacing = filter.streamlines.grid.rake_spacing
|
|
810
|
+
self.seed_spacing = filter.streamlines.grid.seed_spacing
|
|
811
|
+
self.quantity = VisQuantity(filter.streamlines.field.quantity_typ)
|
|
698
812
|
|
|
699
813
|
|
|
700
814
|
class SurfaceStreamlines(Streamlines):
|
|
@@ -741,15 +855,15 @@ class SurfaceStreamlines(Streamlines):
|
|
|
741
855
|
particles further into the volume based on the surface normal. Default: 0.0
|
|
742
856
|
max_length: float
|
|
743
857
|
The maximum path length of the particle in meters. Default: 10
|
|
744
|
-
|
|
745
|
-
The vector field to
|
|
858
|
+
quantity: VisQuantity
|
|
859
|
+
The vector field to use for the particle advection. Default: Velocity
|
|
746
860
|
name : str
|
|
747
861
|
A user provided name for the filter.
|
|
748
862
|
display_attrs : DisplayAttributes
|
|
749
863
|
Specifies this filter's appearance.
|
|
750
864
|
"""
|
|
751
865
|
|
|
752
|
-
def __init__(self, name: str) -> None:
|
|
866
|
+
def __init__(self, name: str = "") -> None:
|
|
753
867
|
super().__init__(name)
|
|
754
868
|
self.offset: float = 0.0
|
|
755
869
|
self._surface_names: List[str] = []
|
|
@@ -783,8 +897,8 @@ class SurfaceStreamlines(Streamlines):
|
|
|
783
897
|
raise TypeError(f"Expected 'float or int', got {type(self.max_length).__name__}")
|
|
784
898
|
if not isinstance(self.offset, (float, int)):
|
|
785
899
|
raise TypeError(f"Expected 'float or int', got {type(self.offset).__name__}")
|
|
786
|
-
if not isinstance(self.
|
|
787
|
-
raise TypeError(f"Expected '
|
|
900
|
+
if not isinstance(self.quantity, VisQuantity):
|
|
901
|
+
raise TypeError(f"Expected 'VisQuantity', got {type(self.quantity).__name__}")
|
|
788
902
|
if not isinstance(self.mode, SurfaceStreamlineMode):
|
|
789
903
|
raise TypeError(f"Expected 'SurfaceStreamlinesMode', got {type(self.mode).__name__}")
|
|
790
904
|
|
|
@@ -796,12 +910,12 @@ class SurfaceStreamlines(Streamlines):
|
|
|
796
910
|
project = False
|
|
797
911
|
# Prevent common mistakes that cause confusion.
|
|
798
912
|
if self.mode == SurfaceStreamlineMode.ADVECT_ON_SURFACE:
|
|
799
|
-
if self.
|
|
913
|
+
if self.quantity == VisQuantity.VELOCITY:
|
|
800
914
|
raise ValueError(
|
|
801
915
|
"SurfacesStreamines: velocity is 0 on surfaces and will produce no data"
|
|
802
916
|
)
|
|
803
917
|
project = True
|
|
804
|
-
elif self.
|
|
918
|
+
elif self.quantity == VisQuantity.WALL_SHEAR_STRESS:
|
|
805
919
|
raise ValueError(
|
|
806
920
|
"SurfacesStreamines: wall shear stress is 0 in the volume and will produce no data "
|
|
807
921
|
)
|
|
@@ -811,10 +925,9 @@ class SurfaceStreamlines(Streamlines):
|
|
|
811
925
|
raise ValueError("SurfaceStreamlines: need at least one surfaces specified.")
|
|
812
926
|
for id in self._surface_names:
|
|
813
927
|
vis_filter.streamlines.surface.surface_names.append(id)
|
|
814
|
-
if not quantity_type._is_vector(self.
|
|
815
|
-
raise ValueError("SurfaceStreamlines:
|
|
816
|
-
vis_filter.streamlines.field.
|
|
817
|
-
vis_filter.streamlines.field.quantity_typ = self.field.quantity.value
|
|
928
|
+
if not quantity_type._is_vector(self.quantity):
|
|
929
|
+
raise ValueError("SurfaceStreamlines: quantity must be a vector type")
|
|
930
|
+
vis_filter.streamlines.field.quantity_typ = self.quantity.value
|
|
818
931
|
return vis_filter
|
|
819
932
|
|
|
820
933
|
def _from_proto(self, filter: vis_pb2.Filter) -> None:
|
|
@@ -831,7 +944,15 @@ class SurfaceStreamlines(Streamlines):
|
|
|
831
944
|
self._surface_names.clear()
|
|
832
945
|
for s in filter.streamlines.surface.surface_names:
|
|
833
946
|
self._surface_names.append(s)
|
|
834
|
-
self.
|
|
947
|
+
self.quantity = VisQuantity(filter.streamlines.field.quantity_typ)
|
|
948
|
+
|
|
949
|
+
def _to_code(self, hide_defaults: bool = True, use_tmp_objs: bool = True) -> str:
|
|
950
|
+
code = super()._to_code(hide_defaults=hide_defaults)
|
|
951
|
+
# We need to explicity write the code for the surfaces since its
|
|
952
|
+
# technically a private variable.
|
|
953
|
+
for s in self._surface_names:
|
|
954
|
+
code += f".add_surface('{s}')\n"
|
|
955
|
+
return code
|
|
835
956
|
|
|
836
957
|
|
|
837
958
|
class SurfaceLIC(Filter):
|
|
@@ -855,7 +976,7 @@ class SurfaceLIC(Filter):
|
|
|
855
976
|
|
|
856
977
|
Attributes:
|
|
857
978
|
-----------
|
|
858
|
-
|
|
979
|
+
quantity: VisQuantity
|
|
859
980
|
Specifies the field used to advect particles for the surface LIC.
|
|
860
981
|
Default: WALL_SHEER_STRESS
|
|
861
982
|
contrast: float
|
|
@@ -864,13 +985,12 @@ class SurfaceLIC(Filter):
|
|
|
864
985
|
higher values mean more contrast. Default: 1
|
|
865
986
|
"""
|
|
866
987
|
|
|
867
|
-
def __init__(self, name: str) -> None:
|
|
988
|
+
def __init__(self, name: str = "") -> None:
|
|
868
989
|
super().__init__(generate_id("surface-lic-"))
|
|
869
990
|
self.name = name
|
|
870
991
|
self.contrast: float = 1.0
|
|
871
992
|
self._surface_names: List[str] = []
|
|
872
|
-
self.
|
|
873
|
-
self.field.quantity = VisQuantity.WALL_SHEAR_STRESS
|
|
993
|
+
self.quantity: VisQuantity = VisQuantity.WALL_SHEAR_STRESS
|
|
874
994
|
|
|
875
995
|
def add_surface(self, id: str) -> None:
|
|
876
996
|
"""
|
|
@@ -898,9 +1018,9 @@ class SurfaceLIC(Filter):
|
|
|
898
1018
|
vis_filter.name = self.name
|
|
899
1019
|
# Prevent common mistakes that cause confusion. The only current option
|
|
900
1020
|
# is to be on a surface, so no velocity.
|
|
901
|
-
if not isinstance(self.
|
|
902
|
-
raise TypeError(f"Expected '
|
|
903
|
-
if self.
|
|
1021
|
+
if not isinstance(self.quantity, VisQuantity):
|
|
1022
|
+
raise TypeError(f"Expected 'VisQuantity', got {type(self.quantity).__name__}")
|
|
1023
|
+
if self.quantity == VisQuantity.VELOCITY:
|
|
904
1024
|
raise ValueError("SurfaceLIC: velocity is 0 on surfaces and will produce no data")
|
|
905
1025
|
if not isinstance(self.contrast, (int, float)):
|
|
906
1026
|
raise TypeError(f"Expected 'int or float', got {type(self.contrast).__name__}")
|
|
@@ -914,7 +1034,7 @@ class SurfaceLIC(Filter):
|
|
|
914
1034
|
geometry = vis_pb2.SurfaceLICGeomtery()
|
|
915
1035
|
vis_filter.surface_lic.geometry.CopyFrom(geometry)
|
|
916
1036
|
|
|
917
|
-
vis_filter.surface_lic.field.quantity_typ = self.
|
|
1037
|
+
vis_filter.surface_lic.field.quantity_typ = self.quantity.value
|
|
918
1038
|
vis_filter.surface_lic.contrast = self.contrast
|
|
919
1039
|
return vis_filter
|
|
920
1040
|
|
|
@@ -932,7 +1052,46 @@ class SurfaceLIC(Filter):
|
|
|
932
1052
|
self._surface_names = []
|
|
933
1053
|
for s in filter.surface_lic.geometry.surface_names:
|
|
934
1054
|
self._surface_names.append(s)
|
|
935
|
-
if not quantity_type._is_vector(self.
|
|
936
|
-
raise ValueError("SurfaceLIC:
|
|
937
|
-
self.
|
|
938
|
-
|
|
1055
|
+
if not quantity_type._is_vector(self.quantity):
|
|
1056
|
+
raise ValueError("SurfaceLIC: quantity must be a vector type")
|
|
1057
|
+
self.quantity = VisQuantity(filter.surface_lic.field.quantity_typ)
|
|
1058
|
+
|
|
1059
|
+
def _to_code(self, hide_defaults: bool = True, use_tmp_objs: bool = True) -> str:
|
|
1060
|
+
code = super()._to_code(hide_defaults=hide_defaults)
|
|
1061
|
+
# We need to explicity write the code for the surfaces since its
|
|
1062
|
+
# technically a private variable.
|
|
1063
|
+
for s in self._surface_names:
|
|
1064
|
+
code += f".add_surface('{s}')\n"
|
|
1065
|
+
return code
|
|
1066
|
+
|
|
1067
|
+
|
|
1068
|
+
def _filter_to_obj_name(filter: Filter) -> str:
|
|
1069
|
+
"""
|
|
1070
|
+
Helper function to convert a filter to a code object name used in code gen.
|
|
1071
|
+
"""
|
|
1072
|
+
if not isinstance(filter, Filter):
|
|
1073
|
+
raise TypeError(f"Expected 'Filter', got {type(filter).__name__}")
|
|
1074
|
+
if isinstance(filter, Slice):
|
|
1075
|
+
return "slice"
|
|
1076
|
+
elif isinstance(filter, Isosurface):
|
|
1077
|
+
return "isosurface"
|
|
1078
|
+
elif isinstance(filter, PlaneClip):
|
|
1079
|
+
return "plane_clip"
|
|
1080
|
+
elif isinstance(filter, BoxClip):
|
|
1081
|
+
return "box_clip"
|
|
1082
|
+
elif isinstance(filter, FixedSizeVectorGlyphs):
|
|
1083
|
+
return "fixed_size_vector_glyphs"
|
|
1084
|
+
elif isinstance(filter, ScaledVectorGlyphs):
|
|
1085
|
+
return "scaled_vector_glyphs"
|
|
1086
|
+
elif isinstance(filter, Threshold):
|
|
1087
|
+
return "threshold"
|
|
1088
|
+
elif isinstance(filter, RakeStreamlines):
|
|
1089
|
+
return "rake_streamlines"
|
|
1090
|
+
elif isinstance(filter, GridStreamlines):
|
|
1091
|
+
return "grid_streamlines"
|
|
1092
|
+
elif isinstance(filter, SurfaceStreamlines):
|
|
1093
|
+
return "surface_streamlines"
|
|
1094
|
+
elif isinstance(filter, SurfaceLIC):
|
|
1095
|
+
return "surface_lic"
|
|
1096
|
+
else:
|
|
1097
|
+
raise TypeError(f"Unknown filter type: {type(filter).__name__}")
|
luminarycloud/vis/primitives.py
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# Copyright 2023-2024 Luminary Cloud, Inc. All Rights Reserved.
|
|
2
2
|
import dataclasses as dc
|
|
3
3
|
from luminarycloud.types import Vector3, Vector3Like
|
|
4
|
+
from .._helpers._code_representation import CodeRepr
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
@dc.dataclass
|
|
7
|
-
class Plane:
|
|
8
|
+
class Plane(CodeRepr):
|
|
8
9
|
"""
|
|
9
10
|
This class defines a plane.
|
|
10
11
|
|
|
@@ -19,7 +20,7 @@ class Plane:
|
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
@dc.dataclass
|
|
22
|
-
class Box:
|
|
23
|
+
class Box(CodeRepr):
|
|
23
24
|
"""
|
|
24
25
|
This class defines a box used for filter such as box clip.
|
|
25
26
|
|