only two? how is about no one?
i repeat my question… what does make a face area zero?
Seems I was editing the post at the same time. The sentence should have been: This code does not deal with 3 inline vertices.
Calculating the area T of a triangle is an elementary problem encountered often in many different situations. The best known and simplest formula is:
T = 1/2bh
where b is the length of the base of the triangle, and h is the height or altitude of the triangle.
which means that if any angle of triangle is Zero the Area is Zero.
all three vertices might be in the same point, or two of them, or no one. but for sure two angles of this triangle must be zero, and one 180.
now you have to clear understand that a welding of zero-area triangle vertices is not a trivial task.
ps. a face deleting method we can discuss for isolated faces only
Sure, and in many cases it won’t be even possible, so you would need to build new faces.
Well, as I mentioned earlier, vertices might not always need to be welded. It all depends on what you need so I wouldnt adventure to claim that face deleting must be only for isolated faces.
Think about this, perhaps there is a chance that you do want those holes after all, even if it doesnt make sense in most cases.
what is the most problem of ‘welding’?
we might move vertices in some cases
we can distort uvs
what is the most problem of ‘deleting’?
we might make new open edges
we can break ‘smoothing’
In my case all 0A faces are garbage, byproduct of software that imported the geometry from non-native format.
They all need to be deleted. Their removal does not effect ‘useful’ geometry.
The question is how to do it efficiently.
Kameleon
Sent you a pm.
So what you are saying is that your code is already producing the results you need, meaning you dont need any vertex welding?
If this is correct then this code should be pretty efficient.
Here is an adapted version of your script that should perform very well, though I would restructure the whole script.
PS: if you are on Max 2010 or below, you will need to comment out the “processPostedMessages” line.
(
try(destroydialog ::Myroll) catch()
total_f = 0
processed_f = 0
lp_count = 0
lp_thresh = 5000
df_count = 0
df_thresh = 5000
df_total = 0
lp_fdelta = 0
lp_flast = 0
for o in (selection as array) do total_f += o.numfaces
format "Total faces selected: %
" (formattedPrint total_f format:"8.0u")
rollout Myroll "Del 0 area faces"
(
button bt_start "Start" width:100
on bt_start pressed do
(
st = lp_tlast = lp_tdelta = timestamp()
exit_bool = 0
_getFaceArea = meshop.getFaceArea
_deleteFaces = meshop.deleteFaces
for o in (selection as array) while exit_bool != 1 do
(
faceSel = #{}
obj_tot_f = o.numfaces
for f = 1 to o.numfaces while exit_bool != 1 do
(
if keyboard.escpressed then exit_bool = 1
if (_getFaceArea o f) == 0 do
(
append faceSel f
df_total += 1
)
if lp_count == lp_thresh then
(
lp_tdelta = timestamp() - lp_tlast
lp_tlast = timestamp()
lp_fdelta = processed_f - lp_flast
lp_flast = processed_f
clearlistener()
eta1 = (lp_tdelta ) * (total_f - processed_f) / (lp_thresh * 1000) --seconds
fps_o = (1000.0*processed_f/(lp_tlast - st))
fps_l = (1000.0*lp_fdelta/lp_tdelta)
format "
"
format "Obj Total F: [ % ] Name: [ % ]
" (formattedPrint obj_tot_f format:"8.0u") o.name
format "
"
format "F Sel buffr: [ % ] Last loop: [ % ] FPS loop: [ % ] FPS overall: [ % ]
" (formattedPrint faceSel.numberset format:"8.0u") (formattedPrint lp_tdelta format:"6.0u") (formattedPrint fps_l format:"10.0f") (formattedPrint fps_o format:"10.0f")
format "
"
format " [ % ] [ % ] / [ % ] Removed: [ % ] Percent: [ % ]
" (formattedPrint (100.0*processed_f/total_f) format:"6.2f") (formattedPrint processed_f format:"8.0u") (formattedPrint total_f format:"8.0u") (formattedPrint df_total format:"8.0u") (formattedPrint (100.0*df_total/processed_f) format:"6.2f")
format "
"
format "ETA 1: % secs / % mins Elapsed: % secs / % mins
" (formattedPrint eta1 format:"7.0f") (formattedPrint (eta1/60) format:"7.2f") (formattedPrint ((lp_tlast - st)/1000) format:"7.0u") (formattedPrint (1.0*(lp_tlast - st)/60000) format:"7.2f")
lp_count = 1
)
else
(
lp_count += 1
)
processed_f += 1
)
_deleteFaces o faceSel
windows.processPostedMessages()
)
lp_tlast = timestamp()
fps_o = (1000.0*processed_f/(lp_tlast - st))
format "
"
format "----------------------------------------------------------------------------------------------
"
format " [ % ] [ % ] / [ % ] Removed: [ % ] Percent: [ % ]
" (formattedPrint (100.0*processed_f/total_f) format:"6.2f") (formattedPrint processed_f format:"8.0u") (formattedPrint total_f format:"8.0u") (formattedPrint df_total format:"8.0u") (formattedPrint (100.0*df_total/processed_f) format:"6.2f")
format "Elapsed: % secs / % mins FPS: [ % ]
" (formattedPrint ((lp_tlast - st)/1000) format:"7.0u") (formattedPrint ((1.0*lp_tlast - st)/60000) format:"7.2f") (formattedPrint fps_o format:"10.0f")
)
)
createDialog Myroll width:300 height:200
)
Thank you for your effort…
However, I don’t see any meaningful difference from the code I wrote. As far as I can see, the problem is in selection gathering and your code is using append same as mine.
Why do you think it will be more efficient?
That being said, I will try it when I have the chance, ofc…
One of the big slowdown in your code was because of the Modify panel and the selection of faces, even if you turn off redraw and undo.
This process if highly optimized if you just use meshop.deleteFaces instead, and avoid working with modifiers and selecting/deselecting faces.
The other mayor slowdown (which I just found), was caused by the meshop.getFaceArea function as Ive described in my previous post.
This is at least what Ive found. Perhaps it is working different on your end, but on my end I get these results:
Here is a brief comparison between the original code and the later optimizations:
Optimization 1: avoid the use of Modify Panel and face selection
Optimization 2: avoid the use of meshop.getFaceArea
The code can be further optimized, but the latest one runs around 20/30 times faster and uses half the memory, which is not bad.
nodes = 50
faces = 41600
time:6261 ram:10340720L ORIGINAL
time: 350 ram: 4876232L OPTIMIZATION 1
time: 286 ram: 4873024L OPTIMIZATION 2
nodes = 1
faces = 41600
time:8395 ram:10327144L ORIGINAL
time:5859 ram: 4870776L OPTIMIZATION 1
time: 278 ram: 4870592L OPTIMIZATION 2
Actually one of the things causing a big slowdown was meshop.getFaceArea, which seems to get exponentially slower the higher the number of the mesh faces is.
So for 100 objects with 1000 faces each, it is pretty fast, but for 1 object with 100000 faces it is unbelievable slow.
Also, depending on how the feedback is working for you, you may want to move the windows.processPostedMessages() inside the face loop and trigger it every 1.5/2.0 seconds.
I just have it on the nodes loop, as it works well on my tests.
Ok, here is a new version with a custom code for the face area. Hope it works well for you.
(
try(destroydialog ::Myroll) catch()
total_f = 0
processed_f = 0
lp_count = 0
lp_thresh = 5000
df_count = 0
df_thresh = 5000
df_total = 0
lp_fdelta = 0
lp_flast = 0
for o in (selection as array) do total_f += o.numfaces
format "Total faces selected: %
" (formattedPrint total_f format:"8.0u")
rollout Myroll "Del 0 area faces"
(
button bt_start "Start" width:100
on bt_start pressed do
(
st = lp_tlast = lp_tdelta = timestamp()
exit_bool = 0
_deleteFaces = meshop.deleteFaces
for o in (selection as array) while exit_bool != 1 do
(
faceSel = #{}
obj_tot_f = o.numfaces
for f = 1 to o.numfaces while exit_bool != 1 do
(
if keyboard.escpressed then exit_bool = 1
face = getface o f
v1 = getvert o face[1]
v2 = getvert o face[2]
v3 = getvert o face[3]
area = length (cross (v1-v2) (v1-v3))/2.0
if area == 0 do
(
append faceSel f
df_total += 1
)
if lp_count == lp_thresh then
(
lp_tdelta = timestamp() - lp_tlast
lp_tlast = timestamp()
lp_fdelta = processed_f - lp_flast
lp_flast = processed_f
clearlistener()
eta1 = (lp_tdelta ) * (total_f - processed_f) / (lp_thresh * 1000) --seconds
fps_o = (1000.0*processed_f/(lp_tlast - st))
fps_l = (1000.0*lp_fdelta/lp_tdelta)
format "
"
format "Obj Total F: [ % ] Name: [ % ]
" (formattedPrint obj_tot_f format:"8.0u") o.name
format "
"
format "F Sel buffr: [ % ] Last loop: [ % ] FPS loop: [ % ] FPS overall: [ % ]
" (formattedPrint faceSel.numberset format:"8.0u") (formattedPrint lp_tdelta format:"6.0u") (formattedPrint fps_l format:"10.0f") (formattedPrint fps_o format:"10.0f")
format "
"
format " [ % ] [ % ] / [ % ] Removed: [ % ] Percent: [ % ]
" (formattedPrint (100.0*processed_f/total_f) format:"6.2f") (formattedPrint processed_f format:"8.0u") (formattedPrint total_f format:"8.0u") (formattedPrint df_total format:"8.0u") (formattedPrint (100.0*df_total/processed_f) format:"6.2f")
format "
"
format "ETA 1: % secs / % mins Elapsed: % secs / % mins
" (formattedPrint eta1 format:"7.0f") (formattedPrint (eta1/60) format:"7.2f") (formattedPrint ((lp_tlast - st)/1000) format:"7.0u") (formattedPrint (1.0*(lp_tlast - st)/60000) format:"7.2f")
lp_count = 1
)
else
(
lp_count += 1
)
processed_f += 1
)
_deleteFaces o faceSel
windows.processPostedMessages()
)
lp_tlast = timestamp()
fps_o = (1000.0*processed_f/(lp_tlast - st))
format "
"
format "----------------------------------------------------------------------------------------------
"
format " [ % ] [ % ] / [ % ] Removed: [ % ] Percent: [ % ]
" (formattedPrint (100.0*processed_f/total_f) format:"6.2f") (formattedPrint processed_f format:"8.0u") (formattedPrint total_f format:"8.0u") (formattedPrint df_total format:"8.0u") (formattedPrint (100.0*df_total/processed_f) format:"6.2f")
format "Elapsed: % secs / % mins FPS: [ % ]
" (formattedPrint ((lp_tlast - st)/1000) format:"7.0u") (formattedPrint ((1.0*lp_tlast - st)/60000) format:"7.2f") (formattedPrint fps_o format:"10.0f")
)
)
createDialog Myroll width:300 height:200
)
the time of computation of mesh face area doesn't depend on number faces in the mesh. in your case the slowing down caused by printing the info in the listener. longer listener - slower code execution.
to make everything works more or less right we have to check that our nodes in list are kinda meshes or OK to be converted to mesh. because we want to delete zero faces it's ok to collapse a stack. so
here is a possible realization:
try(destroydialog zeroFaceHunter) catch()
rollout zeroFaceHunter "ZeroFace Hunter" width:200
(
button hunt_bt "Start Hunting" width:192 pos:[4,4]
local min_area = 0.0
on hunt_bt pressed do
(
local ready_for_clean = #()
local getfacearea = meshop.getfacearea
local canceled = off, meshes = 0, zerofaces = 0, allfaces = 0
t1 = timestamp()
m1 = heapfree
for node in selection while not (canceled = keyboard.escpressed) where canconvertto node editable_mesh do
(
meshes += 1
mesh = snapshotasmesh node
allfaces += (num = mesh.numfaces)
faces = #{}
for f=1 to num while not (canceled = keyboard.escpressed) where (getfacearea mesh f) <= min_area do append faces f
if not faces.isempty do
(
append ready_for_clean #(node, faces)
zerofaces += faces.numberset
)
)
t2 = timestamp()
m2 = heapfree
if not canceled then
(
if zerofaces > 0 then
(
if
(
querybox (ready_for_clean.count as string + " node(s) and " + zerofaces as string + " faces are waiting.
Do you want to clean them up?") title:"ZeroFace Hunter:"
)
do undo "Delete ZERO faces" on
(
suspendEditing()
deletefaces = meshop.deletefaces
t3 = timestamp()
m3 = heapfree
for node in ready_for_clean do
(
converttomesh node[1]
deletefaces node[1] node[2]
)
t4 = timestamp()
m4 = heapfree
format "pocessed:
nodes:% faces:%
" meshes allfaces
format "searched:
time:% memory:%
" (t2-t1) (m1-m2)
format "deleted:
time:% memory:%
" (t4-t3) (m3-m4)
resumeEditing()
)
)
else messagebox "There is no nodes to clean." title:"ZeroFace Hunter:"
)
else messagebox "The hunting was canceled. See you next time." title:"ZeroFace Hunter:"
gc light:on
format
)
)
createdialog zeroFaceHunter
edited… some info added… made undoable