Created
January 29, 2022 19:01
-
-
Save jordan9001/d5191d73993af269b47c27d106c15172 to your computer and use it in GitHub Desktop.
BlenderStars
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
# Creates woven regular star polygons in blender of any "density" or "degree" | |
# created for Blender 3.0.1 | |
import bpy | |
import math | |
def layout_points(n, r, d, rings, name): | |
# create a spline with all the required points | |
crv = bpy.data.curves.new(name, 'CURVE') | |
obj = bpy.data.objects.new(name+'_obj', crv) | |
bpy.context.scene.collection.objects.link(obj) | |
crv.bevel_mode = 'ROUND' | |
crv.bevel_depth = d | |
crv.bevel_resolution = 16 | |
crv.dimensions = '3D' | |
crv.twist_mode = 'Z_UP' | |
ring_off = (2 * math.pi) / (rings * n) | |
for ri in range(rings): | |
crv.splines.new('BEZIER') | |
# add points | |
crv.splines[ri].bezier_points.add(n-1) | |
rad = (2 * math.pi) / n | |
for i in range(n): | |
ang = (rad*i) + (ring_off * ri) | |
pt = (math.cos(ang) * r, math.sin(ang) * r, 0.0) | |
crv.splines[ri].bezier_points[i].co = pt | |
crv.splines[ri].bezier_points[i].handle_left = pt | |
crv.splines[ri].bezier_points[i].handle_right = pt | |
crv.splines[ri].use_cyclic_u = True | |
return (obj, crv) | |
def star_points(crv, deg): | |
# move points to make a star | |
for ri in range(len(crv.splines)): | |
pos = [tuple(p.co) for p in crv.splines[ri].bezier_points] | |
l = len(crv.splines[ri].bezier_points) | |
for i in range(l): | |
ni = (i * (deg+1)) % l | |
pt = pos[ni] | |
crv.splines[ri].bezier_points[i].co = pt | |
crv.splines[ri].bezier_points[i].handle_left = pt | |
crv.splines[ri].bezier_points[i].handle_right = pt | |
def get_star_intersections(crv): | |
# solve intersection points against one line | |
# return list of numbers from 0.0 to 1.0 for intersection points | |
p1_1 = crv.splines[0].bezier_points[0].co | |
p1_2 = crv.splines[0].bezier_points[1].co | |
p1_x1 = p1_1[0] | |
p1_y1 = p1_1[1] | |
p1_x2 = p1_2[0] | |
p1_y2 = p1_2[1] | |
# should never be verticle or have no length | |
dy1 = p1_y2 - p1_y1 | |
dx1 = p1_x2 - p1_x1 | |
m1 = dy1 / dx1 | |
#dl = math.sqrt((dy1*dy1) + (dx1*dx1)) | |
#dxdl = dx1/dl | |
#dydl = dy1/dl | |
intersections = [] | |
for ri in range(len(crv.splines)): | |
l = len(crv.splines[ri].bezier_points) | |
for i in range(l): | |
if ri == 0 and i in (0, 1, l-1): | |
continue | |
p2_1 = crv.splines[ri].bezier_points[i].co | |
p2_2 = crv.splines[ri].bezier_points[(i+1)%l].co | |
p2_x1 = p2_1[0] | |
p2_y1 = p2_1[1] | |
p2_x2 = p2_2[0] | |
p2_y2 = p2_2[1] | |
sol_x = 0.0 | |
# check for vertical lines | |
if p2_x1 == p2_x2: | |
# just solve for the y at that x | |
sol_x = p2_x1 | |
else: | |
m2 = (p2_y2 - p2_y1) / (p2_x2 - p2_x1) | |
# check for parallel lines | |
if m2 == m1: | |
# no intersection | |
continue | |
sol_x = ((m1*p1_x1) - p1_y1 - (m2 * p2_x1) + p2_y1) / (m1-m2) | |
sol_y = (m1 * (sol_x - p1_x1)) + p1_y1 | |
# turn into our relative value, see if in range | |
# x_i = dx * i + x1 | |
intr = (sol_x - p1_x1) / dx1 | |
#print(f"\tIntersection with ({ri}: {i},{(i+1)%l}) @ {intr}") | |
if intr <= 0.0 or intr >= 1.0: | |
continue | |
intersections.append(intr) | |
return intersections | |
def weave_star(crv, zoff, degree): | |
# subdivide each line and put nodes inbetween the intersections | |
# move the handles to weave over the intersections | |
intr = get_star_intersections(crv) | |
intr = sorted(intr) | |
if (degree * 2) != len(intr): | |
# something went wrong | |
print(f"\tExpected degree {degree} star to have {degree*2} intersections per line, but got {len(intr)}") | |
print("\t" + ','.join([str(round(i,2)) for i in intr])) | |
# subdivide the middle points for each star | |
num_mid = len(intr)-1 | |
num_pts = len(crv.splines[0].bezier_points) * (num_mid+1) | |
num_dif = num_pts - len(crv.splines[0].bezier_points) | |
for ri in range(len(crv.splines)): | |
# save existing point points | |
ppts = [tuple(p.co) for p in crv.splines[ri].bezier_points] | |
# extend the spline | |
crv.splines[ri].bezier_points.add(num_dif) | |
for i in range(len(ppts)): | |
pt = crv.splines[ri].bezier_points[i*(num_mid+1)] | |
pt.co = ppts[i] | |
pt.handle_left = ppts[i] | |
pt.handle_right = ppts[i] | |
#TODO handles to intersections + zoff? | |
x1 = ppts[i][0] | |
y1 = ppts[i][1] | |
x2 = ppts[(i+1)%len(ppts)][0] | |
y2 = ppts[(i+1)%len(ppts)][1] | |
dx = (x2 - x1) | |
dy = (y2 - y1) | |
for ii in range(0,num_mid): | |
mpt = crv.splines[ri].bezier_points[i*(num_mid+1) + (ii+1)] | |
# xi = dx*i + x1 | |
mid_i = ((intr[ii+1] - intr[ii]) / 2.0) + intr[ii] | |
midloc = ((dx * mid_i) + x1, (dy * mid_i) + y1, 0.0) | |
mpt.co = midloc | |
z = zoff if ii % 2 == 0 else -zoff | |
left_intr = ((dx * intr[ii]) + x1, (dy * intr[ii]) + y1, z) | |
right_intr = ((dx * intr[ii+1]) + x1, (dy * intr[ii+1]) + y1, -z) | |
mpt.handle_left = left_intr | |
mpt.handle_right = right_intr | |
def create_star_paths(points, degree, depth, zoff, name, r=1.0): | |
# check if valid points/degree | |
if points < 3: | |
print(f"Err: Tried to create {points}-star") | |
return None | |
if degree > (((points -1)//2) - 1): | |
return None | |
# create the needed rings | |
# detect how many descrete rings, and make x copies of one ring, rotated | |
stps = 1 | |
while ((stps * (degree+1)) % points) != 0: | |
stps += 1 | |
rings = points // stps | |
pts = stps | |
deg = (((degree+1) * stps) // points) - 1 | |
print(f"{points} {degree} ({rings} * {pts} @ {deg})") | |
obj, crv = layout_points(pts, r, depth, rings, name+f'_r{rings}') | |
if degree > 0: | |
star_points(crv, deg) | |
# modify | |
if degree > 0: | |
weave_star(crv, zoff, degree) | |
# wave via handles | |
# could mess with bevel radius of points too | |
# etc, fun here | |
return obj | |
def create_star_grid(max_n=18, min_n=3, prefix="star"): | |
sp = 2.5 | |
if min_n < 3: | |
min_n = 3 | |
for n in range(min_n, max_n+1): | |
deg = 0 | |
while True: | |
obj = create_star_paths(n, deg, 0.042, 0.1, f"{prefix}_{n}_{deg}", 1.0) | |
if obj == None: | |
break | |
obj.location = (float(n) * sp, float(deg) * sp, 0.0) | |
deg += 1 | |
#create_star_grid(36) | |
#create_star_paths(9, 2, 0.042, 0.12, "ninestar", 1.0) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment