1 # SPDX-License-Identifier: GPL-2.0-or-later
3 # by Yann Bertrand, january 2014.
6 "name": "Curve Outline",
7 "description": "creates an Outline",
8 "author": "Yann Bertrand (jimflim), Vladimir Spivak (cwolf3d)",
10 "blender": (2, 69, 0),
15 from mathutils
import Vector
16 from mathutils
.geometry
import intersect_line_line
21 def createOutline(curve
, outline
):
23 for spline
in curve
.data
.splines
[:]:
24 if spline
.type == 'BEZIER':
25 p
= spline
.bezier_points
30 n
= ((p
[0].handle_right
- p
[0].co
).normalized() - (p
[0].handle_left
- p
[0].co
).normalized()).normalized()
31 n
= Vector((-n
[1], n
[0], n
[2]))
32 o
= p
[0].co
+ outline
* n
35 for i
in range(1,len(p
)-1):
36 n
= ((p
[i
].handle_right
- p
[i
].co
).normalized() - (p
[i
].handle_left
- p
[i
].co
).normalized()).normalized()
37 n
= Vector((-n
[1], n
[0], n
[2]))
38 o
= intersect_line_line(out
[-1], (out
[-1]+p
[i
].co
- p
[i
-1].co
), p
[i
].co
, p
[i
].co
+ n
)[0]
41 n
= ((p
[-1].handle_right
- p
[-1].co
).normalized() - (p
[-1].handle_left
- p
[-1].co
).normalized()).normalized()
42 n
= Vector((-n
[1], n
[0], n
[2]))
43 o
= p
[-1].co
+ outline
* n
46 curve
.data
.splines
.new(spline
.type)
47 if spline
.use_cyclic_u
:
48 curve
.data
.splines
[-1].use_cyclic_u
= True
49 p_out
= curve
.data
.splines
[-1].bezier_points
52 for i
in range(len(out
)):
53 p_out
[i
].handle_left_type
= 'FREE'
54 p_out
[i
].handle_right_type
= 'FREE'
59 l
= (p
[i
+ 1].co
- p
[i
].co
).length
60 l2
= (out
[i
] - out
[i
+ 1]).length
63 p_out
[i
].handle_left
= out
[i
] + ((p
[i
].handle_left
- p
[i
].co
) * l2
/l
)
65 p_out
[i
+ 1].handle_left
= out
[i
+ 1] + ((p
[i
+ 1].handle_left
- p
[i
+ 1].co
) * l2
/l
)
66 p_out
[i
].handle_right
= out
[i
] + ((p
[i
].handle_right
- p
[i
].co
) * l2
/l
)
68 for i
in range(len(p
)):
69 p_out
[i
].handle_left_type
= p
[i
].handle_left_type
70 p_out
[i
].handle_right_type
= p
[i
].handle_right_type
73 if len(spline
.points
) < 2:
76 for point
in spline
.points
:
77 v
= Vector((point
.co
[0], point
.co
[1], point
.co
[2]))
81 n
= ((p
[1] - p
[0]).normalized() - (p
[-1] - p
[0]).normalized()).normalized()
82 n
= Vector((-n
[1], n
[0], n
[2]))
83 o
= p
[0] + outline
* n
86 for i
in range(1,len(p
)-1):
87 n
= ((p
[i
+1] - p
[i
]).normalized() - (p
[i
-1] - p
[i
]).normalized()).normalized()
88 n
= Vector((-n
[1], n
[0], n
[2]))
89 o
= intersect_line_line(out
[-1], (out
[-1]+p
[i
] - p
[i
-1]), p
[i
], p
[i
] + n
)[0]
92 n
= ((p
[0] - p
[-1]).normalized() - (p
[-2] - p
[-1]).normalized()).normalized()
93 n
= Vector((-n
[1], n
[0], n
[2]))
94 o
= p
[-1] + outline
* n
97 curve
.data
.splines
.new(spline
.type)
98 if spline
.use_cyclic_u
:
99 curve
.data
.splines
[-1].use_cyclic_u
= True
100 p_out
= curve
.data
.splines
[-1].points
101 p_out
.add(len(out
)-1)
103 for i
in range(len(out
)):
104 p_out
[i
].co
= (out
[i
][0], out
[i
][1], out
[i
][2], 0.0)
109 class CurveOutline(bpy
.types
.Operator
):
111 bl_idname
= "curvetools.outline"
112 bl_label
= "Create Outline"
113 bl_options
= {'REGISTER', 'UNDO'}
114 outline
: bpy
.props
.FloatProperty(name
="Amount", default
=0.1)
117 def poll(cls
, context
):
118 return util
.Selected1OrMoreCurves()
120 def execute(self
, context
):
121 createOutline(context
.object, self
.outline
)
124 def invoke(self
, context
, event
):
125 return context
.window_manager
.invoke_props_popup(self
, event
)
127 def menu_func(self
, context
):
128 self
.layout
.operator(CurveOutline
.bl_idname
)
132 bpy
.utils
.register_class(operators
)
136 bpy
.utils
.unregister_class(operators
)
138 if __name__
== "__main__":
141 operators
= [CurveOutline
]