import trimesh,numpy as np,json,hashlib,os,pyvista as pv,pymeshfix
src_original='cfd_ready/Al_Rihla_2022_CFD_0p220m.stl'
src_cluster='Al_Rihla_2022_remeshed_120k.stl'
m=trimesh.load(src_cluster,process=True)
if m.volume<0:m.invert()
# Split only long edges without moving the clustered surface.
v,f=trimesh.remesh.subdivide_to_size(m.vertices,m.faces,max_edge=0.0025,max_iter=5,return_index=False)
r=trimesh.Trimesh(v,f,process=True)
if not (r.is_watertight and r.euler_number==2 and r.is_winding_consistent):
 mf=pymeshfix.MeshFix(r.vertices,r.faces);mf.repair(joincomp=True,remove_smallest_components=True);r=trimesh.Trimesh(mf.points,mf.faces,process=True)
if r.volume<0:r.invert()
out='Al_Rihla_2022_feature_preserving_remesh.stl';r.export(out)
origpv=pv.read(src_original).triangulate(); rpv=pv.wrap(r)
d_rem=np.abs(np.asarray(rpv.compute_implicit_distance(origpv).point_data['implicit_distance']))
opv=pv.wrap(trimesh.load(src_original,process=True));d_src=np.abs(np.asarray(opv.compute_implicit_distance(rpv).point_data['implicit_distance']))
def qs(x):return {str(q):float(np.quantile(x,q)) for q in [0,.5,.9,.95,.99,.999,1]}
tri=r.triangles;l=np.linalg.norm(tri[:,[1,2,0]]-tri[:,[0,1,2]],axis=2);qual=4*np.sqrt(3)*r.area_faces/np.sum(l*l,axis=1)
rep={'label':'Feature-preserving remesh of unverified third-party Al Rihla model','method':'PyACVD clustering to 120000 target vertices, followed by planar subdivision of edges longer than 2.5 mm','vertices':len(r.vertices),'triangles':len(r.faces),'watertight':r.is_watertight,'winding_consistent':r.is_winding_consistent,'euler_number':r.euler_number,'body_count':r.body_count,'volume_m3':float(r.volume),'area_m2':float(r.area),'extents_m':r.extents.tolist(),'edge_length_m_quantiles':qs(r.edges_unique_length),'triangle_quality_quantiles':qs(qual),'remesh_to_source_distance_m':qs(d_rem),'source_to_remesh_distance_m':qs(d_src),'acceptance_note':'99% of source vertices lie within the reported 0.99 source-to-remesh distance. Maximum deviations may represent removed microscopic or needle-triangle features.','scientific_limitation':'No physical scan or manufacturer CAD was available; real seam and texture fidelity is unverified.','sha256':hashlib.sha256(open(out,'rb').read()).hexdigest(),'bytes':os.path.getsize(out)}
open('Al_Rihla_2022_feature_preserving_remesh_validation.json','w').write(json.dumps(rep,indent=2)+'\n');print(json.dumps(rep,indent=2))
