@@ -720,146 +720,6 @@ def bitmap_coordinates_to_target_coordinates(
720720 return target_coordinates
721721
722722
723- def target_coordinates_to_bitmap_coordinates (
724- world_coordinates : torch .Tensor ,
725- bitmap_resolution : torch .Tensor ,
726- solar_tower : SolarTower ,
727- target_area_indices : torch .Tensor ,
728- device : torch .device | None = None ,
729- ) -> torch .Tensor :
730- """
731- Map 3D world coordinates on tower targets into 2D bitmap pixel coordinates.
732-
733- Bitmaps and the resolution are conceptually defined as: [W, H] # width, height
734- Tensor memory layout follows PyTorch convention: [H, W] # height, width
735-
736- The bitmap is treated as a discrete image grid with resolution:
737- - bitmap_resolution = [width, height]
738- Pixel coordinates follow image indexing conventions:
739- - bitmap_coordinates[..., e] ∈ [0, W-1]
740- - bitmap_coordinates[..., u] ∈ [0, H-1]
741- They are interpreted as centered pixels:
742- - (e + 0.5) / W
743- - (u + 0.5) / H
744- This ensures each pixel represents its spatial cell center rather than its corner.
745-
746- The e-axis is intentionally flipped (0.5 - e_norm) to match the desired bitmap orientation.
747- This means: increasing bitmap e → decreases world e.
748-
749- Parameters
750- ----------
751- world_coordinates : torch.Tensor
752- Coordinates in world space.
753- Shape is ``[number_of_active_heliostats, 4]``.
754- bitmap_resolution : torch.Tensor
755- Resolution of the bitmap (width, height) in pixels.
756- Shape is ``[2]``.
757- solar_tower : SolarTower
758- Solar tower containing all target area definitions (planar and cylindrical).
759- target_area_indices : torch.Tensor
760- Global target area index for each heliostat (planar indices first, cylindrical second).
761- Shape is ``[number_of_active_heliostats]``.
762- device : torch.device | None
763- The device on which to perform computations or load tensors and models (default is None).
764- If None, ``ARTIST`` will automatically select the most appropriate
765- device (CUDA or CPU) based on availability and OS.
766-
767- Returns
768- -------
769- torch.Tensor
770- Bitmap coordinates in homogeneous format.
771- Shape is ``[number_of_active_heliostats, 4]``.
772- """
773- device = get_device (device = device )
774-
775- world_enu = world_coordinates [:, :3 ]
776- number_of_coordinates = world_enu .shape [0 ]
777-
778- bitmap_coords = torch .zeros ((number_of_coordinates , 2 ), device = device )
779-
780- bitmap_width = bitmap_resolution [index_mapping .unbatched_bitmap_e ]
781- bitmap_height = bitmap_resolution [index_mapping .unbatched_bitmap_u ]
782-
783- planar_mask = (
784- target_area_indices
785- < solar_tower .number_of_target_areas_per_type [index_mapping .planar_target_areas ]
786- )
787-
788- if planar_mask .any ():
789- planar_indices = target_area_indices [planar_mask ]
790-
791- planar = cast (
792- TowerTargetAreasPlanar ,
793- solar_tower .target_areas [index_mapping .planar_target_areas ],
794- )
795-
796- centers = planar .centers [planar_indices ][:, :3 ]
797- dims = planar .dimensions [planar_indices ]
798-
799- e_axis = torch .tensor ([1.0 , 0.0 , 0.0 ], device = device ).expand (
800- planar_indices .numel (), 3
801- )
802- u_axis = torch .tensor ([0.0 , 0.0 , 1.0 ], device = device ).expand (
803- planar_indices .numel (), 3
804- )
805-
806- rel = world_enu [planar_mask ] - centers
807-
808- e_local = - torch .sum (rel * e_axis , dim = - 1 )
809- u_local = - torch .sum (rel * u_axis , dim = - 1 )
810-
811- e_norm = e_local / dims [:, index_mapping .target_dimensions_width ] + 0.5
812- u_norm = u_local / dims [:, index_mapping .target_dimensions_height ] + 0.5
813-
814- bitmap_coords [planar_mask , index_mapping .unbatched_bitmap_e ] = (
815- e_norm * bitmap_width - 0.5
816- )
817- bitmap_coords [planar_mask , index_mapping .unbatched_bitmap_u ] = (
818- u_norm * bitmap_height - 0.5
819- )
820-
821- if (~ planar_mask ).any ():
822- cylinder_indices = (
823- target_area_indices [~ planar_mask ]
824- - solar_tower .number_of_target_areas_per_type [
825- index_mapping .planar_target_areas
826- ]
827- )
828- cylindrical = cast (
829- TowerTargetAreasCylindrical ,
830- solar_tower .target_areas [index_mapping .cylindrical_target_areas ],
831- )
832-
833- centers = cylindrical .centers [cylinder_indices ][:, :3 ]
834- axes = cylindrical .axes [cylinder_indices ][:, :3 ]
835- normals = cylindrical .normals [cylinder_indices ][:, :3 ]
836- heights = cylindrical .heights [cylinder_indices ].flatten ()
837- opening_angles = cylindrical .opening_angles [cylinder_indices ].flatten ()
838-
839- v = torch .cross (axes , normals , dim = - 1 )
840-
841- rel = world_enu [~ planar_mask ] - centers
842-
843- z = torch .sum (rel * axes , dim = - 1 )
844-
845- x_n = torch .sum (rel * normals , dim = - 1 )
846- x_v = torch .sum (rel * v , dim = - 1 )
847-
848- theta = torch .atan2 (x_v , x_n )
849-
850- e_norm = theta / opening_angles + 0.5
851- u_norm = - z / heights + 0.5
852-
853- bitmap_coords [~ planar_mask , index_mapping .unbatched_bitmap_e ] = (
854- e_norm * bitmap_width - 0.5
855- )
856- bitmap_coords [~ planar_mask , index_mapping .unbatched_bitmap_u ] = (
857- u_norm * bitmap_height - 0.5
858- )
859-
860- return bitmap_coords
861-
862-
863723def create_nurbs_evaluation_grid (
864724 number_of_evaluation_points : torch .Tensor ,
865725 epsilon : float = 1e-7 ,
0 commit comments