Hello again,
I decided to try to render this thing called a ‘Klein Bagel’ Klein bottle - Wikipedia
Here’s the resulting skp file
klein_bagel.skp (1.6 MB)
Plotting the edges is easy. The hardest part is figuring out how to create the faces so it looks like a surface.
Here is the script
module FooKleinBagel
def self.draw_edges(entities, start, limit, scale_factor, facet_size)
points = []
edges = []
cycles = []
i = 0
last_point = nil
r = 3
for theta in (start..limit).step(facet_size)
cycle = []
for v in (0..2*Math::PI).step(facet_size)
half_theta = theta / 2.0
sinv = Math.sin(v)
sin2v = Math.sin(2*v)
cos_theta = Math.cos(theta)
sin_theta = Math.sin(theta)
cos_half_theta = Math.cos(half_theta)
sin_half_theta = Math.sin(half_theta)
part1 = (r + (cos_half_theta * sinv) - (sin_half_theta * sin2v))
x = part1 * cos_theta * scale_factor
y = part1 * sin_theta * scale_factor
z = ((sin_half_theta * sinv) + (cos_half_theta * sin2v)) * scale_factor
point = Geom::Point3d.new(x,y,z)
points << point
if last_point
edge = entities.add_line(last_point, point)
edges << edge
cycle << edge
end
last_point = point
i += 1
end # for v
cycles << cycle
end # for theta
return [points, edges, cycles]
end
def self.connect_with_faces(entities, cycle1, cycle2, reverse_start_and_end=FALSE)
cycle1.each_with_index { |edge, i|
edge2 = cycle2[i]
if edge2.nil?
next
end
v1 = edge2.start.position
v2 = edge2.end.position
v3 = edge.start.position
v4 = edge.end.position
if reverse_start_and_end
v1 = edge2.start.position
v2 = edge2.end.position
v3 = edge.end.position
v4 = edge.start.position
end
pts1 = [v1,v2,v3].uniq(&:to_a)
if pts1.length > 2
face1 = entities.add_face(pts1)
else
print("i: #{i}, non unique: #{pts1}")
end
pts2 = [v2,v3,v4].uniq(&:to_a)
if pts2.length > 2
face2 = entities.add_face(pts2)
else
print("i: #{i}, non unique: #{pts2}")
end
} # end for each edge in cycle
end # method connect_with_faces
def self.draw_surfaces(entities, cycles)
last_cycle = cycles[0]
first_cycle = last_cycle
cycles = cycles.drop(1)
cycles.each { |cycle|
self.connect_with_faces(entities, cycle, last_cycle)
last_cycle = cycle
}
# now connect up the end to the beginning to complete it
# reverse the order of the edges for the first_cycle because we're all twisted around
rev_cycle = first_cycle.reverse
self.connect_with_faces(entities, last_cycle, rev_cycle, TRUE)
end # method: draw_surfaces
end # module FooKleinBottle
model = Sketchup.active_model
ents = model.active_entities
scale_factor = 100
facet_size = 0.1
start = 0
limit = 2 * Math::PI
model.start_operation('draw_edges', true)
edge_group = ents.add_group
entities = edge_group.entities
points, edges, cycles = FooKleinBagel.draw_edges(entities, start, limit, scale_factor, facet_size)
model.commit_operation
model.start_operation('draw_surfaces', true)
face_group = ents.add_group
FooKleinBagel.draw_surfaces(face_group.entities, cycles)
model.commit_operation
I came up with this approach to doing it, however, in the last step, I have to connect the first cycle up to the last one by reversing the first cycle and stitching them together with faces. However, there’s a glitchy hole created in this last step.
Is there an easier way to connect the edges with surfaces?
Thank you for any thoughts
-j_jones