# Heal the klein bagel

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
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
else
print("i: #{i}, non unique: #{pts1}")
end
pts2 = [v2,v3,v4].uniq(&:to_a)
if pts2.length > 2
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)

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)

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

1 Like

``````edges.each do |edge|
edge.find_faces
edge.soft= true
edge.smooth= true
end
``````
1 Like

There was a tiny error in the way the edge loops were being created. Here I’ve changed the draw_edges() method to isolate each cycle from its neighbors.

``````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 = []
first_point = nil
last_point = nil

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 first_point
edges << edge
cycle << edge
else
first_point = point
end
last_point = point
i += 1
end # for v

edges << edge
cycle << edge

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
else
print("i: #{i}, non unique: #{pts1}")
end
pts2 = [v2,v3,v4].uniq(&:to_a)
if pts2.length > 2
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 = 1.0/16.0 * Math::PI
limit = 2 * Math::PI

model.start_operation('draw_edges', true)

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)

FooKleinBagel.draw_surfaces(face_group.entities, cycles)

model.commit_operation
``````
1 Like

Thank you

I wish the find_faces would have worked, but I guess the edges were too ambiguous for it to just guess.

But the changes suggested by sWilliams did the trick, thank you!

1 Like

Just wanted to show off this stripedy version

I found out the hard way that there is a limit to the number of materials you can use before poor sketchup gets very bogged down indeed.

I still have a little glitch to fix, but I will re-upload the correction soon.

glitch fixed!

2 Likes