Hello again,
I set out to try to create a ruby script that could select faces by their average color, but I got hung up right away in trying to understand the whole UVQ to XYZ texture mapping thing.
I read several forum topics and tried various ideas, however, I got kinda stuck as well as confused.
I’m attaching a model which I found in the 3D warehouse. It isn’t my model and I hope it’s ok to use it for this example.
Here is the example model:
select_by_color_example.skp (5.5 MB)
Here I’ve selected one face in the skintone area and as you can see, the correct RGB value is highlighted [254,188,155]
I found a pure ruby gem called ChunkyPNG which can manipulate PNG images and I was able to install into sketchup easily
Gem.install "chunky_png"
Here is my script so far. In it I select a face in a textured model and then obtain the UVQ coordinates of the face vertices and then try to map them to the texture png itself.
The output of the script gives quite incorrect colors, somewhere in the dark green/orange/brown area.
Thank you for any and all clues or hints
-j_jones
require 'chunky_png'
module JJones
def self.getUVs(front, faceEnt, tw)
verts = faceEnt.outer_loop.vertices
points = verts.collect { |v| v.position }
# uv helper allows you to determine the location (UV coordinates) of a texture on a face
uvh = faceEnt.get_UVHelper(true, true, tw)
if front
uvs = points.collect {|p| uvh.get_front_UVQ(p)}
else
uvs = points.collect {|p| uvh.get_back_UVQ(p)}
end
for i in (0..uvs.length-1)
uvs[i] = uvs[i].to_a # convert point3d to array
uv_w = uvs[i].z
uvs[i].x = uvs[i].x / uv_w
uvs[i].y = uvs[i].y / uv_w
uvs[i].y = uvs[i].y / uv_w
end
return uvs
end # getUVs
def self.describe_face(face, tw=Sketchup.create_texture_writer)
texture = face.material.texture
height = texture.height
width = texture.width
image_height = texture.image_height
image_width = texture.image_width
avg_color = texture.average_color
texture_file_name = texture.filename
image = get_texture_png(texture_file_name)
back_uvs = getUVs(false, face, tw)
front_uvs = getUVs(true, face, tw)
return [texture_file_name, texture, image, image_height, image_width, avg_color, back_uvs, front_uvs]
end # describe_face
def self.get_texture_png(texture_file_name)
image = ChunkyPNG::Image.from_file(texture_file_name)
return image
end # get_texture_png
def self.get_pixel_color_from_uv(png_image, u, v)
texture_width = png_image.width
texture_height = png_image.height
pixel_x = (u * texture_width).to_i
pixel_y = (v * texture_height).to_i
if (pixel_x >= texture_width) || (pixel_y >= texture_height)
# print("pixel(s) #{pixel_x}, #{pixel_y} out of bounds for image?")
return nil
end
# sample the color at these pixel coords
pixel_color = png_image[pixel_x, pixel_y]
r = ChunkyPNG::Color.r(pixel_color)
g = ChunkyPNG::Color.g(pixel_color)
b = ChunkyPNG::Color.b(pixel_color)
a = ChunkyPNG::Color.a(pixel_color)
return [r,g,b,a]
end # get_pixel_color_from_uv
def self.find_similar_faces()
model = Sketchup.active_model
entz = model.active_entities
selection = model.selection
faces = selection.grep(Sketchup::Face)
return unless faces.any?
face = faces[0] # just use the first selected face for this example
texture_file_name, texture, image_png, image_height, image_width, avg_color, back_uvs, front_uvs = JJones.describe_face(face)
print("FRONT_UVS: #{front_uvs}")
front_uvs.each do |p|
u,v = p
pixel_color = JJones.get_pixel_color_from_uv(image_png, u, v)
if ! pixel_color.nil?
print("pixel color: #{pixel_color}")
end
end
print("BACK_UVS: #{back_uvs}")
back_uvs.each do |p|
u,v = p
pixel_color = JJones.get_pixel_color_from_uv(image_png, u, v)
i f ! pixel_color.nil?
print("pixel color: #{pixel_color}")
end
end
end
end # module JJones
# first select a face, then run this to find faces of a similar color, doesn't work yet
JJones.find_similar_faces()
BTW, here is the texture associated with the model. Again, not my model, hope it’s ok to use this for an example.