SketchUp really should provide a method in the API for detecting and/or correcting reversed faces in manifolds.
I have yet to come up with a really efficient and successful way of dealing with reversed faces.
SketchUp really should provide a method in the API for detecting and/or correcting reversed faces in manifolds.
I have yet to come up with a really efficient and successful way of dealing with reversed faces.
SketchUp shouldnât even mark mixed faces manifolds as Solids.
It should detect if manifolds have their faces:
I think it lies in the SketchUp concept to be forgiving, but I really like the concept of solid voids, as it is a perfectly valid topology.
I think the issue is differentiating âvoids inside solidsââŚ
the check needs to to all
âback faces face back facesâ > âSolid with or without VoidââŚ
john
Have you cribbed my âsolidsolverâ code ?
Around line #456
# Based on Shellify by Anders Lyhagen && some later TT's recoding
# methods added/adjusted by TIG
class Shell
If checks the surfaces of a known solid and tries to reverse them all âoutwardsâ where necessary.
It finds connected surfaces and does a centered raytest to find if a face is reversed etc and fixes itâŚ
It seems to work in most cases,
I know, but I wasnât ready yet.
I know that a void inside a Solid (the grouped geometry) subtracts from the Solid. At least that is what it should do. Whatever the outcome of ones geometry, all faces should have their back towards where the material is, irrespective of how many manifolds are in the same grouped environment, resulting in a SketchUp Solid.
I guess so. But people learn fast enough. Questioning âwhy isnât my manifold seen as solid?â You make that mistake once and never forget. Unlike with wresteling with a face that looks planar but inreality isnât. SketchUp has its tolerance and cannot do without.
I should probably take a second look at your code. Iâve previously tried to come up with my own âsimpleâ algorithm but it fails for certain solids.
Wouldnât a center ray test potentially fail with a convoluted solid like this roof?
Actually, with this example I can easily check the Z component of the face normal (of the inclined roof planes) and determine if it is positive or negative which tells me which face(s) to reverse. The bottom face is also another easy check of the face normal to see if it is parallel (same direction) to the Z axis.
The problem is when I have some gable ends added to the mix, with randomly reversed faces.
I can quite easily determine those vertical faces (gable ends) however it is not readily apparent on how to determine if they are reversed or not. One thought is to compare them to a properly oriented bottom face or inclined (faces) and then cheat it this way.
Test itâŚ
In the example you illustrated each of the roof face.normal.z are > 0
So for each face, if itâs correctly oriented OK⌠but if itâs not, then reverse that faceâŚ
My âShellâ code is probably too complex for what you want !
A simple face.normal.z
check with a face.reverse
if NOT should sufficeâŚ
To check the gable-faces orientation you could check everything else first, then deal with just the vertical facesâŚ
Compare their normal orientations with adjoining face.normal results.
If necessary, reverse the gable-face to matchâŚ
Youâve just confirmed what I thought might be the simplest solution to this problem.
Here is a snippet from a previous post that I am still wrapping my head around how it is supposed to work:
face1.reverse! if edge.reversed_in?(face0) == edge.reversed_in?(face1)
A face is oriented according to its face loop. If the face loop is counter clockwise as seen from the camera you will see the front material.
If you look in the first image below, there are two faces sharing an edge. In order for BOTH faces to be correctly oriented the face loops at the edge must use the edge in opposite directions. For a solid to be consistently oriented, then for every edge the two connected faces must use the edge in opposite directions.
face1.reverse! if edge.reversed_in?(face0) == edge.reversed_in?(face1)
This code reverse face1
if we have the situation in the second image. That is, we know that face0
is correctly oriented. If face1
uses the common edge
in the same direction as face0
, then it must be reversed.
Thus, if we start with a face that we know is correctly oriented we can orient all other faces in the solid consistently by unwinding the geometry from the start face.
In my suite of Plugins, FredoTools, there is one tool called AutoReverseFaces. It normally works on true manifold solids, but also on pseudo-solids, independently of the view camera.
Can you try it on one of your roofs and see if it does the job.
If so, Iâll post the algorithm.
Sure, Iâll give it a try.
Now Iâm just trying to think up the best way (most efficient) to cycle through each (wall) vertical face and then grab an adjacent face (and its common edge) without having to eat up too much processing power.
The good news is that no two vertical faces will ever be adjacent to each other, so every vertical face will be surrounded by inclined faces (or in some cases, the bottom face) that are all correctly oriented.
Here is my first crack at it:
@wall_faces = @all_roof_faces.find_all {|f| f.normal.perpendicular?(Z_AXIS) }
@wall_faces.each do |w|
edgelist = w.edges
ce = edgelist[0]
facelist = ce.faces
adjface = facelist.find {|a| a != w }
w.reverse! if ce.reversed_in?(adjface) == ce.reversed_in?(w)
end
It is a little verbose, so I can probably declare a few less variables and make it more compact, but I think it might work.
P.S.
Since Iâve already collected the inclined roof faces in a previous section of code, I will replace the first line with this:
@wall_faces = @all_roof_faces - @roof_faces
Slightly compressed, yields this block of code:
@wall_faces = @all_roof_faces - @roof_faces
@wall_faces.each do |w|
ce = w.edges[0]
adjface = ce.faces.find {|a| a != w }
w.reverse! if ce.reversed_in?(adjface) == ce.reversed_in?(w)
end
Since Iâve managed to deal with all of the inclined roof faces and bottom face previously and the wall faces are always adjacent to only properly oriented faces the solution is actually relatively simple.
This still does not fully solve my quest for a general method to check manifolds for reversed faces but it does solve my immediate problem.
Or just post a model with mixed reversed faces and I will check
Give this one a go:
Turn off the sheathing layer, and you can delete or ignore the roof outline group (blue).
Iâm mostly interested in the roof primitive (lumber color). This group should be a solid and it needs to be clean (no reversed faces).
With my current algorithm the roof primitive is a solid and the reversed faces are now eliminated, you can verify per the model.
P.S.
The reason that I want it to be a valid solid is that it should be available to the user to perform additional boolean operations with SketchUpâs or @Eneroth3âs solid tools. And just as a plug for Enerothâs tools, I will say that they do have some distinct advantages over the built in tools.
It seems to work fine.
But this is somehow normal because your roof is based on solids, since you keep the horizontal base.
Iâll make more thorough tests tomorrow to see what happens in degraded conditions, when the roofs elements are not solid.
I donât think that will work because you just randomly access the faces in your face array. What you need is something like a flood fill, that is, you start with a correct face and then you unwind the geometry from there, reaching new faces from faces already processed and so on.
Something like:
def self.orient_faces(start_face)
face_stack = [start_face]
processed = Set.new
while face_stack.length > 0 do
face0 = face_stack.pop
processed.add(face0)
face0.edges.each { |edge|
next if processed.include?(edge)
face1 = get_other_face(edge, face0)
next if face1.nil? || processed.include?(face1)
face1.reverse! if edge.reversed_in?(face0) == edge.reversed_in?(face1)
face_stack.push(face1)
processed.add(edge)
}
end
end
def self.get_other_face(edge, face)
return nil if edge.faces.length == 1
return edge.faces[0] == face ? edge.faces[1] : edge.faces[0]
end
The only problem remaining is to find a start face and orient it correctly. I think something like the below will work:
def self.get_start_face(ents)
vxs = ents.grep(Sketchup::Edge).map{ |e| e.vertices}.flatten!.uniq!
max_vx = vxs.max_by { |vx| vx.position.z }
min_e = max_vx.edges.min_by { |e|
(e.start.position - e.end.position).normalize!.z.abs
}
max_f = min_e.faces.max_by { |f| f.normal.z.abs }
return max_f.normal.z < 0 ? max_f.reverse! : max_f
end
Send in an array with all the faces in your solid and you should get a correctly oriented start face in return.
Even though my current algorithm works for what I currently have, I may have use for a more general solution as my roof primitives are going to get a lot more ugly with the introduction of the secondary roof concept (ie. connecting two complex roofs together).