api-issue-tracker
api-issue-tracker copied to clipboard
Depending on vertex order, #add_face may produce inconsistent geometry
- SketchUp/LayOut Version: SketchUp Pro 23.1.318
- OS Platform: MacOS
I'm trying to create a square ring and add a triangle to its side (I refer to this mesh as 'VRing' in the snippet). When using Sketchup::Entities#add_face, I've noticed that rotating the order of the triangle's vertices (while maintaining their CCW orientation) yields different outcomes. In the 'bad' case, it ruins the geometry of the ring.
Below, I rotate the vertices of the triangle. The first and the second attempts yield the expected result.
Topology is really broken:
class MakeVRing
# CCW, starting with lowest leftmost point
SQUARE_PTS = [
[-1, -1, 0], [1, -1, 0], [1, 1, 0], [-1, 1, 0]
].map { |a| Geom::Point3d.new(*a) }
TRIANGLE_PTS = [
[1, -1, 0], [1, 1, 0], [0, 0, 0]
].map { |a| Geom::Point3d.new(*a) }
def initialize(opts)
@entities = opts[:entities]
@radius_ext = opts[:radius]
@thickness = opts[:thickness]
@radius_int = @radius_ext - @thickness
end
def execute(orig_v, idxs)
orig_v = Geom::Vector3d.new(orig_v)
add_ring(orig_v)
add_triangle(orig_v, idxs)
end
private
def add_ring(orig_v)
pts_e = SQUARE_PTS.map do |pt|
Geom::Transformation.scaling(@radius_ext) * pt + orig_v
end
pts_i = SQUARE_PTS.map do |pt|
Geom::Transformation.scaling(@radius_int) * pt + orig_v
end
f_e = @entities.add_face(pts_e)
f_i = @entities.add_face(pts_i)
f_i.erase!
f_e.reverse!
end
def add_triangle(orig_v, idxs)
pts =
idxs
.map { |idx| TRIANGLE_PTS[idx] }
.map do |pt|
Geom::Transformation.scaling(@thickness) * pt + orig_v + [@radius_int, 0, 0]
end
@entities.add_face(pts)
end
end
command = MakeVRing.new({
entities: Sketchup.active_model.entities,
radius: 50,
thickness: 10
})
idxs = [0, 1, 2]
3.times do |i|
command.execute([100 + i * 150, 100, 0], idxs)
idxs.rotate!
end
A better example might be constructing a solid torus instead of a ring. In that case, using the 'wrong' vertex order when adding a triangle results in a non-manifold surface.
class MakeVRing
# CCW, starting with lowest leftmost point
SQUARE_PTS = [
[-1, -1, 0], [1, -1, 0], [1, 1, 0], [-1, 1, 0]
].map { |a| Geom::Point3d.new(*a) }
TRIANGLE_PTS = [
[1, -1, 0], [1, 1, 0], [0, 0, 0]
].map { |a| Geom::Point3d.new(*a) }
def initialize(opts)
@entities = opts[:entities]
@radius_ext = opts[:radius]
@thickness = opts[:thickness]
@radius_int = @radius_ext - @thickness
end
def execute(orig_v, idxs)
orig_v = Geom::Vector3d.new(orig_v)
add_ring(orig_v)
add_triangle(orig_v, idxs)
end
private
def add_ring(orig_v)
pts_e = SQUARE_PTS.map do |pt|
Geom::Transformation.scaling(@radius_ext) * pt + orig_v
end
pts_i = SQUARE_PTS.map do |pt|
Geom::Transformation.scaling(@radius_int) * pt + orig_v
end
f_e = @entities.add_face(pts_e)
f_i = @entities.add_face(pts_i)
f_i.erase!
f_e.reverse!
f_e.pushpull(-@thickness)
end
def add_triangle(orig_v, idxs)
pts =
idxs
.map { |idx| TRIANGLE_PTS[idx] }
.map do |pt|
Geom::Transformation.scaling(@thickness) * pt + orig_v + [@radius_int, 0, 0]
end
@entities.add_face(pts)
end
end
command = MakeVRing.new({
entities: Sketchup.active_model.entities,
radius: 50,
thickness: 10
})
idxs = [0, 1, 2]
3.times do |i|
command.execute([100 + i * 150, 100, 0], idxs)
idxs.rotate!
end
Logged as: SKEXT-3886