I am trying to color each polygon of a Polymesh with a different color based on a specific criteria. It seems that the time required grows very quickly with the number of polygons. For example with around 500 polygons and colors/materials it takes more than 16 seconds and I usually deal with meshes of a few thousands polygons.
My understanding is that for each different color I have to define a new material and then assign this material to the face. This is an extract from the code that I am using at the moment.
# polymesh is a Polymesh defined elsewhere
# number_of_cells is the number of polygons of polymesh
colors = []
start_time = Time.now
number_of_cells.times{colors << [rand(0..255), rand(0..255), rand(0..255)]}
puts "After Random: #{(Time.now-start_time).to_s}"
start_time = Time.now
mod = Sketchup.active_model
ent = mod.entities
mod.start_operation("view_mesh", true)
mesh_view = ent.add_group
mesh_view.layer = mod.layers.add 'color_mesh'
mesh_view.entities.fill_from_mesh(poly_mesh, true, 0)
puts "After Polymesh: #{(Time.now-start_time).to_s}"
start_time = Time.now
mesh_view.entities.grep(Sketchup::Face).zip(colors).each do |f, c|
f.material = c#mat
f.back_material = c#mat
end
mod.commit_operation
puts "After Color: #{(Time.now-start_time).to_s}"
Am I doing something wrong? If so, is there a faster way to do this?
Are you sure about that? It’s the grepping AND color assignment that you’re timing here.
You could time them separately to find out. That way you could also find out if the .zip really makes a difference.
Seems like to gain some speed you have to declare a unique named material yourself instead of letting SketchUp do that for you.
In the top part of your code add: mats = mod.materials
In the material assignment loop do:
c=0
start_time = Time.now
ents.each do |f|
name = 'mat' + c.to_s
m = mats.add(name)
m.color = colors[c]
f.material = m
f.back_material = m
c+=1
end
edit: add a check to see if the material name already exits. If you run this code twice, it’s just as slow as before. My guess is: due to having to check all the time of a material name already exists, add a number, check again etc
edit 2: run code once, for 500 entities is 0.04s Run code again, its 18s
Once you get to around 2000 it seems to get stuck and SU memory use goes to more than 1GB for a simple model. I wonder whether there is a limit in the number of materials SU can handle.
Latest version of the code:
start_time = Time.now
ci = 0
faces.each do |f|
name = 'mat' + colors[ci][0].to_s.rjust(3,"0") + colors[ci][1].to_s.rjust(3,"0") + colors[ci][2].to_s.rjust(3,"0")
if mats[name] == nil
mat = mats.add(name)
mat.color = colors[ci]
else
mat = mats[name]
end
f.material = mat
f.back_material = mat
ci += 1
end
mod.commit_operation
puts "Number of faces: #{ci}"
puts "After Color: #{(Time.now-start_time).to_s}"
SketchUp does have its limitations. 2000+ materials in a file is quite something and makes using the materialpanel also a bit hard. Maybe have a look at it in another way?
Instead of using 2000 materials, each with its own color you could also use just one material with a high resolution texture with a color gradient on it. You could then apply random texture mapping coordinates to each face so each face uses a random but very small (small enough not to recognize the gradient) part of the texture. Could be a lot faster because you don’t run into problems with SketchUp building some sort of a materials array.
Never coded uv coordinates in SketchUp though so it could be a nice challenge. I’m curious though what kind of objects you are color painting and why you need 2000+ colors. Could you maybe post a picture to satisfy our curiosity
I was using random colors just to test the code. In the actual plugin I am generating contour plots and the local color depends on the specific value of a field variable.
I am working on a plugin to perform various simulations (wind, radiation, comfort…) on masterplans. I see you are an architect so I assume you understand what kind of analysis.
For example, the first picture shows the results of a wind analysis for a resort visualized in SU.
For this visualisations I already use textures. They are rendered by an external program and then scaled and placed correctly in SketchUp.
This is quite easy when you have one single flat face like that in the picture, but if you have multiple surfaces with multiple orientations (see second picture) it becomes more complicated as I should keep track in the code of every single surface, its orientation and the associated mesh and I was trying to avoid this.
Very interesting. I used the code shown below to add 3,000 materials to the model, then added them to the faces of a model with over 3,000 faces. Total time was about 3 seconds, without anything ‘interfering’. If I had the native ‘Materials’ window open, it took 544 seconds. If you run it a second time, it shows that testing for the existence of 3,000 materials also takes a little time…
Therein maybe an issue. I can’t test anything past SU 2014 for the time being, but many of the native SketchUp windows do not respect operations. The main surface does, so within an operation that adds materials and changes the faces’ materials, the model won’t change, but the native ‘Materials’ window will refresh every time a material is added to the model. Obviously, this should change. This issue is also part of the reason for the ‘Transaction’ callbacks being added to SU.
Also, you may have plug-ins installed that don’t correctly work with this kind of situation and a MaterialsObserver.
My plug-in has a Materials web dialog, and it actually works much better in this situation than the native window. Then again, I’ve got a timer in my MaterialsObserver to wait until events quit firing to update the UI. Screen grab below.
am = Sketchup.active_model
am_mats = am.materials
FMT_C_M = "mat_%03d_%03d_%03d"
len = 3000
mats = []
mat = nil
name = ''
c = []
ctr = 0
am.start_operation("test", true)
t_start = Time.now
# create colors, add to materials
0.upto(len) { |i|
c = [ 128, (i/256).to_i, i % 256 ]
name = FMT_C_M % c
mats << ( am_mats[name] || (
mat = am_mats.add(name)
mat.color = c
mat ) )
}
t_mid = Time.now
# get faces and change material
faces = am.entities.grep(Sketchup::Face)
faces.each { |f|
f.material = mats[ctr]
f.back_material = mats[ctr]
ctr += 1
break if (ctr > len)
}
am.commit_operation
t1 = t_mid - t_start
t2 = Time.now - t_mid
puts "Faces & Materials Count = #{len}\n" \
" Materials Time = %5.3f\n" \
" Faces Time = %5.3f\n" \
"-------------------------------" % [t1, t2]
Also at the moment I am using one single mesh to represent all the points were the calculation is performed . I am planning to extend the code to avoid this issue, but it is not a priority at the moment.
I have tried to run your code with my mesh of 1080 faces. It hangs and at least after 15 minutes it has not finished yet. I have also looked for MaterialsObserver and removed the only plugin that was defining one. No difference.
I have rerun my plugin and visualised the counter in the status bar. You can clearly see that around 1300 materials the speed drops suddenly and it takes almost 2 seconds to add a new color. I would have expected a more gradual slow down actually.
Odd. I wish I knew what was so different with our models / hardware, etc. When your system hangs, and mine completes in 30 seconds, something is very odd.
My model has a little over 6k faces, and no materials.
I ran it with the loop counter at 6k, and got the following –
Faces & Materials Count = 6000
Materials Time = 11.326
Faces Time = 0.124
--------------------------------
I verified that there were 6k materials, then I changed the red value to 64, and ran it again. Got the following –
Faces & Materials Count = 6000
Materials Time = 29.671
Faces Time = 0.140
--------------------------------
Verified that the model has 12k materials. Then, I ran it again. Got the following –
Faces & Materials Count = 6000
Materials Time = 14.742
Faces Time = 0.078
--------------------------------
Of course, materials aren’t added this time, but it took 14.7 seconds to determine that the 6k materials already existed.
I’ve attached my model in case you want to test with it (1 MB). Regardless, it seems like your application requires ‘painting’ in such a way that using discrete materials for each color may not be possible.
Thanks for checking. The 2nd post results I listed were with the Materials window closed.
I did some more Windows tests (listed below). I didn’t run ‘Large Thumbnails’, but ‘Small’ and ‘Medium’ had little effect on the timing, it’s only when I selected ‘List View’ that the time jumped to the level that your test was.
Odd question, do you know if you had previously opened the Materials Window in the session you did the testing in? Also, what the view setting was?
As an aside, the Web Dialog I showed a screen grab of is the Materials WD in my plug-in. I just opened it with 6k materials, and it loaded. I’ll have to check my code, but I’m really sure I did not do any length checks on the execute_script string that loads the html table. Hence, on Windows, 6k names and color info went in one statement. I guess if there’s a length limit, that didn’t hit it.
Thanks,
Greg
Material Window Closed
Faces & Materials Count = 3000
Materials Time = 4.649
Faces Time = 0.047
Material Window Open - set to Small Thumbnails
Faces & Materials Count = 3000
Materials Time = 6.365
Faces Time = 0.312
Material Window Open - set to Medium Thumbnails
Faces & Materials Count = 3000
Materials Time = 6.458
Faces Time = 0.312
Material Window Open - set to List View
Faces & Materials Count = 3000
Materials Time = 559.321
Faces Time = 0.328
I ran into similar issue a couple of years ago when I created a script to color each face based on it’s normal. On larger meshes I noticed it took an incredible amount of time. And any attempt to interact with the material browser failed.
But even simply creating material seemed to trigger a rebuild of the material browser’s list. I haven’t actually dug into the code to see what goes on though. But there is an issue filed. I’ll bump it with reference to this issue.