99from compas_tna .envelope import Envelope
1010
1111
12+ def griddata_project (xy : list [list [float ]], xyz_target : list [list [float ]], method = "linear" ):
13+ """Project a point cloud onto a target point cloud using griddata interpolation.
14+
15+ Parameters
16+ ----------
17+ xy : list[list[float]]
18+ The XY coordinates of the points to project.
19+ xyz_target : list[list[float]]
20+ The XYZ coordinates of the target points.
21+ method : str, optional
22+ The method to use for interpolation. Default is "linear".
23+
24+ Returns
25+ -------
26+ list[float]
27+ The projected Z coordinates.
28+ """
29+ xy = asarray (xy )
30+ xy_target = asarray (xyz_target )[:, :2 ]
31+ z_target = asarray (xyz_target )[:, 2 ]
32+ return griddata (xy_target , z_target , xy , method = method ).tolist ()
33+
34+
1235def interpolate_middle_mesh (intrados : Mesh , extrados : Mesh ) -> Mesh :
1336 """Interpolate a middle mesh between intrados and extrados meshes.
1437
@@ -30,25 +53,18 @@ def interpolate_middle_mesh(intrados: Mesh, extrados: Mesh) -> Mesh:
3053 # Use the intrados as base topology
3154 middle = intrados .copy ()
3255
33- # Get point clouds for interpolation
34- intrados_points = asarray ( intrados . vertices_attributes ( "xyz" ) )
35- extrados_points = asarray ( extrados .vertices_attributes ("xyz" ))
56+ # Get Z coordinates from both surfaces based on the same XY coordinates
57+ zi = middle . vertices_attribute ( "z" )
58+ ze = griddata_project ( middle . vertices_attributes ( "xy" ), extrados .vertices_attributes ("xyz" ), method = "linear" )
3659
37- # Get XY coordinates of middle mesh
38- middle_xy = asarray (middle .vertices_attributes ("xy" ))
39-
40- # Interpolate Z coordinates from both surfaces
41- zi = griddata (intrados_points [:, :2 ], intrados_points [:, 2 ], middle_xy , method = "linear" )
42- ze = griddata (extrados_points [:, :2 ], extrados_points [:, 2 ], middle_xy , method = "linear" )
43-
44- # First loop: set middle Z as average
60+ # First loop: set middle Z as average of intrados and extrados
4561 for i , key in enumerate (middle .vertices ()):
4662 middle_z = (zi [i ] + ze [i ]) / 2.0
4763 middle .vertex_attribute (key , "z" , middle_z )
4864
4965 # Second loop: calculate and set thickness using correct normals
5066 for i , key in enumerate (middle .vertices ()):
51- nx , ny , nz = middle .vertex_normal (key )
67+ _ , _ , nz = middle .vertex_normal (key )
5268 z_diff = ze [i ] - zi [i ]
5369 if abs (nz ) > 0.1 :
5470 thickness = abs (z_diff ) * abs (nz )
@@ -158,7 +174,6 @@ def project_mesh_to_target_vertica_nearest(mesh: Mesh, target: Mesh) -> None:
158174 mesh .vertex_attributes (vertex , "xyz" , new_point )
159175
160176
161- # TODO: What if the target is a surface and not a mesh?
162177def project_mesh_to_target_vertical (mesh : Mesh , target : Mesh ) -> None :
163178 """Project a mesh vertically (in Z direction) onto a target mesh.
164179
@@ -174,14 +189,7 @@ def project_mesh_to_target_vertical(mesh: Mesh, target: Mesh) -> None:
174189 None
175190 The mesh is modified in place.
176191 """
177- # Get point clouds for interpolation
178- target_points = asarray (target .vertices_attributes ("xyz" ))
179-
180- # Get XY coordinates of middle mesh
181- mesh_xy = asarray (mesh .vertices_attributes ("xy" ))
182-
183- # Interpolate Z coordinates from both surfaces
184- z_target = griddata (target_points [:, :2 ], target_points [:, 2 ], mesh_xy , method = "linear" ).tolist ()
192+ z_target = griddata_project (mesh .vertices_attributes ("xy" ), target .vertices_attributes ("xyz" ), method = "linear" )
185193
186194 for key , i in enumerate (mesh .vertices ()):
187195 mesh .vertex_attribute (key , "z" , z_target [i ])
@@ -477,7 +485,7 @@ def compute_area(self) -> float:
477485 return self .middle .area ()
478486
479487 # =============================================================================
480- # TNA-specific operations (accept formdiagram as parameter)
488+ # Loads operations
481489 # =============================================================================
482490
483491 def apply_selfweight_to_formdiagram (self , formdiagram : FormDiagram , normalize = True ) -> None :
@@ -539,6 +547,51 @@ def apply_selfweight_to_formdiagram(self, formdiagram: FormDiagram, normalize=Tr
539547
540548 print (f"Selfweight applied to form diagram. Total load: { sum (abs (formdiagram .vertex_attribute (vertex , 'pz' )) for vertex in formdiagram .vertices ())} " )
541549
550+ def apply_fill_weight_to_formdiagram (self , formdiagram : FormDiagram ) -> None :
551+ """Apply fill weight to the nodes of a form diagram based on the fill surface and local thicknesses."""
552+ if self .fill is None or self .extrados is None :
553+ raise ValueError ("Fill mesh is not set. Please set the fill mesh and extrados before applying fill weight." )
554+
555+ # Step 2: Copy the form diagram for projection
556+ form_fill = formdiagram .copy () # For upper bound (extrados)
557+ form_ub = formdiagram .copy () # For lower bound (intrados)
558+ form_zero = formdiagram .copy () # For zero bound (extrados)
559+ form_zero .vertices_attribute ("z" , 0.0 )
560+
561+ # Step 3: Project form diagram onto extrados (upper bound)
562+ project_mesh_to_target_vertical (form_fill , self .fill )
563+ project_mesh_to_target_vertical (form_ub , self .extrados )
564+
565+ fill_weight = 0.0
566+
567+ # Step 4: Collect heights and assign to form diagram
568+ for vertex in formdiagram .vertices ():
569+ if vertex in form_ub .vertices () and vertex in form_fill .vertices ():
570+ # Get z coordinates from projected meshes
571+ _ , _ , z_fill = form_fill .vertex_coordinates (vertex )
572+ _ , _ , z_ub = form_ub .vertex_coordinates (vertex )
573+ a0 = form_zero .vertex_area (vertex )
574+
575+ if z_fill < z_ub :
576+ z_fill = z_ub
577+
578+ zdiff = z_fill - z_ub
579+ pz_fill = - a0 * zdiff * self .rho_fill
580+ pz0 = formdiagram .vertex_attribute (vertex , "pz" )
581+ formdiagram .vertex_attribute (vertex , "pz" , pz_fill + pz0 )
582+ fill_weight += abs (pz_fill )
583+
584+ # Store z_fill
585+ formdiagram .vertex_attribute (vertex , "zfill" , z_fill )
586+ else :
587+ print (f"Warning: Vertex { vertex } not found in projected meshes" )
588+
589+ print (f"Fill weight applied to form diagram. Total load: { fill_weight } " )
590+
591+ # =============================================================================
592+ # Envelope and target projection operations
593+ # =============================================================================
594+
542595 def apply_bounds_to_formdiagram (self , formdiagram : FormDiagram ) -> None :
543596 """Apply envelope bounds to a form diagram based on the intrados and extrados surfaces.
544597
0 commit comments