1 # SPDX-FileCopyrightText: 2019-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 # by Yann Bertrand, january 2014.
8 "name": "Curve Outline",
9 "description": "creates an Outline",
10 "author": "Yann Bertrand (jimflim), Vladimir Spivak (cwolf3d)",
12 "blender": (2, 69, 0),
17 from mathutils
import Vector
18 from mathutils
.geometry
import intersect_line_line
23 def createOutline(curve
, outline
):
25 for spline
in curve
.data
.splines
[:]:
26 if spline
.type == 'BEZIER':
27 p
= spline
.bezier_points
32 n
= ((p
[0].handle_right
- p
[0].co
).normalized() - (p
[0].handle_left
- p
[0].co
).normalized()).normalized()
33 n
= Vector((-n
[1], n
[0], n
[2]))
34 o
= p
[0].co
+ outline
* n
37 for i
in range(1,len(p
)-1):
38 n
= ((p
[i
].handle_right
- p
[i
].co
).normalized() - (p
[i
].handle_left
- p
[i
].co
).normalized()).normalized()
39 n
= Vector((-n
[1], n
[0], n
[2]))
40 o
= intersect_line_line(out
[-1], (out
[-1]+p
[i
].co
- p
[i
-1].co
), p
[i
].co
, p
[i
].co
+ n
)[0]
43 n
= ((p
[-1].handle_right
- p
[-1].co
).normalized() - (p
[-1].handle_left
- p
[-1].co
).normalized()).normalized()
44 n
= Vector((-n
[1], n
[0], n
[2]))
45 o
= p
[-1].co
+ outline
* n
48 curve
.data
.splines
.new(spline
.type)
49 if spline
.use_cyclic_u
:
50 curve
.data
.splines
[-1].use_cyclic_u
= True
51 p_out
= curve
.data
.splines
[-1].bezier_points
54 for i
in range(len(out
)):
55 p_out
[i
].handle_left_type
= 'FREE'
56 p_out
[i
].handle_right_type
= 'FREE'
61 l
= (p
[i
+ 1].co
- p
[i
].co
).length
62 l2
= (out
[i
] - out
[i
+ 1]).length
65 p_out
[i
].handle_left
= out
[i
] + ((p
[i
].handle_left
- p
[i
].co
) * l2
/l
)
67 p_out
[i
+ 1].handle_left
= out
[i
+ 1] + ((p
[i
+ 1].handle_left
- p
[i
+ 1].co
) * l2
/l
)
68 p_out
[i
].handle_right
= out
[i
] + ((p
[i
].handle_right
- p
[i
].co
) * l2
/l
)
70 for i
in range(len(p
)):
71 p_out
[i
].handle_left_type
= p
[i
].handle_left_type
72 p_out
[i
].handle_right_type
= p
[i
].handle_right_type
75 if len(spline
.points
) < 2:
78 for point
in spline
.points
:
79 v
= Vector((point
.co
[0], point
.co
[1], point
.co
[2]))
83 n
= ((p
[1] - p
[0]).normalized() - (p
[-1] - p
[0]).normalized()).normalized()
84 n
= Vector((-n
[1], n
[0], n
[2]))
85 o
= p
[0] + outline
* n
88 for i
in range(1,len(p
)-1):
89 n
= ((p
[i
+1] - p
[i
]).normalized() - (p
[i
-1] - p
[i
]).normalized()).normalized()
90 n
= Vector((-n
[1], n
[0], n
[2]))
91 o
= intersect_line_line(out
[-1], (out
[-1]+p
[i
] - p
[i
-1]), p
[i
], p
[i
] + n
)[0]
94 n
= ((p
[0] - p
[-1]).normalized() - (p
[-2] - p
[-1]).normalized()).normalized()
95 n
= Vector((-n
[1], n
[0], n
[2]))
96 o
= p
[-1] + outline
* n
99 curve
.data
.splines
.new(spline
.type)
100 if spline
.use_cyclic_u
:
101 curve
.data
.splines
[-1].use_cyclic_u
= True
102 p_out
= curve
.data
.splines
[-1].points
103 p_out
.add(len(out
)-1)
105 for i
in range(len(out
)):
106 p_out
[i
].co
= (out
[i
][0], out
[i
][1], out
[i
][2], 0.0)
111 class CurveOutline(bpy
.types
.Operator
):
113 bl_idname
= "curvetools.outline"
114 bl_label
= "Create Outline"
115 bl_options
= {'REGISTER', 'UNDO'}
116 outline
: bpy
.props
.FloatProperty(name
="Amount", default
=0.1)
119 def poll(cls
, context
):
120 return util
.Selected1OrMoreCurves()
122 def execute(self
, context
):
123 createOutline(context
.object, self
.outline
)
126 def invoke(self
, context
, event
):
127 return context
.window_manager
.invoke_props_popup(self
, event
)
129 def menu_func(self
, context
):
130 self
.layout
.operator(CurveOutline
.bl_idname
)
134 bpy
.utils
.register_class(operators
)
138 bpy
.utils
.unregister_class(operators
)
140 if __name__
== "__main__":
143 operators
= [CurveOutline
]