Created
December 17, 2022 22:54
-
-
Save smurfix/3f1e5966265a0094649abb28e65c56e6 to your computer and use it in GitHub Desktop.
Borromean Ring
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
The purpose of this script is to mash three elongated toruses, linked as | |
Borromean Rings, into a single circular torus. | |
https://en.wikipedia.org/wiki/Borromean_ringshttps://en.wikipedia.org/wiki/Borromean_rings | |
The script starts in a state where the linked rings do not touch. | |
Then it reduces both elogation and angles in small steps, removing the | |
overlap and smoothing the resulting mesh every time, until the link | |
lies flat and is no longer stretched. | |
The result should be a contiguous, irregularly-cut third of a torus that | |
does not self-intersect when rotated by 120°. | |
""" | |
# in Blender's Python console: | |
# filename = "/…/rings.py" | |
# exec(compile(open(filename).read(), filename, 'exec')) | |
import bpy | |
C = bpy.context | |
OBJ = bpy.ops.object | |
MESH = bpy.ops.mesh | |
import math as m | |
DEG = m.pi/180 | |
import time | |
# Ring data | |
# elongation, sufficient to remove touching | |
E=1.6 | |
# ring angle | |
A=30 | |
# radii | |
R1=1 | |
R2=0.2 | |
# How fine grained do we want this? | |
steps=30 | |
def deleteAllObjects(): | |
""" | |
Deletes all objects in the current scene | |
""" | |
deleteObjects = {'MESH', 'CURVE', 'SURFACE', 'META', 'FONT', 'HAIR', | |
'POINTCLOUD', 'VOLUME', 'GPENCIL', 'ARMATURE', 'LATTICE', | |
'EMPTY'} | |
# Select all objects in the scene to be deleted: | |
OBJ.mode_set(mode='OBJECT') | |
OBJ.select_all(action='DESELECT') | |
for o in C.scene.objects: | |
o.select_set(o.type in deleteObjects) | |
# Deletes all selected objects in the scene: | |
OBJ.delete() | |
deleteAllObjects() | |
def setup(): | |
MESH.primitive_torus_add(major_segments=24, major_radius=R1, minor_radius=R2) | |
t = C.active_object | |
t.scale[0]=E | |
t.rotation_euler=[A*DEG, A*DEG, 0] | |
t.name="ring" | |
return t | |
def triples(t): | |
""" | |
Add two copies of t, rotated 120° | |
""" | |
t1 = t.copy() | |
t1.data = t.data.copy() | |
C.collection.objects.link(t1) | |
t1.rotation_euler[2]=120*DEG | |
t2 = t.copy() | |
t2.data = t.data.copy() | |
C.collection.objects.link(t2) | |
t2.rotation_euler[2]=-120*DEG | |
return t1,t2 | |
def diff(a, b): | |
name = a.name | |
mod_bool = a.modifiers.new('diffy', 'BOOLEAN') | |
mod_bool.operation = 'DIFFERENCE' | |
# Set the object to be used by the modifier. | |
mod_bool.object = b | |
bpy.context.view_layer.objects.active = a | |
# Apply the modifier. | |
res = OBJ.modifier_apply(modifier = 'diffy') | |
OBJ.select_all(action='DESELECT') | |
b.select_set(True) | |
OBJ.delete() | |
a = bpy.data.objects[name] | |
a.select_set(True) | |
return a | |
def simplify(t): | |
merge_threshold = 0.01 | |
OBJ.select_all(action='DESELECT') | |
t.select_set(True) | |
name = t.name | |
OBJ.mode_set(mode='EDIT') | |
MESH.select_all(action='SELECT') | |
MESH.remove_doubles(threshold = merge_threshold) | |
MESH.select_all(action='DESELECT') | |
#MESH.select_mode(type = 'FACE') | |
#MESH.select_interior_faces() | |
#MESH.delete(type='FACE') | |
OBJ.mode_set(mode='OBJECT') | |
OBJ.select_all(action='DESELECT') | |
t = bpy.data.objects[name] | |
t.select_set(True) | |
return t | |
def work(t, f, delay=0.5): | |
t.scale[0]=1+(E-1)*f # scale=1…E for f=0…1 | |
t.rotation_euler=[A*DEG*f, A*DEG*f, 0] | |
a,b = triples(t) | |
t = diff(t,a) | |
t = diff(t,b) | |
t = simplify(t) | |
if delay: | |
bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1) | |
time.sleep(delay) | |
return t | |
t = setup() | |
for n in range(steps+1): | |
t = work(t, 1-n/steps) | |
# if n == 11: | |
# break | |
# show the combined result | |
# triples(t) | |
# This script exhibits a problem (Blender 3.3.1, Debian Unstable). | |
# | |
# On my system, after a few steps Blender shows what looks like memory | |
# corruption. The "simplify" step either resurrects one of the copied | |
# meshes that should have been deleted by the "diff" step, adding it to the | |
# ring, or it creates a non-manifold fragment. | |
# | |
# Reducing E to 1.5, which causes the rings to initially overlap, triggers | |
# the problem on n==4 instead of 11. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment