Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 676d67c

Browse files
committedNov 20, 2017
Rudimentary ribbon support
1 parent c06f74d commit 676d67c

File tree

1 file changed

+103
-11
lines changed

1 file changed

+103
-11
lines changed
 

‎celtic-knot.py

+103-11
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,24 @@
3636

3737
import bpy
3838
import bmesh
39+
from bpy_extras import object_utils
3940
from collections import defaultdict
4041
from mathutils import Vector
4142
from math import pi, sin, cos
4243

4344
HANDLE_TYPE_MAP = {"AUTO": "AUTOMATIC", "ALIGNED": "ALIGNED"}
4445

46+
# Twist types
4547
TWIST_CW = "TWIST_CW"
4648
STRAIGHT = "STRAIGHT"
4749
TWIST_CCW = "TWIST_CCW"
4850
IGNORE = "IGNORE"
4951

52+
# output types
53+
BEZIER = "BEZIER"
54+
PIPE = "PIPE"
55+
RIBBON = "RIBBON"
56+
5057

5158
def get_celtic_twists(bm):
5259
twists = []
@@ -58,6 +65,59 @@ def get_celtic_twists(bm):
5865
return twists
5966

6067

68+
class RibbonBuilder:
69+
def __init__(self, weave_up, weave_down):
70+
self.weave_up = weave_up
71+
self.weave_down = weave_down
72+
self.vertices = []
73+
self.faces = []
74+
self.prev_out_verts = None
75+
self.first_in_verts = None
76+
77+
def start_strand(self):
78+
self.first_in_verts = None
79+
self.prev_out_verts = None
80+
81+
def add_loop(self, prev_loop, loop, forward):
82+
normal = loop.calc_normal() + prev_loop.calc_normal()
83+
normal.normalize()
84+
offset = (self.weave_up if forward else self.weave_down) * normal
85+
86+
center1 = prev_loop.face.calc_center_median()
87+
center2 = loop.face.calc_center_median()
88+
v1 = loop.vert.co
89+
v2 = loop.link_loop_next.vert.co
90+
i = len(self.vertices)
91+
self.vertices.append(v1 + offset)
92+
self.vertices.append(center1 + offset)
93+
self.vertices.append(v2 + offset)
94+
self.vertices.append(center2 + offset)
95+
#self.faces.append([i, i+1, i+2, i+3])
96+
self.faces.append([i, i + 1, i + 2])
97+
self.faces.append([i, i + 2, i + 3])
98+
if forward:
99+
in_verts = [i + 1, i + 0]
100+
out_verts = [i + 3, i + 2]
101+
else:
102+
in_verts = [i + 2, i + 1]
103+
out_verts = [i + 0, i + 3]
104+
105+
if self.first_in_verts is None:
106+
self.first_in_verts = in_verts
107+
if self.prev_out_verts is not None:
108+
self.faces.append(self.prev_out_verts + in_verts)
109+
self.prev_out_verts = out_verts
110+
111+
def end_strand(self):
112+
self.faces.append(self.prev_out_verts + self.first_in_verts)
113+
114+
def make_mesh(self):
115+
me = bpy.data.meshes.new("")
116+
me.from_pydata(self.vertices, [], self.faces)
117+
me.update(calc_edges=True)
118+
return me
119+
120+
61121
class BezierBuilder:
62122
def __init__(self, bm, crossing_angle, crossing_strength, handle_type, weave_up, weave_down):
63123
# Cache some values
@@ -191,7 +251,6 @@ def create_bezier(context, bm, twists,
191251

192252
orig_obj = context.active_object
193253
# Create an object from the curve
194-
from bpy_extras import object_utils
195254
object_utils.object_data_add(context, curve, operator=None)
196255
# Set the handle type (this is faster than setting it pointwise)
197256
bpy.ops.object.editmode_toggle()
@@ -205,6 +264,16 @@ def create_bezier(context, bm, twists,
205264
context.scene.objects.active = orig_obj
206265
return curve_obj
207266

267+
def create_ribbon(context, bm, twists, weave_up, weave_down):
268+
builder = RibbonBuilder(weave_up, weave_down)
269+
visit_strands(bm, twists, builder)
270+
mesh = builder.make_mesh()
271+
orig_obj = context.active_object
272+
object_utils.object_data_add(context, mesh, operator=None)
273+
mesh_obj = context.active_object
274+
context.scene.objects.active = orig_obj
275+
return mesh_obj
276+
208277

209278
def create_pipe_from_bezier(context, curve_obj, thickness):
210279
bpy.ops.curve.primitive_bezier_circle_add()
@@ -237,6 +306,14 @@ class CelticKnotOperator(bpy.types.Operator):
237306
description="Distance to shift curve downward under knots",
238307
subtype="DISTANCE",
239308
unit="LENGTH")
309+
output_types = [(BEZIER, "Bezier", "Bezier curve"),
310+
(PIPE, "Pipe", "Rounded solid mesh"),
311+
(RIBBON, "Ribbon", "Flat plane mesh")]
312+
output_type = bpy.props.EnumProperty(items=output_types,
313+
name="Output Type",
314+
description="Controls what type of curve/mesh is generated",
315+
default=BEZIER)
316+
240317
handle_types = [("ALIGNED","Aligned","Points at a fixed crossing angle"),
241318
("AUTO","Auto","Automatic control points")]
242319
handle_type = bpy.props.EnumProperty(items=handle_types,
@@ -260,6 +337,18 @@ class CelticKnotOperator(bpy.types.Operator):
260337
subtype="DISTANCE",
261338
unit="LENGTH")
262339

340+
def draw(self, context):
341+
layout = self.layout
342+
layout.prop(self, "weave_up")
343+
layout.prop(self, "weave_down")
344+
layout.prop(self, "output_type")
345+
if self.output_type in (BEZIER, PIPE):
346+
layout.prop(self, "handle_type")
347+
layout.prop(self, "crossing_angle")
348+
layout.prop(self, "crossing_strength")
349+
if self.output_type == PIPE:
350+
layout.prop(self, "thickness")
351+
263352
@classmethod
264353
def poll(cls, context):
265354
ob = context.active_object
@@ -274,16 +363,19 @@ def execute(self, context):
274363
bm = bmesh.new()
275364
bm.from_mesh(obj.data)
276365
twists = get_celtic_twists(bm)
277-
curve_obj = create_bezier(context, bm, twists,
278-
self.crossing_angle,
279-
self.crossing_strength,
280-
self.handle_type,
281-
self.weave_up,
282-
self.weave_down)
283-
284-
# If thick, then give it a bevel_object and convert to mesh
285-
if self.thickness > 0:
286-
create_pipe_from_bezier(context, curve_obj, self.thickness)
366+
if self.output_type in (BEZIER, PIPE):
367+
curve_obj = create_bezier(context, bm, twists,
368+
self.crossing_angle,
369+
self.crossing_strength,
370+
self.handle_type,
371+
self.weave_up,
372+
self.weave_down)
373+
374+
# If thick, then give it a bevel_object and convert to mesh
375+
if self.output_type == PIPE and self.thickness > 0:
376+
create_pipe_from_bezier(context, curve_obj, self.thickness)
377+
else:
378+
create_ribbon(context, bm, twists, self.weave_up, self.weave_down)
287379
return {'FINISHED'}
288380

289381
def menu_func(self, context):

0 commit comments

Comments
 (0)
Please sign in to comment.