How to see the results of this method?
-
def sum_area( material, entities, tr = Geom;;Transformation.new ) area = 0.0 for entity in entities if entity.is_a?( Sketchup;;Group ) area += sum_area( material, entity.entities, tr * entity.transformation ) elsif entity.is_a?( Sketchup;;ComponentInstance ) area += sum_area( material, entity.definition.entities, tr * entity.transformation ) elsif entity.is_a?( Sketchup;;Face ) && entity.material == material # (!) The area returned is the unscaled area of the definition. # Use the combined transformation to calculate the correct area. # (Sorry, I don't remember from the top of my head how one does that.) # # (!) Also not that this only takes into account materials on the front # of faces. You must decide if you want to take into account the back # size as well. area += entity.area end end area end
Thank you for your help!
-
Please learn to put your code in 'code' format.
I have done it for you - edit the post to see what I did...
To do single-lines use the 'ruby' formating [as I show below]...Now for your question...
The method return the area as it completes its processing, so something like:
my_area = sum_area( some_material, some_entities, some_transformation )
will return a float in square-inches thus1234.56789
- here assigned to a referencemy_area
If you run the code in the Ruby Console it should complete showing the area you want.
Obviously to make it into some other units - e.g. m² - use a suitable conversion factor...
my_area_m2 = sum_area( some_material, some_entities, some_transformation )*0.00064516
The 3rd argument transformation could be passed as empty, or a 'blank' new transformation... -
But it seems no way to calculate the area...
-
I don't understand your comment.
Please try and explain it another way...
I can get your code to report the area of selected faces or faces inside a group etc, thus:
my_area_m2 = sum_area( nil, Sketchup.active_model.selection )*0.00064516
This works for NO material [nil
].
If you want to get another material you must pass it as a 'material', perhaps found 'by name' thus:
m2 = 0.0 mat = Sketchup.active_model.materials['Red'] m2 = sum_area(mat, Sketchup.active_model.selection) if mat
-
@ceit81729 said:
But it seems no way to calculate the area...
This is a rather interesting problem. If you create a group, select it and run code that gets the area of say "red" material, then scale the group and repeat the code, you will get the same area. So somehow the scale factors have to be considered but how and which ones? If you open the group for edit and run the code, you will get the correct area of the scaled face.
-
So the question is actually, "How to determine the area of a specific face, given that the face is inside a 'container' that can be scaled in any of its three axes..."
First we can get the axes of the transformation [tr].
xaxis = tr.xaxis yaxis = tr.yaxis zaxis = tr.zaxis
we can also get the normal of the face.
normal = face.normal
We can get the three possible scaling factors for the transformation, thus:
xscale = X_AXIS.transform(tr).length.to_f
yscale = Y_AXIS.transform(tr).length.to_f
zscale = Z_AXIS.transform(tr).length.to_f
Note that these do not show negative scaling if handed etc - but for area calculations you don't want that anyway...
Next we need to apply each of the three scaling factors to the face's area, having due regard in turn for the angle between the face.normal and each of the three axes relating to each scale factor...
Obviously a face that is perpendicular to an axis is fully scaled as that axis's scaling, but one that is parallel to the axes is unaffected by that axis's scaling - a box scaled in the Z by 2.0 has no scaling of its faces that have a normal parallel to the Z_AXIS, but the side faces will scale in the other axes...
Something like:
ascaler = 1.0 xscaler = Math.sin(xaxis.angle_between(normal)) ascaler *= xscaler*xscale if xscaler > 0 yscaler = Math.sin(yaxis.angle_between(normal)) ascaler *= yscaler*yscale if yscaler > 0 zscaler = Math.sin(zaxis.angle_between(normal)) ascaler *= zscaler*zscale if zscaler > 0 area *= ascaler
Note - I haven't tested this !
But you should get the idea... -
Do a search on this forum for "binormal" and see AdamB's code.
-
Thanks, I knew there was something somewhere - but it was six and a half years ago !
To précis a version of AdamB's [much cleverer] code:def self.face_area(mat, context, tr) area = 0.0 context.entities.grep(Sketchup;;Group).each{|e| area += self.face_area(mat, e, e.transformation) } context.entities.grep(Sketchup;;Component_Instance).each{|i| area += self.face_area(mat, i.definition, i.transformation) } context.entities.grep(Sketchup;;Face).select{|f| f.material == mat }.each{|f| normal = f.normal binormal = Geom;;Vector3d.new(normal.y, normal.z, normal.x) tangent = (normal * binormal).normalize areascale = (tr * binormal).length * (tr * tangent).length area += ( f.area * areascale ) } return area end
The 'mat' must be a reference to a model.material [or nil]...
The 'context' must be either the model or a group or a definition [i.e. something that will have 'entities']
The transformation 'tr' must be a 'Geom::Transformation' - even it's 'blank', so the arguments are:
(material, model, Geom::Transformation.new()) (material, group, group.transformation) (material, instance.definition, instance.transformation)
The method returns the area of all faces within the context, included nested containers, and adjusts them to allow for any scaling of those containers...
To apply transformations of nested containers which themselves are within transformed containers etc, you need to iterate the method within itself, passing/reapplying transformations [similar to the earlier example 'sum_area' method] which I have botched in here... Note how its 'self.' assumes it's called as a method within a module.
Advertisement