Group encountered a NIL issue while executing Subtract, and the copy without group. copy was also deleted


The entities of two goups are not completely overlapping, only partially face overlapping, but the subtract operation will also result in nil. The subtract results presented in the Sketchup are different every time.
无标题.skp (874.3 KB)

The situation with nil is sporadic. The data of wall is in wall.txt
wall.txt (5.1 KB)

The key code is as follows:

type or paste code here


def to_su_wall(wall)
  
  begin
    comp_def = Sketchup.active_model.definitions.add "Test"
    entities = comp_def.entities

    wall_group = entities.add_group
    build_group(wall_group, wall.build_element)

    openings = wall.openings
    wall_group = build_openings_group(openings, entities, wall_group)

    # the wall data is from storey.walls[0] 
    trans = Geom::Transformation.new(ThTCH2SUConvertUtil.to_su_point3d(storey.origin))
    instance = model.active_entities.add_instance(comp_def, trans)

  rescue => e
    e.message
  end
end

def build_openings_group(openings, entities, wall_group)
  wall_group_copy = wall_group.copy
  openings.each{ |opening|
    opening_group = entities.add_group
    build_group(opening_group, opening.build_element)
    # subtract
    subtract_opening_group = opening_group.subtract(wall_group)
    if subtract_opening_group.nil?
      entities.erase_entities opening_group if !opening_group.nil? and !opening_group.deleted?
      wall_group = wall_group_copy
    else
      wall_group = subtract_opening_group
    end
  }
  return wall_group
end


def build_group(group, build_element)
  polyline = build_element.outline.shell, 
  origin = build_element.origin
  points = []
  if !origin.nil?
    polyline.points.each { |pt|
      new_point = pt.dup
      new_point.z = pt.z + origin.z
      points.push new_point
    }
  else
    points = polyline.points
  end
  edges = []
  polyline.segments.each{ |segment|
    if segment.index.length == 2
      pt1 = Geom::Point3d.new(points[segment.index[0]].x.mm, points[segment.index[0]].y.mm, points[segment.index[0]].z.mm)
      pt2 = Geom::Point3d.new(points[segment.index[1]].x.mm, points[segment.index[1]].y.mm, points[segment.index[1]].z.mm)
      if pt1 != pt2
        edges.push group.entities.add_line(pt1, pt2)
      end
    else
      edges = edges + to_su_curve_edge(group, points[segment.index[0]], points[segment.index[1]], points[segment.index[2]])
    end
  }
  face = group.entities.add_face(edges)
  face.reverse! if face.normal.z < 0 
  face.pushpull(build_element.height.mm)
end


Moved to the right category

Please post the code in the right format and try to upload an example model in order to be helped

1 Like

Thanks. I have uploaded the code and data.

Maybe this should include an instance, not a definition.
Entities is not something unique in your model… you should specify a instance from the definition

Sorry, incorrect. In SketchUp models only definitions have an Entities collection. Instances do not have them.

Then here entities is only “test” entities?

Here, … entities is the reference to the "Test" definition’s Entities collection, which at this point has no Drawingelement < Entity members.

Directly after these statements the code added a Group instance to this "Test" definition’s entities collection (which also transparently caused a parent definition for the group instance to be created in the model’s DefinitionList collection.)

Then the wall_group instance was passed to the build_group() method, which did bunch of calculating before using the group that was passed into the method.


@carol I suggest you immediately add a cpoint to your groups at their origin to prevent SketchUp from deleting your empty groups. So, in to_su_wall() you should do …

def to_su_wall(wall)
  begin
    comp_def = Sketchup.active_model.definitions.add "Test"
    entities = comp_def.entities

    wall_group = entities.add_group
    cpt = wall_group.definition.entities.add_cpoint(ORIGIN)

    build_group(wall_group, wall.build_element)

    openings = wall.openings
    wall_group = build_openings_group(openings, entities, wall_group)

    cpt.erase!

    wall_group
  rescue => e
    e.message
  end
end

Your code can erase the cpoint when it is no longer needed.

1 Like

Thanks for the explanation @DanRathbun

1 Like

Yes. Finally, I will generate an instance based on this definition.
I have added the code like this:

trans = Geom::Transformation.new(ORIGIN)
instance = Sketchup.active_model.active_entities.add_instance(comp_def, trans)

But my question is,
Why does the subtract() have nil or unexpected results. For example, sometimes the result is nil, sometimes the result obtained is one or two faces, and sometimes the result obtained in A-B looks like B-A. This may be because the entities in my two groups may have overlapping faces, but how can I avoid this situation.
The second question is,
When I try to use copy() to backup wall_group, and then reassign the wall_group_copy to wall_group when I obtain the result of NIL. This is to avoid the exception of group being deleted and resulting in nil after subtract(), but it was found that both wall_group and wall_group_copy were deleted at the same time.

Thanks for your guidance,
But after I added the line of cpt=…, there will still be calculation errors of subtract() or the wall_group_copy that copy from wall_group being deleted

The documentation is clear. If nil is returned then one or both of the volumes were not manifold.

You can check before or after the subtract() call by using the manifold? query method.

As to this, it all depends upon what the expectation was beforehand.

Sorry, I objectively considered it to be a non-manifold entity. Thank you for your guidance. I will add some checks and try again.
However, I noticed that the documentation mentions that ‘copy’ would return a new group. However, why is it that when ‘subtract’ encounters a ‘nil’ value on the original group, both the original group and its copy are deleted?
At the same time, as shown in the .skp document and screenshot, I expected to achieve the effect of subtracting the smaller entity from the larger entity using the ‘subtract’ method. But the results are always inconsistent, with different outcomes for the remaining shapes in the file, all of which were processed using the same data and method.

The copy() method both copies the definition and creates an instance of the new group definition.

You mean when the original group us non-manifold but the second group is manifold?

I just tried this in SketchUp 24.0 and the result was nil (as advertised) and the group instances were unaffected. They remained and were not deleted.

Perhaps there is a bug in SU 2020 ?

Thank you for your answer.
Firstly, I have two non-manifold groups, wall_group and opening_group. Whenever I attempt to subtract opening_group from wall_group using opening_group.subtract(wall_group), the results vary unpredictably. I suspect this is because of precision issues with overlapping faces, which sometimes leaves behind a single face after clipping.

Secondly, in the SU 2020, let’s assume I have a manifold group called a_group and a non-manifold group named b_group. Before performing the subtraction, I create a backup of a_group called a_group_copy. However, I’ve noticed that when I execute b_group.subtract(a_group), if the result is nil, both a_group and its backup a_group_copy are unexpectedly deleted.

@carol Sun, you cannot use Boolean tools upon non-manifold groups (or instances).
Both objects must be manifold.

Thank you. Later, I will use the manifold method to check before subtracting.