Face area calculation too slow


#1

Trying to calculate surface areas with greater precision than the “meshop.getfacearea()” method. I need to calculate in double precision, so using a bit of .net to give me greater accuracy. Any thoughts on how I can speed this up without compromising accuracy? (Is really quite slow with high poly objects)

Pt = dotnetclass "System.Windows.Media.Media3D.point3d"
Crossproduct = (dotnetclass "System.Windows.Media.Media3D.Vector3D").Crossproduct
Fn GetFaceArea obj faces =
(
	start = timeStamp()
	TotalArea = 0
	faces = (faces as array)
	for i = 1 to faces.count do
	(
		V = (meshop.GetVertsUsingFace obj i)as array
		if V.count == 3 do
		(
		        Verts = (for j = 1 to 3 collect (getvert obj V[j]))
			Pt1 = dotnetobject Pt (Verts[1].x) (Verts[1].y) (Verts[1].z)
			Pt2 = dotnetobject Pt (Verts[2].x) (Verts[2].y) (Verts[2].z)
			Pt3 = dotnetobject Pt (Verts[3].x) (Verts[3].y) (Verts[3].z)
			TotalArea += ((Crossproduct (pt.subtract pt1 pt2) (pt.subtract pt1 pt3)).length)/2.0
		)
	)
	end = timeStamp()
	format "Processing took % seconds\n" ((end - start) / 1000.0)
	Return (TotalArea)
)

#2

I didn’t check whether unwrap getArea function uses double precision to calc the area (unlikely), but it returned exactly the same result as yours and did it substantially faster. So why complicate things? :slight_smile:

t1=timestamp();hf = heapfree

	addModifier $ (Unwrap_UVW())
	$.modifiers[1].getarea #{1..$.numfaces} &x &y &width &height &areaUVW &areaGeom
	
format "Time: %sec. Mem: %  Area: %\n" ((timestamp()-t1)/1000 as float) (hf-heapfree) areaGeom

-- or simply
t1=timestamp();hf = heapfree

	mesh_area = meshop.getFaceArea $ #all -- returns exactly the same result as your double precision fn

format "Time: %sec. Mem: %  Area: %\n" ((timestamp()-t1)/1000 as float) (hf-heapfree) mesh_area

upd
here’s the optimized version. It is still much slower compared to meshop.getFaceArea and the difference in accuracy not that big


fn GetFaceArea obj faces =
(
	start = timeStamp()
	TotalArea = 0
	sqrtd = (dotNetClass "system.math").sqrt
	
	/*
	-- #1
	fn CrossProdLengthDouble a b =
	(
-- 		x = (a.Y as double) * b.Z - (a.Z as double) * b.Y
-- 		y = (a.Z as double) * b.X - (a.X as double) * b.Z
-- 		z = (a.X as double) * b.Y - (a.Y as double) * b.X
		
		sqrtd (((a.Y as double) * b.Z - (a.Z as double) * b.Y)^2 + ((a.Z as double) * b.X - (a.X as double) * b.Z)^2 + ((a.X as double) * b.Y - (a.Y as double) * b.X)^2)
	)	
	
	for f in faces do
	(
		V  = getFace obj f
		v1 = getvert obj V[1]
		TotalArea += CrossProdLengthDouble (v1 - (getvert obj V[2])) (v1 - (getvert obj V[3]))
	)
	*/
	
	-- #2 more optimized
	tri = snapshotAsMesh obj
	for f in faces do
	(
		V = getFace tri f
		v1 = getvert tri V[1]
		a  = v1 - getvert tri V[2]
		b  = v1 - getvert tri V[3]
		
		TotalArea += sqrtd (((a.Y as double) * b.Z - (a.Z as double) * b.Y)^2 + ((a.Z as double) * b.X - (a.X as double) * b.Z)^2 + ((a.X as double) * b.Y - (a.Y as double) * b.X)^2)
	)
	free tri
	end = timeStamp()
	format "Processing took % seconds.  Area: %\n" ((end - start) / 1000.0) (TotalArea / 2.0)
	TotalArea / 2.0
)

GetFaceArea $ #{1..$.numfaces}

#3

Floating point precision is usually sufficient for any calculation in 3DS MAX. But you should use the work unit scale appropriate for your task. That for some reason, some projects are the last to care …