Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSpivak Vladimir (cwolf3d) <cwolf3d@gmail.com>2019-10-11 02:52:59 +0300
committerSpivak Vladimir (cwolf3d) <cwolf3d@gmail.com>2019-10-11 02:52:59 +0300
commit04f482c5aa48a1b04a429dafba3e09f7287b9110 (patch)
tree310459ae8934ebb7b3449217a758a00d374ce7d0
parentdb2c65d9e7885809a9ac6ab09fa2585e6b35d353 (diff)
Addon: Curve Tools: Refactoring. Added context menu.
-rw-r--r--curve_tools/Curves.py16
-rw-r--r--curve_tools/Operators.py124
-rw-r--r--curve_tools/Properties.py15
-rw-r--r--curve_tools/Surfaces.py24
-rw-r--r--curve_tools/__init__.py132
-rw-r--r--curve_tools/auto_loft.py17
-rw-r--r--curve_tools/cad.py77
-rw-r--r--curve_tools/curve_outline.py112
-rw-r--r--curve_tools/curve_remove_doubles.py110
-rw-r--r--curve_tools/exports.py11
-rw-r--r--curve_tools/fillet.py143
-rw-r--r--curve_tools/internal.py19
-rw-r--r--curve_tools/intersections.py (renamed from curve_tools/CurveIntersections.py)54
-rw-r--r--curve_tools/mathematics.py (renamed from curve_tools/Math.py)0
-rw-r--r--curve_tools/outline.py118
-rw-r--r--curve_tools/path_finder.py (renamed from curve_tools/PathFinder.py)28
-rw-r--r--curve_tools/remove_doubles.py135
-rw-r--r--curve_tools/show_resolution.py (renamed from curve_tools/ShowCurveResolution.py)19
-rw-r--r--curve_tools/splines_sequence.py (renamed from curve_tools/SplinesSequence.py)17
-rw-r--r--curve_tools/toolpath.py23
20 files changed, 760 insertions, 434 deletions
diff --git a/curve_tools/Curves.py b/curve_tools/Curves.py
index d5608e3c..da0b1398 100644
--- a/curve_tools/Curves.py
+++ b/curve_tools/Curves.py
@@ -1,4 +1,4 @@
-from . import Math
+from . import mathematics
import bpy
@@ -390,7 +390,7 @@ class BezierSpline:
self.segments.append(BezierSegment(self.segments[-1].bezierPoint2, spline2.segments[0].bezierPoint1))
for seg2 in spline2.segments: self.segments.append(seg2)
- self.resolution += spline2.resolution # extra segment will usually be short -- impact on resolution negligible
+ self.resolution += spline2.resolution # extra segment will usually be short -- impact on resolution negligable
self.isCyclic = False # is this ok?
@@ -559,11 +559,11 @@ class Curve:
currEndPoint = currentSpline.segments[-1].bezierPoint2.co
nextStartPoint = nextSpline.segments[0].bezierPoint1.co
- if Math.IsSamePoint(currEndPoint, nextStartPoint, threshold): return [currentSpline, nextSpline]
+ if mathematics.IsSamePoint(currEndPoint, nextStartPoint, threshold): return [currentSpline, nextSpline]
nextEndPoint = nextSpline.segments[-1].bezierPoint2.co
currStartPoint = currentSpline.segments[0].bezierPoint1.co
- if Math.IsSamePoint(nextEndPoint, currStartPoint, threshold): return [nextSpline, currentSpline]
+ if mathematics.IsSamePoint(nextEndPoint, currStartPoint, threshold): return [nextSpline, currentSpline]
return None
else:
@@ -575,18 +575,18 @@ class Curve:
currEndPoint = currentSpline.segments[-1].bezierPoint2.co
nextStartPoint = nextSpline.segments[0].bezierPoint1.co
- if Math.IsSamePoint(currEndPoint, nextStartPoint, threshold): return [currentSpline, nextSpline]
+ if mathematics.IsSamePoint(currEndPoint, nextStartPoint, threshold): return [currentSpline, nextSpline]
nextEndPoint = nextSpline.segments[-1].bezierPoint2.co
currStartPoint = currentSpline.segments[0].bezierPoint1.co
- if Math.IsSamePoint(nextEndPoint, currStartPoint, threshold): return [nextSpline, currentSpline]
+ if mathematics.IsSamePoint(nextEndPoint, currStartPoint, threshold): return [nextSpline, currentSpline]
- if Math.IsSamePoint(currEndPoint, nextEndPoint, threshold):
+ if mathematics.IsSamePoint(currEndPoint, nextEndPoint, threshold):
nextSpline.Reverse()
#print("## ", "nextSpline.Reverse()")
return [currentSpline, nextSpline]
- if Math.IsSamePoint(currStartPoint, nextStartPoint, threshold):
+ if mathematics.IsSamePoint(currStartPoint, nextStartPoint, threshold):
currentSpline.Reverse()
#print("## ", "currentSpline.Reverse()")
return [currentSpline, nextSpline]
diff --git a/curve_tools/Operators.py b/curve_tools/Operators.py
index edf16ab1..e4fe24dd 100644
--- a/curve_tools/Operators.py
+++ b/curve_tools/Operators.py
@@ -7,12 +7,12 @@ from bpy_extras import object_utils, view3d_utils
from mathutils import *
from math import *
-from . import Properties
-from . import Curves
-from . import CurveIntersections
-from . import Util
-from . import Surfaces
-from . import Math
+from . import properties
+from . import curves
+from . import intersections
+from . import util
+from . import surfaces
+from . import mathematics
# 1 CURVE SELECTED
# ################
@@ -24,11 +24,11 @@ class OperatorCurveInfo(bpy.types.Operator):
@classmethod
def poll(cls, context):
- return Util.Selected1Curve()
+ return util.Selected1Curve()
def execute(self, context):
- curve = Curves.Curve(context.active_object)
+ curve = curves.Curve(context.active_object)
nrSplines = len(curve.splines)
nrSegments = 0
@@ -52,11 +52,11 @@ class OperatorCurveLength(bpy.types.Operator):
@classmethod
def poll(cls, context):
- return Util.Selected1Curve()
+ return util.Selected1Curve()
def execute(self, context):
- curve = Curves.Curve(context.active_object)
+ curve = curves.Curve(context.active_object)
context.scene.curvetools.CurveLength = curve.length
@@ -72,11 +72,11 @@ class OperatorSplinesInfo(bpy.types.Operator):
@classmethod
def poll(cls, context):
- return Util.Selected1Curve()
+ return util.Selected1Curve()
def execute(self, context):
- curve = Curves.Curve(context.active_object)
+ curve = curves.Curve(context.active_object)
nrSplines = len(curve.splines)
print("")
@@ -105,11 +105,11 @@ class OperatorSegmentsInfo(bpy.types.Operator):
@classmethod
def poll(cls, context):
- return Util.Selected1Curve()
+ return util.Selected1Curve()
def execute(self, context):
- curve = Curves.Curve(context.active_object)
+ curve = curves.Curve(context.active_object)
nrSplines = len(curve.splines)
nrSegments = 0
@@ -146,7 +146,7 @@ class OperatorOriginToSpline0Start(bpy.types.Operator):
@classmethod
def poll(cls, context):
- return Util.Selected1Curve()
+ return util.Selected1Curve()
def execute(self, context):
@@ -183,11 +183,11 @@ class OperatorIntersectCurves(bpy.types.Operator):
@classmethod
def poll(cls, context):
- return Util.Selected2OrMoreCurves()
+ return util.Selected2OrMoreCurves()
def execute(self, context):
- print("### TODO: OperatorIntersectCurves.execute()")
+ print("### TODO: OperatorIntersectcurves.execute()")
algo = context.scene.curvetools.IntersectCurvesAlgorithm
print("-- algo:", algo)
@@ -213,7 +213,7 @@ class OperatorIntersectCurves(bpy.types.Operator):
selected_objects[j].select_set(True)
if selected_objects[i].type == 'CURVE' and selected_objects[j].type == 'CURVE':
- curveIntersector = CurveIntersections.CurvesIntersector.FromSelection()
+ curveIntersector = intersections.CurvesIntersector.FromSelection()
rvIntersectionNrs = curveIntersector.CalcAndApplyIntersections()
self.report({'INFO'}, "Active curve points: %d; other curve points: %d" % (rvIntersectionNrs[0], rvIntersectionNrs[1]))
@@ -234,16 +234,16 @@ class OperatorLoftCurves(bpy.types.Operator):
@classmethod
def poll(cls, context):
- return Util.Selected2Curves()
+ return util.Selected2Curves()
def execute(self, context):
- #print("### TODO: OperatorLoftCurves.execute()")
+ #print("### TODO: OperatorLoftcurves.execute()")
- loftedSurface = Surfaces.LoftedSurface.FromSelection()
+ loftedSurface = surfaces.LoftedSurface.FromSelection()
loftedSurface.AddToScene()
- self.report({'INFO'}, "OperatorLoftCurves.execute()")
+ self.report({'INFO'}, "OperatorLoftcurves.execute()")
return {'FINISHED'}
@@ -259,16 +259,16 @@ class OperatorSweepCurves(bpy.types.Operator):
@classmethod
def poll(cls, context):
- return Util.Selected2Curves()
+ return util.Selected2Curves()
def execute(self, context):
- #print("### TODO: OperatorSweepCurves.execute()")
+ #print("### TODO: OperatorSweepcurves.execute()")
- sweptSurface = Surfaces.SweptSurface.FromSelection()
+ sweptSurface = surfaces.SweptSurface.FromSelection()
sweptSurface.AddToScene()
- self.report({'INFO'}, "OperatorSweepCurves.execute()")
+ self.report({'INFO'}, "OperatorSweepcurves.execute()")
return {'FINISHED'}
@@ -284,11 +284,11 @@ class OperatorBirail(bpy.types.Operator):
@classmethod
def poll(cls, context):
- return Util.Selected3Curves()
+ return util.Selected3Curves()
def execute(self, context):
- birailedSurface = Surfaces.BirailedSurface.FromSelection()
+ birailedSurface = surfaces.BirailedSurface.FromSelection()
birailedSurface.AddToScene()
self.report({'INFO'}, "OperatorBirail.execute()")
@@ -307,12 +307,12 @@ class OperatorSplinesSetResolution(bpy.types.Operator):
@classmethod
def poll(cls, context):
- return Util.Selected1OrMoreCurves()
+ return util.Selected1OrMoreCurves()
def execute(self, context):
splRes = context.scene.curvetools.SplineResolution
- selCurves = Util.GetSelectedCurves()
+ selCurves = util.GetSelectedCurves()
for blCurve in selCurves:
for spline in blCurve.data.splines:
@@ -331,14 +331,14 @@ class OperatorSplinesRemoveZeroSegment(bpy.types.Operator):
@classmethod
def poll(cls, context):
- return Util.Selected1OrMoreCurves()
+ return util.Selected1OrMoreCurves()
def execute(self, context):
- selCurves = Util.GetSelectedCurves()
+ selCurves = util.GetSelectedCurves()
for blCurve in selCurves:
- curve = Curves.Curve(blCurve)
+ curve = curves.Curve(blCurve)
nrSplines = curve.nrSplines
splinesToRemove = []
@@ -365,15 +365,15 @@ class OperatorSplinesRemoveShort(bpy.types.Operator):
@classmethod
def poll(cls, context):
- return Util.Selected1OrMoreCurves()
+ return util.Selected1OrMoreCurves()
def execute(self, context):
threshold = context.scene.curvetools.SplineRemoveLength
- selCurves = Util.GetSelectedCurves()
+ selCurves = util.GetSelectedCurves()
for blCurve in selCurves:
- curve = Curves.Curve(blCurve)
+ curve = curves.Curve(blCurve)
nrSplines = curve.nrSplines
nrRemovedSplines = curve.RemoveShortSplines(threshold)
@@ -394,14 +394,14 @@ class OperatorSplinesJoinNeighbouring(bpy.types.Operator):
@classmethod
def poll(cls, context):
- return Util.Selected1OrMoreCurves()
+ return util.Selected1OrMoreCurves()
def execute(self, context):
- selCurves = Util.GetSelectedCurves()
+ selCurves = util.GetSelectedCurves()
for blCurve in selCurves:
- curve = Curves.Curve(blCurve)
+ curve = curves.Curve(blCurve)
nrSplines = curve.nrSplines
threshold = context.scene.curvetools.SplineJoinDistance
@@ -423,7 +423,7 @@ def SurfaceFromBezier(surfacedata, points, center):
len_points = len(points) - 1
if len_points % 2 == 0:
- h = Math.subdivide_cubic_bezier(
+ h = mathematics.subdivide_cubic_bezier(
points[len_points].co, points[len_points].handle_right,
points[0].handle_left, points[0].co, 0.5
)
@@ -550,7 +550,7 @@ class ConvertSelectedFacesToBezier(bpy.types.Operator):
@classmethod
def poll(cls, context):
- return Util.Selected1Mesh()
+ return util.Selected1Mesh()
def execute(self, context):
# main function
@@ -620,7 +620,7 @@ class ConvertBezierToSurface(bpy.types.Operator):
@classmethod
def poll(cls, context):
- return Util.Selected1OrMoreCurves()
+ return util.Selected1OrMoreCurves()
def execute(self, context):
# main function
@@ -686,7 +686,7 @@ class BezierPointsFillet(bpy.types.Operator):
@classmethod
def poll(cls, context):
- return Util.Selected1OrMoreCurves()
+ return util.Selected1OrMoreCurves()
def execute(self, context):
# main function
@@ -798,7 +798,7 @@ class BezierDivide(bpy.types.Operator):
@classmethod
def poll(cls, context):
- return Util.Selected1OrMoreCurves()
+ return util.Selected1OrMoreCurves()
def execute(self, context):
# main function
@@ -834,7 +834,7 @@ class BezierDivide(bpy.types.Operator):
if (j in ii) and (j + 1 in ii):
bezier_points[j + jn].select_control_point = True
bezier_points[j + 1 + jn].select_control_point = True
- h = Math.subdivide_cubic_bezier(
+ h = mathematics.subdivide_cubic_bezier(
bezier_points[j + jn].co, bezier_points[j + jn].handle_right,
bezier_points[j + 1 + jn].handle_left, bezier_points[j + 1 + jn].co, self.Bezier_t / 100
)
@@ -853,7 +853,7 @@ class BezierDivide(bpy.types.Operator):
if j == n - 1 and (0 in ii) and spline.use_cyclic_u:
bezier_points[j + jn].select_control_point = True
bezier_points[0].select_control_point = True
- h = Math.subdivide_cubic_bezier(
+ h = mathematics.subdivide_cubic_bezier(
bezier_points[j + jn].co, bezier_points[j + jn].handle_right,
bezier_points[0].handle_left, bezier_points[0].co, self.Bezier_t / 100
)
@@ -922,10 +922,10 @@ class Split(bpy.types.Operator):
@classmethod
def poll(cls, context):
- return Util.Selected1OrMoreCurves()
+ return util.Selected1OrMoreCurves()
def execute(self, context):
- selected_Curves = Util.GetSelectedCurves()
+ selected_Curves = util.GetSelectedCurves()
for curve in selected_Curves:
spline_points = []
@@ -1004,6 +1004,33 @@ class Split(bpy.types.Operator):
num=i
return {'FINISHED'}
+
+class SeparateOutline(bpy.types.Operator):
+ bl_idname = "curvetools.sep_outline"
+ bl_label = "Separate Outline"
+ bl_options = {'REGISTER', 'UNDO'}
+ bl_description = "Makes 'Outline' separate mesh"
+
+ @classmethod
+ def poll(cls, context):
+ return util.Selected1OrMoreCurves()
+
+ def execute(self, context):
+ bpy.ops.object.mode_set(mode = 'EDIT')
+ bpy.ops.curve.separate()
+
+ return {'FINISHED'}
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(operators)
+
+def unregister():
+ for cls in classes:
+ bpy.utils.unregister_class(operators)
+
+if __name__ == "__main__":
+ register()
operators = [
OperatorCurveInfo,
@@ -1025,4 +1052,5 @@ operators = [
BezierDivide,
CurveScaleReset,
Split,
+ SeparateOutline,
]
diff --git a/curve_tools/Properties.py b/curve_tools/Properties.py
index 415b3c8b..d6fe9e0f 100644
--- a/curve_tools/Properties.py
+++ b/curve_tools/Properties.py
@@ -87,3 +87,18 @@ class curvetoolsSelectedObject(bpy.types.PropertyGroup):
for blObject in blenderSelectedObjects: rvNames.append(blObject.name)
return rvNames
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(operators)
+
+def unregister():
+ for cls in classes:
+ bpy.utils.unregister_class(operators)
+
+if __name__ == "__main__":
+ register()
+
+operators = [
+ curvetoolsSelectedObject,
+ ]
diff --git a/curve_tools/Surfaces.py b/curve_tools/Surfaces.py
index 22b5119c..cfae7040 100644
--- a/curve_tools/Surfaces.py
+++ b/curve_tools/Surfaces.py
@@ -1,8 +1,8 @@
import bpy
import bmesh
-from . import Math
-from . import Curves
+from . import mathematics
+from . import curves
@@ -62,8 +62,8 @@ class LoftedSurface:
blenderOtherCurve = selObjects[0]
if blenderActiveCurve == blenderOtherCurve: blenderOtherCurve = selObjects[1]
- aCurve = Curves.Curve(blenderActiveCurve)
- oCurve = Curves.Curve(blenderOtherCurve)
+ aCurve = curves.Curve(blenderActiveCurve)
+ oCurve = curves.Curve(blenderOtherCurve)
name = "TODO: autoname"
@@ -162,7 +162,7 @@ class SweptSplineSurface:
prevDerivativeO = localDerivativesO[0]
for iO in range(self.resolutionO):
currDerivativeO = localDerivativesO[iO]
- localRotMatO = Math.CalcRotationMatrix(prevDerivativeO, currDerivativeO)
+ localRotMatO = mathematics.CalcRotationMatrix(prevDerivativeO, currDerivativeO)
currLocalAToLocalO = worldMatrixOInv @ currWorldMatrixA
worldPointsA = []
@@ -210,8 +210,8 @@ class SweptSurface:
blenderOtherCurve = selObjects[0]
if blenderActiveCurve == blenderOtherCurve: blenderOtherCurve = selObjects[1]
- aCurve = Curves.Curve(blenderActiveCurve)
- oCurve = Curves.Curve(blenderOtherCurve)
+ aCurve = curves.Curve(blenderActiveCurve)
+ oCurve = curves.Curve(blenderOtherCurve)
name = "TODO: autoname"
@@ -315,7 +315,7 @@ class BirailedSplineSurface:
prevDerivativeRail1 = localDerivativesRail1[0]
for iRail in range(self.resolutionRails):
currDerivativeRail1 = localDerivativesRail1[iRail]
- localRotMatRail1 = Math.CalcRotationMatrix(prevDerivativeRail1, currDerivativeRail1)
+ localRotMatRail1 = mathematics.CalcRotationMatrix(prevDerivativeRail1, currDerivativeRail1)
currLocalProfileToLocalRail1 = worldMatrixRail1Inv @ currWorldMatrixProfile
worldPointsProfileRail1 = []
@@ -336,7 +336,7 @@ class BirailedSplineSurface:
scaleFactorRail2 = v3To.magnitude / v3From.magnitude
else:
scaleFactorRail2 = 1
- rotMatRail2 = Math.CalcRotationMatrix(v3From, v3To)
+ rotMatRail2 = mathematics.CalcRotationMatrix(v3From, v3To)
worldOffsetsProfileRail2 = []
for iProfile in range(self.resolutionProfile):
@@ -397,9 +397,9 @@ class BirailedSurface:
if profileBlenderCurve is None: raise Exception("profileBlenderCurve is None")
- rail1Curve = Curves.Curve(rail1BlenderCurve)
- rail2Curve = Curves.Curve(rail2BlenderCurve)
- profileCurve = Curves.Curve(profileBlenderCurve)
+ rail1Curve = curves.Curve(rail1BlenderCurve)
+ rail2Curve = curves.Curve(rail2BlenderCurve)
+ profileCurve = curves.Curve(profileBlenderCurve)
name = "TODO: autoname"
diff --git a/curve_tools/__init__.py b/curve_tools/__init__.py
index e38f854e..736e60fb 100644
--- a/curve_tools/__init__.py
+++ b/curve_tools/__init__.py
@@ -25,13 +25,12 @@ bl_info = {
"name": "Curve Tools",
"description": "Adds some functionality for bezier/nurbs curve/surface modeling",
"author": "Mackraken",
- "version": (0, 3, 3),
+ "version": (0, 4, 0),
"blender": (2, 80, 0),
"location": "View3D > Tool Shelf > Edit Tab",
"warning": "WIP",
- "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/"
- "Scripts/Curve/Curve_Tools",
- "tracker_url": "https://developer.blender.org/maniphest/task/edit/form/2/",
+ "wiki_url": "",
+ "tracker_url": "",
"category": "Add Curve"}
@@ -50,22 +49,25 @@ from bpy.props import (
StringProperty,
FloatVectorProperty,
)
-from . import Properties
-from . import Operators
-from . import auto_loft
-from . import curve_outline
-from . import PathFinder
-from . import ShowCurveResolution
-from . import SplinesSequence
+from . import properties, operators, auto_loft, outline, remove_doubles
+from . import path_finder, show_resolution, splines_sequence, fillet
from . import internal, cad, toolpath, exports
-if 'internal' in locals():
+if 'bpy' in locals():
+ importlib.reload(properties)
+ importlib.reload(operators)
+ importlib.reload(auto_loft)
+ importlib.reload(outline)
+ importlib.reload(remove_doubles)
+ importlib.reload(path_finder)
+ importlib.reload(show_resolution)
+ importlib.reload(splines_sequence)
+ importlib.reload(fillet)
importlib.reload(internal)
importlib.reload(cad)
importlib.reload(toolpath)
importlib.reload(exports)
-
from bpy.types import (
AddonPreferences,
)
@@ -81,28 +83,10 @@ def UpdateDummy(object, context):
UTILSDROP = scene.UTUtilsDrop
-class SeparateOutline(Operator):
- bl_idname = "object.sep_outline"
- bl_label = "Separate Outline"
- bl_options = {'REGISTER', 'UNDO'}
- bl_description = "Makes 'Outline' separate mesh"
-
- @classmethod
- def poll(cls, context):
- return (context.object is not None and
- context.object.type == 'CURVE')
-
- def execute(self, context):
- bpy.ops.object.mode_set(mode = 'EDIT')
- bpy.ops.curve.separate()
-
- return {'FINISHED'}
-
-
class curvetoolsSettings(PropertyGroup):
# selection
SelectedObjects: CollectionProperty(
- type=Properties.curvetoolsSelectedObject
+ type=properties.curvetoolsSelectedObject
)
NrSelectedObjects: IntProperty(
name="NrSelectedObjects",
@@ -308,7 +292,6 @@ class VIEW3D_PT_CurvePanel(Panel):
row = col.row(align=True)
row.prop(context.scene.curvetools, "LimitDistance", text="LimitDistance")
- # row.active = (context.scene.curvetools.IntersectCurvesAlgorithm == '3D')
row = col.row(align=True)
row.prop(context.scene.curvetools, "IntersectCurvesAlgorithm", text="Algorithm")
@@ -346,12 +329,14 @@ class VIEW3D_PT_CurvePanel(Panel):
if ADVANCEDDROP:
# C. 3 curves
row = col.row(align=True)
- row.operator("object._curve_outline", text="Curve Outline")
+ row.operator("curvetools.outline", text="Curve Outline")
row = col.row(align=True)
- row.operator("object.sep_outline", text="Separate Outline or selected")
+ row.operator("curvetools.sep_outline", text="Separate Outline or selected")
row = col.row(align=True)
row.operator("curvetools.bezier_points_fillet", text='Fillet')
row = col.row(align=True)
+ row.operator("curvetools.bezier_cad_handle_projection", text='Handle Projection')
+ row = col.row(align=True)
row.operator("curvetools.bezier_spline_divide", text='Divide')
row = col.row(align=True)
row.operator("curvetools.scale_reset", text='Scale Reset')
@@ -369,17 +354,19 @@ class VIEW3D_PT_CurvePanel(Panel):
row.prop(scene, "UTExtendedDrop", icon="TRIA_DOWN")
if EXTENDEDDROP:
row = col.row(align=True)
- row.operator("curve.add_toolpath_offset_curve", text="Offset Curve")
+ row.operator("curvetools.add_toolpath_offset_curve", text="Offset Curve")
row = col.row(align=True)
- row.operator("curve.bezier_cad_boolean", text="Boolean 2 selected spline")
+ row.operator("curvetools.bezier_cad_boolean", text="Boolean 2 selected spline")
row = col.row(align=True)
- row.operator("curve.bezier_cad_subdivide", text="Multi Subdivide")
+ row.operator("curvetools.bezier_cad_subdivide", text="Multi Subdivide")
row = col.row(align=True)
row.operator("curvetools.split", text='Split by selected points')
row = col.row(align=True)
- row.operator("curve.add_toolpath_discretize_curve", text="Discretize Curve")
+ row.operator("curvetools.remove_doubles", text='Remove Doubles')
+ row = col.row(align=True)
+ row.operator("curvetools.add_toolpath_discretize_curve", text="Discretize Curve")
row = col.row(align=True)
- row.operator("curve.bezier_cad_array", text="Array selected spline")
+ row.operator("curvetools.bezier_cad_array", text="Array selected spline")
# Utils Curve options
box1 = self.layout.box()
@@ -396,7 +383,7 @@ class VIEW3D_PT_CurvePanel(Panel):
row = col.row(align=True)
row.prop(context.scene.curvetools, "curve_vertcolor", text="")
row = col.row(align=True)
- row.operator("curve.show_resolution", text="Run [ESC]")
+ row.operator("curvetools.show_resolution", text="Run [ESC]")
# D.1 set spline sequence
row = col.row(align=True)
@@ -406,12 +393,12 @@ class VIEW3D_PT_CurvePanel(Panel):
row.prop(context.scene.curvetools, "font_thickness", text="")
row.prop(context.scene.curvetools, "font_size", text="")
row = col.row(align=True)
- oper = row.operator("curve.rearrange_spline", text="<")
+ oper = row.operator("curvetools.rearrange_spline", text="<")
oper.command = 'PREV'
- oper = row.operator("curve.rearrange_spline", text=">")
+ oper = row.operator("curvetools.rearrange_spline", text=">")
oper.command = 'NEXT'
row = col.row(align=True)
- row.operator("curve.show_splines_sequence", text="Run [ESC]")
+ row.operator("curvetools.show_splines_sequence", text="Run [ESC]")
# D.2 remove splines
row = col.row(align=True)
@@ -455,7 +442,6 @@ class VIEW3D_PT_CurvePanel(Panel):
row = col.row(align=True)
row.label(text="A - deselect all")
-
# Add-ons Preferences Update Panel
# Define Panel classes for updating
@@ -500,6 +486,27 @@ class CurveAddonPreferences(AddonPreferences):
col.label(text="Tab Category:")
col.prop(self, "category", text="")
+# Context MENU
+def curve_tools_context_menu(self, context):
+ bl_label = 'Curve tools'
+
+ self.layout.operator("curvetools.bezier_points_fillet", text="Fillet")
+ self.layout.operator("curvetools.bezier_cad_handle_projection", text='Handle Projection')
+ self.layout.operator("curvetools.bezier_spline_divide", text="Divide")
+ self.layout.operator("curvetools.add_toolpath_offset_curve", text="Offset Curve")
+ self.layout.operator("curvetools.remove_doubles", text='Remove Doubles')
+ self.layout.separator()
+
+def curve_tools_object_context_menu(self, context):
+ bl_label = 'Curve tools'
+
+ if context.active_object.type == "CURVE":
+ self.layout.operator("curvetools.scale_reset", text="Scale Reset")
+ self.layout.operator("curvetools.add_toolpath_offset_curve", text="Offset Curve")
+ self.layout.operator("curvetools.remove_doubles", text='Remove Doubles')
+ self.layout.separator()
+
+# Import-export 2d svg
def menu_file_export(self, context):
for operator in exports.operators:
self.layout.operator(operator.bl_idname)
@@ -509,16 +516,21 @@ def menu_file_import(self, context):
self.layout.operator(operator.bl_idname)
# REGISTER
-classes = cad.operators + toolpath.operators + exports.operators + Operators.operators + [
- Properties.curvetoolsSelectedObject,
- CurveAddonPreferences,
- curvetoolsSettings,
- SeparateOutline,
- PathFinder.PathFinder,
- ShowCurveResolution.ShowCurveResolution,
- SplinesSequence.ShowSplinesSequence,
- SplinesSequence.RearrangeSpline,
- ]
+classes = cad.operators + \
+ toolpath.operators + \
+ exports.operators + \
+ operators.operators + \
+ properties.operators + \
+ path_finder.operators + \
+ show_resolution.operators + \
+ splines_sequence.operators + \
+ outline.operators + \
+ fillet.operators + \
+ remove_doubles.operators + \
+ [
+ CurveAddonPreferences,
+ curvetoolsSettings,
+ ]
def register():
bpy.types.Scene.UTSingleDrop = BoolProperty(
@@ -560,13 +572,14 @@ def register():
auto_loft.register()
- curve_outline.register()
-
bpy.types.TOPBAR_MT_file_export.append(menu_file_export)
bpy.types.Scene.curvetools = bpy.props.PointerProperty(type=curvetoolsSettings)
update_panel(None, bpy.context)
+
+ bpy.types.VIEW3D_MT_edit_curve_context_menu.prepend(curve_tools_context_menu)
+ bpy.types.VIEW3D_MT_object_context_menu.prepend(curve_tools_object_context_menu)
def unregister():
@@ -579,10 +592,11 @@ def unregister():
auto_loft.unregister()
- curve_outline.unregister()
-
bpy.types.TOPBAR_MT_file_export.remove(menu_file_export)
+ bpy.types.VIEW3D_MT_edit_curve_context_menu.remove(curve_tools_context_menu)
+ bpy.types.VIEW3D_MT_object_context_menu.remove(curve_tools_object_context_menu)
+
for panel in panels:
bpy.utils.unregister_class(panel)
diff --git a/curve_tools/auto_loft.py b/curve_tools/auto_loft.py
index 6675aaf1..c0711196 100644
--- a/curve_tools/auto_loft.py
+++ b/curve_tools/auto_loft.py
@@ -1,9 +1,9 @@
import bpy
from bpy.props import BoolProperty
from bpy.types import Operator, Panel
-from curve_tools.Surfaces import LoftedSurface
-from curve_tools.Curves import Curve
-from curve_tools import Util
+from . import surfaces
+from . import curves
+from . import util
class OperatorAutoLoftCurves(Operator):
@@ -13,16 +13,16 @@ class OperatorAutoLoftCurves(Operator):
@classmethod
def poll(cls, context):
- return Util.Selected2Curves()
+ return util.Selected2Curves()
def execute(self, context):
- #print("### TODO: OperatorLoftCurves.execute()")
+ #print("### TODO: OperatorLoftcurves.execute()")
mesh = bpy.data.meshes.new("LoftMesh")
curve0 = context.selected_objects[0]
curve1 = context.selected_objects[1]
- ls = LoftedSurface(Curve(curve0), Curve(curve1), "AutoLoft")
+ ls = surfaces.LoftedSurface(curves.Curve(curve0), curves.Curve(curve1), "AutoLoft")
ls.bMesh.to_mesh(mesh)
@@ -37,8 +37,6 @@ class OperatorAutoLoftCurves(Operator):
"description": "Auto loft from %s to %s" % (curve0.name, curve1.name),
"curve0": curve0.name,
"curve1": curve1.name}
- #print(loftobj['_RNA_UI'].to_dict())
- #self.report({'INFO'}, "OperatorAutoLoftCurves.execute()")
return {'FINISHED'}
@@ -61,12 +59,11 @@ class AutoLoftModalOperator(Operator):
#print("TIMER", lofters)
for loftmesh in lofters:
- #loftmesh.hide_select = True
rna = loftmesh['_RNA_UI']["autoloft"].to_dict()
curve0 = scene.objects.get(rna["curve0"])
curve1 = scene.objects.get(rna["curve1"])
if curve0 and curve1:
- ls = LoftedSurface(Curve(curve0), Curve(curve1), loftmesh.name)
+ ls = surfaces.LoftedSurface(curves.Curve(curve0), curves.Curve(curve1), loftmesh.name)
ls.bMesh.to_mesh(loftmesh.data)
return {'FINISHED'}
diff --git a/curve_tools/cad.py b/curve_tools/cad.py
index 288f9149..49ccf171 100644
--- a/curve_tools/cad.py
+++ b/curve_tools/cad.py
@@ -28,20 +28,20 @@ bl_info = {
import bpy
from . import internal
-from . import Util
+from . import util
class Fillet(bpy.types.Operator):
- bl_idname = 'curve.bezier_cad_fillet'
+ bl_idname = 'curvetools.bezier_cad_fillet'
bl_description = bl_label = 'Fillet'
bl_options = {'REGISTER', 'UNDO'}
radius: bpy.props.FloatProperty(name='Radius', description='Radius of the rounded corners', unit='LENGTH', min=0.0, default=0.1)
chamfer_mode: bpy.props.BoolProperty(name='Chamfer', description='Cut off sharp without rounding', default=False)
- limit_half_way: bpy.props.BoolProperty(name='Limit Half Way', description='Limits the segments to half their length in order to prevent collisions', default=False)
+ limit_half_way: bpy.props.BoolProperty(name='Limit Half Way', description='Limits the segements to half their length in order to prevent collisions', default=False)
@classmethod
def poll(cls, context):
- return Util.Selected1OrMoreCurves()
+ return util.Selected1OrMoreCurves()
def execute(self, context):
splines = internal.getSelectedSplines(True, True, True)
@@ -54,7 +54,7 @@ class Fillet(bpy.types.Operator):
return {'FINISHED'}
class Boolean(bpy.types.Operator):
- bl_idname = 'curve.bezier_cad_boolean'
+ bl_idname = 'curvetools.bezier_cad_boolean'
bl_description = bl_label = 'Boolean'
bl_options = {'REGISTER', 'UNDO'}
@@ -66,7 +66,7 @@ class Boolean(bpy.types.Operator):
@classmethod
def poll(cls, context):
- return Util.Selected1OrMoreCurves()
+ return util.Selected1OrMoreCurves()
def execute(self, context):
current_mode = bpy.context.object.mode
@@ -80,7 +80,7 @@ class Boolean(bpy.types.Operator):
if len(splines) != 2:
self.report({'WARNING'}, 'Invalid selection. Only work to selected two spline.')
return {'CANCELLED'}
- bpy.ops.curve.spline_type_set(type='BEZIER')
+ bpy.ops.curvetools.spline_type_set(type='BEZIER')
splineA = bpy.context.object.data.splines.active
splineB = splines[0] if (splines[1] == splineA) else splines[1]
if not internal.bezierBooleanGeometry(splineA, splineB, self.operation):
@@ -92,13 +92,13 @@ class Boolean(bpy.types.Operator):
return {'FINISHED'}
class Intersection(bpy.types.Operator):
- bl_idname = 'curve.bezier_cad_intersection'
+ bl_idname = 'curvetools.bezier_cad_intersection'
bl_description = bl_label = 'Intersection'
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
- return Util.Selected1OrMoreCurves()
+ return util.Selected1OrMoreCurves()
def execute(self, context):
segments = internal.bezierSegments(bpy.context.object.data.splines, True)
@@ -108,15 +108,33 @@ class Intersection(bpy.types.Operator):
internal.bezierMultiIntersection(segments)
return {'FINISHED'}
+
+class HandleProjection(bpy.types.Operator):
+ bl_idname = 'curvetools.bezier_cad_handle_projection'
+ bl_description = bl_label = 'Handle Projection'
+ bl_options = {'REGISTER', 'UNDO'}
+
+ @classmethod
+ def poll(cls, context):
+ return internal.curveObject()
+
+ def execute(self, context):
+ segments = internal.bezierSegments(bpy.context.object.data.splines, True)
+ if len(segments) < 1:
+ self.report({'WARNING'}, 'Nothing selected')
+ return {'CANCELLED'}
+
+ internal.bezierProjectHandles(segments)
+ return {'FINISHED'}
class MergeEnds(bpy.types.Operator):
- bl_idname = 'curve.bezier_cad_merge_ends'
+ bl_idname = 'curvetools.bezier_cad_merge_ends'
bl_description = bl_label = 'Merge Ends'
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
- return Util.Selected1OrMoreCurves()
+ return util.Selected1OrMoreCurves()
def execute(self, context):
points = []
@@ -151,17 +169,17 @@ class MergeEnds(bpy.types.Operator):
points[0].handle_left = handle
points[0].co = new_co
- bpy.ops.curve.select_all(action='DESELECT')
+ bpy.ops.curvetools.select_all(action='DESELECT')
points[1].select_control_point = True
- bpy.ops.curve.delete()
+ bpy.ops.curvetools.delete()
selected_splines[0].bezier_points[-1 if is_last_point[0] else 0].select_control_point = True
selected_splines[1].bezier_points[-1 if is_last_point[1] else 0].select_control_point = True
- bpy.ops.curve.make_segment()
- bpy.ops.curve.select_all(action='DESELECT')
+ bpy.ops.curvetools.make_segment()
+ bpy.ops.curvetools.select_all(action='DESELECT')
return {'FINISHED'}
class Subdivide(bpy.types.Operator):
- bl_idname = 'curve.bezier_cad_subdivide'
+ bl_idname = 'curvetools.bezier_cad_subdivide'
bl_description = bl_label = 'Subdivide'
bl_options = {'REGISTER', 'UNDO'}
@@ -169,7 +187,7 @@ class Subdivide(bpy.types.Operator):
@classmethod
def poll(cls, context):
- return Util.Selected1OrMoreCurves()
+ return util.Selected1OrMoreCurves()
def execute(self, context):
current_mode = bpy.context.object.mode
@@ -193,7 +211,7 @@ class Subdivide(bpy.types.Operator):
return {'FINISHED'}
class Array(bpy.types.Operator):
- bl_idname = 'curve.bezier_cad_array'
+ bl_idname = 'curvetools.bezier_cad_array'
bl_description = bl_label = 'Array'
bl_options = {'REGISTER', 'UNDO'}
@@ -204,7 +222,7 @@ class Array(bpy.types.Operator):
@classmethod
def poll(cls, context):
- return Util.Selected1OrMoreCurves()
+ return util.Selected1OrMoreCurves()
def execute(self, context):
splines = internal.getSelectedSplines(True, True)
@@ -215,13 +233,13 @@ class Array(bpy.types.Operator):
return {'FINISHED'}
class Circle(bpy.types.Operator):
- bl_idname = 'curve.bezier_cad_circle'
+ bl_idname = 'curvetools.bezier_cad_circle'
bl_description = bl_label = 'Circle'
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
- return Util.Selected1OrMoreCurves()
+ return util.Selected1OrMoreCurves()
def execute(self, context):
segments = internal.bezierSegments(bpy.context.object.data.splines, True)
@@ -240,12 +258,12 @@ class Circle(bpy.types.Operator):
return {'FINISHED'}
class Length(bpy.types.Operator):
- bl_idname = 'curve.bezier_cad_length'
+ bl_idname = 'curvetools.bezier_cad_length'
bl_description = bl_label = 'Length'
@classmethod
def poll(cls, context):
- return Util.Selected1OrMoreCurves()
+ return util.Selected1OrMoreCurves()
def execute(self, context):
segments = internal.bezierSegments(bpy.context.object.data.splines, True)
@@ -259,4 +277,15 @@ class Length(bpy.types.Operator):
self.report({'INFO'}, bpy.utils.units.to_string(bpy.context.scene.unit_settings.system, 'LENGTH', length))
return {'FINISHED'}
-operators = [Fillet, Boolean, Intersection, MergeEnds, Subdivide, Array, Circle, Length]
+def register():
+ for cls in classes:
+ bpy.utils.register_class(operators)
+
+def unregister():
+ for cls in classes:
+ bpy.utils.unregister_class(operators)
+
+if __name__ == "__main__":
+ register()
+
+operators = [Fillet, Boolean, Intersection, HandleProjection, MergeEnds, Subdivide, Array, Circle, Length]
diff --git a/curve_tools/curve_outline.py b/curve_tools/curve_outline.py
deleted file mode 100644
index 6847e3c5..00000000
--- a/curve_tools/curve_outline.py
+++ /dev/null
@@ -1,112 +0,0 @@
-'''
-by Yann Bertrand, january 2014.
-
-BEGIN GPL LICENSE BLOCK
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software Foundation,
-Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-END GPL LICENCE BLOCK
-'''
-
-bl_info = {
- "name": "Curve Outline",
- "description": "creates an Outline",
- "category": "Object",
- "author": "Yann Bertrand (jimflim)",
- "version": (0, 4),
- "blender": (2, 69, 0),
-}
-
-import bpy
-from mathutils import Vector
-from mathutils.geometry import intersect_line_line
-
-
-def createOutline(curve, outline):
-
- for spline in curve.data.splines[:]:
- p = spline.bezier_points
- out = []
-
- n = ((p[0].handle_right-p[0].co).normalized()-(p[0].handle_left-p[0].co).normalized()).normalized()
- n = Vector((-n[1], n[0], n[2]))
- o = p[0].co+outline*n
- out.append(o)
-
- for i in range(1,len(p)):
- n = ((p[i].handle_right-p[i].co).normalized()-(p[i].handle_left-p[i].co).normalized()).normalized()
- n = Vector((-n[1], n[0], n[2]))
- o = intersect_line_line(out[-1], (out[-1]+p[i].co-p[i-1].co), p[i].co, p[i].co+n)[0]
- out.append(o)
-
- curve.data.splines.new('BEZIER')
- if spline.use_cyclic_u:
- curve.data.splines[-1].use_cyclic_u = True
- p_out = curve.data.splines[-1].bezier_points
- p_out.add(len(out)-1)
-
- for i in range(len(out)):
- p_out[i].handle_left_type = 'FREE'
- p_out[i].handle_right_type = 'FREE'
-
- p_out[i].co = out[i]
-
- if i<len(out)-1:
- l = (p[i+1].co-p[i].co).length
- l2 = (out[i]-out[i+1]).length
-
- if i==0:
- p_out[i].handle_left = out[i] + ((p[i].handle_left-p[i].co)*l2/l)
- if i<len(out)-1:
- p_out[i+1].handle_left = out[i+1] + ((p[i+1].handle_left-p[i+1].co)*l2/l)
- p_out[i].handle_right = out[i] + ((p[i].handle_right-p[i].co)*l2/l)
-
- for i in range(len(p)):
- p_out[i].handle_left_type = p[i].handle_left_type
- p_out[i].handle_right_type = p[i].handle_right_type
-
- return
-
-
-class CurveOutline(bpy.types.Operator):
- """Curve Outliner"""
- bl_idname = "object._curve_outline"
- bl_label = "Create Outline"
- bl_options = {'REGISTER', 'UNDO'}
- outline: bpy.props.FloatProperty(name="Amount", default=0.1)
-
- @classmethod
- def poll(cls, context):
- return (context.object is not None and
- context.object.type == 'CURVE')
-
- def execute(self, context):
- createOutline(context.object, self.outline)
- return {'FINISHED'}
-
- def invoke(self, context, event):
- return context.window_manager.invoke_props_popup(self, event)
-
-def menu_func(self, context):
- self.layout.operator(CurveOutline.bl_idname)
-
-def register():
- bpy.utils.register_class(CurveOutline)
-
-def unregister():
- bpy.utils.unregister_class(CurveOutline)
-
-if __name__ == "__main__":
- register()
diff --git a/curve_tools/curve_remove_doubles.py b/curve_tools/curve_remove_doubles.py
deleted file mode 100644
index 373b69c9..00000000
--- a/curve_tools/curve_remove_doubles.py
+++ /dev/null
@@ -1,110 +0,0 @@
-import bpy, mathutils
-
-
-bl_info = {
- 'name': 'Curve Remove Doubles',
- 'author': 'Michael Soluyanov',
- 'version': (1, 1),
- 'blender': (2, 80, 0),
- 'location': 'View3D > Context menu (W/RMB) > Remove Doubles',
- 'description': 'Adds command "Remove Doubles" for curves',
- 'category': 'Object'
-}
-
-def main(context, distance = 0.01):
-
- obj = context.active_object
- dellist = []
-
- if bpy.ops.object.mode_set.poll():
- bpy.ops.object.mode_set(mode='EDIT')
-
- for spline in obj.data.splines:
- if len(spline.bezier_points) > 1:
- for i in range(0, len(spline.bezier_points)):
-
- if i == 0:
- ii = len(spline.bezier_points) - 1
- else:
- ii = i - 1
-
- dot = spline.bezier_points[i];
- dot1 = spline.bezier_points[ii];
-
- while dot1 in dellist and i != ii:
- ii -= 1
- if ii < 0:
- ii = len(spline.bezier_points)-1
- dot1 = spline.bezier_points[ii]
-
- if dot.select_control_point and dot1.select_control_point and (i!=0 or spline.use_cyclic_u):
-
- if (dot.co-dot1.co).length < distance:
- # remove points and recreate hangles
- dot1.handle_right_type = "FREE"
- dot1.handle_right = dot.handle_right
- dot1.co = (dot.co + dot1.co) / 2
- dellist.append(dot)
-
- else:
- # Handles that are on main point position converts to vector,
- # if next handle are also vector
- if dot.handle_left_type == 'VECTOR' and (dot1.handle_right - dot1.co).length < distance:
- dot1.handle_right_type = "VECTOR"
- if dot1.handle_right_type == 'VECTOR' and (dot.handle_left - dot.co).length < distance:
- dot.handle_left_type = "VECTOR"
-
-
-
- bpy.ops.curve.select_all(action = 'DESELECT')
-
- for dot in dellist:
- dot.select_control_point = True
-
- count = len(dellist)
-
- bpy.ops.curve.delete(type = 'VERT')
-
- bpy.ops.curve.select_all(action = 'SELECT')
-
- return count
-
-
-
-class CurveRemvDbs(bpy.types.Operator):
- """Merge consecutive points that are near to each other"""
- bl_idname = 'curve.remove_doubles'
- bl_label = 'Remove Doubles'
- bl_options = {'REGISTER', 'UNDO'}
-
- distance: bpy.props.FloatProperty(name = 'Distance', default = 0.01)
-
- @classmethod
- def poll(cls, context):
- obj = context.active_object
- return (obj and obj.type == 'CURVE')
-
- def execute(self, context):
- removed=main(context, self.distance)
- self.report({'INFO'}, "Removed %d bezier points" % removed)
- return {'FINISHED'}
-
-
-
-
-def menu_func(self, context):
- self.layout.operator(CurveRemvDbs.bl_idname, text='Remove Doubles')
-
-def register():
- bpy.utils.register_class(CurveRemvDbs)
- bpy.types.VIEW3D_MT_edit_curve_context_menu.append(menu_func)
-
-def unregister():
- bpy.utils.unregister_class(CurveRemvDbs)
- bpy.types.VIEW3D_MT_edit_curve_context_menu.remove(menu_func)
-
-if __name__ == "__main__":
- register()
-
-
-
diff --git a/curve_tools/exports.py b/curve_tools/exports.py
index b3b7d900..130fb2a7 100644
--- a/curve_tools/exports.py
+++ b/curve_tools/exports.py
@@ -224,4 +224,15 @@ class GCodeExport(bpy.types.Operator, ExportHelper):
f.write(speed_code+' X{:.3f} Y{:.3f} Z{:.3f}\n'.format(position[0], position[1], position[2]))
return {'FINISHED'}
+def register():
+ for cls in classes:
+ bpy.utils.register_class(operators)
+
+def unregister():
+ for cls in classes:
+ bpy.utils.unregister_class(operators)
+
+if __name__ == "__main__":
+ register()
+
operators = [SvgExport, GCodeExport]
diff --git a/curve_tools/fillet.py b/curve_tools/fillet.py
new file mode 100644
index 00000000..3bb3e492
--- /dev/null
+++ b/curve_tools/fillet.py
@@ -0,0 +1,143 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and / or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+bl_info = {
+ 'name': 'Curve Fillet',
+ 'author': 'Spivak Vladimir (cwolf3d)',
+ 'version': (0, 0, 1),
+ 'blender': (2, 80, 0),
+ 'location': 'Curve Tools addon. (N) Panel',
+ 'description': 'Various types of fillet (chamfering)',
+ 'warning': '', # used for warning icon and text in addons panel
+ 'wiki_url': '',
+ 'tracker_url': '',
+ 'category': 'Curve'}
+
+
+import bpy
+from bpy.props import *
+from bpy_extras import object_utils, view3d_utils
+from mathutils import *
+from math import *
+
+def click(self, context, event):
+ bpy.ops.object.mode_set(mode = 'EDIT')
+ bpy.context.view_layer.update()
+
+
+def remove_handler(handlers):
+ for handler in handlers:
+ try:
+ bpy.types.SpaceView3D.draw_handler_remove(handler, 'WINDOW')
+ except:
+ pass
+ for handler in handlers:
+ handlers.remove(handler)
+
+
+class Fillet(bpy.types.Operator):
+ bl_idname = "curvetools.fillet"
+ bl_label = "Curve Fillet"
+ bl_description = "Curve Fillet"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ x: IntProperty(name="x", description="x")
+ y: IntProperty(name="y", description="y")
+ location3D: FloatVectorProperty(name = "",
+ description = "Start location",
+ default = (0.0, 0.0, 0.0),
+ subtype = 'XYZ')
+
+ handlers = []
+
+ def execute(self, context):
+ self.report({'INFO'}, "ESC or TAB - cancel")
+ bpy.ops.object.mode_set(mode = 'EDIT')
+
+ # color change in the panel
+ self.path_color = bpy.context.scene.curvetools.path_color
+ self.path_thickness = bpy.context.scene.curvetools.path_thickness
+
+ def modal(self, context, event):
+ context.area.tag_redraw()
+
+ if event.type in {'ESC', 'TAB'}: # Cancel
+ remove_handler(self.handlers)
+ return {'CANCELLED'}
+
+ if event.type in {'X', 'DEL'}: # Cancel
+ remove_handler(self.handlers)
+ bpy.ops.curve.delete(type='VERT')
+ return {'RUNNING_MODAL'}
+
+ elif event.alt and event.shift and event.type == 'LEFTMOUSE':
+ click(self, context, event)
+
+ elif event.alt and not event.shift and event.type == 'LEFTMOUSE':
+ remove_handler(self.handlers)
+ bpy.ops.curve.select_all(action='DESELECT')
+ click(self, context, event)
+
+ elif event.alt and event.type == 'RIGHTMOUSE':
+ remove_handler(self.handlers)
+ bpy.ops.curve.select_all(action='DESELECT')
+ click(self, context, event)
+
+ elif event.alt and not event.shift and event.shift and event.type == 'RIGHTMOUSE':
+ click(self, context, event)
+
+ elif event.type == 'A':
+ remove_handler(self.handlers)
+ bpy.ops.curve.select_all(action='DESELECT')
+
+ elif event.type == 'MOUSEMOVE': #
+ self.x = event.mouse_x
+ self.y = event.mouse_y
+ region = bpy.context.region
+ rv3d = bpy.context.space_data.region_3d
+ self.location3D = view3d_utils.region_2d_to_location_3d(
+ region,
+ rv3d,
+ (event.mouse_region_x, event.mouse_region_y),
+ (0.0, 0.0, 0.0)
+ )
+
+ return {'PASS_THROUGH'}
+
+ def invoke(self, context, event):
+ self.execute(context)
+ context.window_manager.modal_handler_add(self)
+ return {'RUNNING_MODAL'}
+
+ @classmethod
+ def poll(cls, context):
+ return (context.object is not None and
+ context.object.type == 'CURVE')
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(operators)
+
+def unregister():
+ for cls in classes:
+ bpy.utils.unregister_class(operators)
+
+if __name__ == "__main__":
+ register()
+
+operators = [Fillet]
diff --git a/curve_tools/internal.py b/curve_tools/internal.py
index 6741bb22..e967fc6e 100644
--- a/curve_tools/internal.py
+++ b/curve_tools/internal.py
@@ -408,6 +408,25 @@ def bezierMultiIntersection(segments):
prepareSegmentIntersections(segments)
subdivideBezierSegments(segments)
+def bezierProjectHandles(segments):
+ insertions = []
+ index_offset = 0
+ for segment in segments:
+ if len(insertions) > 0 and insertions[-1][0] != segment['spline']:
+ index_offset = 0
+ points = bezierSegmentPoints(segment['beginPoint'], segment['endPoint'])
+ paramA, paramB, pointA, pointB = nearestPointOfLines(points[0], points[1]-points[0], points[3], points[2]-points[3])
+ if pointA and pointB:
+ segment['cuts'].append({'param': 0.5})
+ insertions.append((segment['spline'], segment['beginIndex']+1+index_offset, (pointA+pointB)*0.5))
+ index_offset += 1
+ subdivideBezierSegments(segments)
+ for insertion in insertions:
+ bezier_point = insertion[0].bezier_points[insertion[1]]
+ bezier_point.co = insertion[2]
+ bezier_point.handle_left_type = 'VECTOR'
+ bezier_point.handle_right_type = 'VECTOR'
+
def bezierSubivideAt(points, params):
if len(params) == 0:
return []
diff --git a/curve_tools/CurveIntersections.py b/curve_tools/intersections.py
index 06254701..77f19861 100644
--- a/curve_tools/CurveIntersections.py
+++ b/curve_tools/intersections.py
@@ -1,7 +1,7 @@
import bpy
-from . import Math
-from . import Curves
-from . import Util
+from . import mathematics
+from . import curves
+from . import util
from mathutils import Vector
@@ -58,7 +58,7 @@ class BezierSegmentsIntersector:
Q0 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=segPar20)
Q1 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=segPar21)
- intersectionPointData = Math.CalcIntersectionPointLineSegments(P0, P1, Q0, Q1, limitDistance)
+ intersectionPointData = mathematics.CalcIntersectionPointLineSegments(P0, P1, Q0, Q1, limitDistance)
if intersectionPointData is None:
continue
@@ -94,15 +94,15 @@ class BezierSegmentsIntersector:
Q0 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=segPar20)
Q1 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=segPar21)
- intersectionPointData = Math.CalcIntersectionPointLineSegments(P0, P1, Q0, Q1, limitDistance)
+ intersectionPointData = mathematics.CalcIntersectionPointLineSegments(P0, P1, Q0, Q1, limitDistance)
if intersectionPointData is None:
continue
# intersection point can't be an existing point
intersectionSegment1Parameter = segPar10 + (intersectionPointData[0] / (fltNrSamples1))
worldPoint1 = self.worldMatrix1 @ self.segment1.CalcPoint(parameter=intersectionSegment1Parameter)
- if (Math.IsSamePoint(P0, worldPoint1, limitDistance)) or \
- (Math.IsSamePoint(P1, worldPoint1, limitDistance)):
+ if (mathematics.IsSamePoint(P0, worldPoint1, limitDistance)) or \
+ (mathematics.IsSamePoint(P1, worldPoint1, limitDistance)):
intersectionPoint1 = None
else:
@@ -112,8 +112,8 @@ class BezierSegmentsIntersector:
intersectionSegment2Parameter = segPar20 + (intersectionPointData[1] / (fltNrSamples2))
worldPoint2 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=intersectionSegment2Parameter)
- if (Math.IsSamePoint(Q0, worldPoint2, limitDistance)) or \
- (Math.IsSamePoint(Q1, worldPoint2, limitDistance)):
+ if (mathematics.IsSamePoint(Q0, worldPoint2, limitDistance)) or \
+ (mathematics.IsSamePoint(Q1, worldPoint2, limitDistance)):
intersectionPoint2 = None
else:
@@ -143,7 +143,7 @@ class BezierSegmentsIntersector:
Q0 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=segPar20)
Q1 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=segPar21)
- intersectionPointData = Math.CalcIntersectionPointsLineSegmentsDIR(P0, P1, Q0, Q1, algoDIR)
+ intersectionPointData = mathematics.CalcIntersectionPointsLineSegmentsDIR(P0, P1, Q0, Q1, algoDIR)
if intersectionPointData is None:
continue
@@ -183,15 +183,15 @@ class BezierSegmentsIntersector:
Q0 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=segPar20)
Q1 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=segPar21)
- intersectionPointData = Math.CalcIntersectionPointsLineSegmentsDIR(P0, P1, Q0, Q1, algoDIR)
+ intersectionPointData = mathematics.CalcIntersectionPointsLineSegmentsDIR(P0, P1, Q0, Q1, algoDIR)
if intersectionPointData is None:
continue
# intersection point can't be an existing point
intersectionSegment1Parameter = segPar10 + (intersectionPointData[0] / (fltNrSamples1))
worldPoint1 = self.worldMatrix1 @ self.segment1.CalcPoint(parameter=intersectionSegment1Parameter)
- if (Math.IsSamePoint(P0, worldPoint1, limitDistance)) or \
- (Math.IsSamePoint(P1, worldPoint1, limitDistance)):
+ if (mathematics.IsSamePoint(P0, worldPoint1, limitDistance)) or \
+ (mathematics.IsSamePoint(P1, worldPoint1, limitDistance)):
intersectionPoint1 = None
else:
@@ -201,8 +201,8 @@ class BezierSegmentsIntersector:
intersectionSegment2Parameter = segPar20 + (intersectionPointData[1] / (fltNrSamples2))
worldPoint2 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=intersectionSegment2Parameter)
- if (Math.IsSamePoint(Q0, worldPoint2, limitDistance)) or \
- (Math.IsSamePoint(Q1, worldPoint2, limitDistance)):
+ if (mathematics.IsSamePoint(Q0, worldPoint2, limitDistance)) or \
+ (mathematics.IsSamePoint(Q1, worldPoint2, limitDistance)):
intersectionPoint2 = None
else:
@@ -232,7 +232,7 @@ class BezierSegmentsIntersector:
Q0 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=segPar20)
Q1 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=segPar21)
- intersectionPointData = Math.CalcIntersectionPointsLineSegmentsPOV(P0, P1, Q0, Q1, algoPOV)
+ intersectionPointData = mathematics.CalcIntersectionPointsLineSegmentsPOV(P0, P1, Q0, Q1, algoPOV)
if intersectionPointData is None:
continue
@@ -272,15 +272,15 @@ class BezierSegmentsIntersector:
Q0 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=segPar20)
Q1 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=segPar21)
- intersectionPointData = Math.CalcIntersectionPointsLineSegmentsPOV(P0, P1, Q0, Q1, algoPOV)
+ intersectionPointData = mathematics.CalcIntersectionPointsLineSegmentsPOV(P0, P1, Q0, Q1, algoPOV)
if intersectionPointData is None:
continue
# intersection point can't be an existing point
intersectionSegment1Parameter = segPar10 + (intersectionPointData[0] / fltNrSamples1)
worldPoint1 = self.worldMatrix1 @ self.segment1.CalcPoint(parameter=intersectionSegment1Parameter)
- if (Math.IsSamePoint(P0, worldPoint1, limitDistance)) or \
- (Math.IsSamePoint(P1, worldPoint1, limitDistance)):
+ if (mathematics.IsSamePoint(P0, worldPoint1, limitDistance)) or \
+ (mathematics.IsSamePoint(P1, worldPoint1, limitDistance)):
intersectionPoint1 = None
else:
@@ -290,8 +290,8 @@ class BezierSegmentsIntersector:
intersectionSegment2Parameter = segPar20 + (intersectionPointData[1] / fltNrSamples2)
worldPoint2 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=intersectionSegment2Parameter)
- if (Math.IsSamePoint(Q0, worldPoint2, limitDistance)) or \
- (Math.IsSamePoint(Q1, worldPoint2, limitDistance)):
+ if (mathematics.IsSamePoint(Q0, worldPoint2, limitDistance)) or \
+ (mathematics.IsSamePoint(Q1, worldPoint2, limitDistance)):
intersectionPoint2 = None
else:
@@ -341,7 +341,7 @@ class BezierSegmentsIntersector:
Q0 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=segPar20)
Q1 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=segPar21)
- intersectionPointData = Math.CalcIntersectionPointLineSegments(P0, P1, Q0, Q1, limitDistance)
+ intersectionPointData = mathematics.CalcIntersectionPointLineSegments(P0, P1, Q0, Q1, limitDistance)
if intersectionPointData is None:
continue
@@ -382,7 +382,7 @@ class BezierSegmentsIntersector:
Q0 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=segPar20)
Q1 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=segPar21)
- intersectionPointData = Math.CalcIntersectionPointsLineSegmentsDIR(P0, P1, Q0, Q1, algoDIR)
+ intersectionPointData = mathematics.CalcIntersectionPointsLineSegmentsDIR(P0, P1, Q0, Q1, algoDIR)
if intersectionPointData is None:
continue
@@ -423,7 +423,7 @@ class BezierSegmentsIntersector:
Q0 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=segPar20)
Q1 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=segPar21)
- intersectionPointData = Math.CalcIntersectionPointsLineSegmentsPOV(P0, P1, Q0, Q1, algoPOV)
+ intersectionPointData = mathematics.CalcIntersectionPointsLineSegmentsPOV(P0, P1, Q0, Q1, algoPOV)
if intersectionPointData is None:
continue
@@ -508,8 +508,8 @@ class CurvesIntersector:
if blenderActiveCurve == blenderOtherCurve:
blenderOtherCurve = selObjects[1]
- aCurve = Curves.Curve(blenderActiveCurve)
- oCurve = Curves.Curve(blenderOtherCurve)
+ aCurve = curves.Curve(blenderActiveCurve)
+ oCurve = curves.Curve(blenderOtherCurve)
return CurvesIntersector(aCurve, oCurve)
@@ -528,7 +528,7 @@ class CurvesIntersector:
algo = bpy.context.scene.curvetools.IntersectCurvesAlgorithm
if algo == 'From View':
- regionView3D = Util.GetFirstRegionView3D()
+ regionView3D = util.GetFirstRegionView3D()
if regionView3D is None:
print("### ERROR: regionView3D is None. Stopping.")
return
diff --git a/curve_tools/Math.py b/curve_tools/mathematics.py
index 4a61af4d..4a61af4d 100644
--- a/curve_tools/Math.py
+++ b/curve_tools/mathematics.py
diff --git a/curve_tools/outline.py b/curve_tools/outline.py
new file mode 100644
index 00000000..66cc29e4
--- /dev/null
+++ b/curve_tools/outline.py
@@ -0,0 +1,118 @@
+'''
+by Yann Bertrand, january 2014.
+
+BEGIN GPL LICENSE BLOCK
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software Foundation,
+Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+END GPL LICENCE BLOCK
+'''
+
+bl_info = {
+ "name": "Curve Outline",
+ "description": "creates an Outline",
+ "category": "Object",
+ "author": "Yann Bertrand (jimflim)",
+ "version": (0, 4),
+ "blender": (2, 69, 0),
+}
+
+import bpy
+from mathutils import Vector
+from mathutils.geometry import intersect_line_line
+
+from . import util
+
+
+def createOutline(curve, outline):
+
+ for spline in curve.data.splines[:]:
+ if spline.type == 'BEZIER':
+ p = spline.bezier_points
+ out = []
+
+ n = ((p[0].handle_right-p[0].co).normalized()-(p[0].handle_left-p[0].co).normalized()).normalized()
+ n = Vector((-n[1], n[0], n[2]))
+ o = p[0].co+outline*n
+ out.append(o)
+
+ for i in range(1,len(p)):
+ n = ((p[i].handle_right-p[i].co).normalized()-(p[i].handle_left-p[i].co).normalized()).normalized()
+ n = Vector((-n[1], n[0], n[2]))
+ o = intersect_line_line(out[-1], (out[-1]+p[i].co-p[i-1].co), p[i].co, p[i].co+n)[0]
+ out.append(o)
+
+ curve.data.splines.new('BEZIER')
+ if spline.use_cyclic_u:
+ curve.data.splines[-1].use_cyclic_u = True
+ p_out = curve.data.splines[-1].bezier_points
+ p_out.add(len(out)-1)
+
+ for i in range(len(out)):
+ p_out[i].handle_left_type = 'FREE'
+ p_out[i].handle_right_type = 'FREE'
+
+ p_out[i].co = out[i]
+
+ if i<len(out)-1:
+ l = (p[i+1].co-p[i].co).length
+ l2 = (out[i]-out[i+1]).length
+
+ if i==0:
+ p_out[i].handle_left = out[i] + ((p[i].handle_left-p[i].co)*l2/l)
+ if i<len(out)-1:
+ p_out[i+1].handle_left = out[i+1] + ((p[i+1].handle_left-p[i+1].co)*l2/l)
+ p_out[i].handle_right = out[i] + ((p[i].handle_right-p[i].co)*l2/l)
+
+ for i in range(len(p)):
+ p_out[i].handle_left_type = p[i].handle_left_type
+ p_out[i].handle_right_type = p[i].handle_right_type
+
+ return
+
+
+class CurveOutline(bpy.types.Operator):
+ """Curve Outliner"""
+ bl_idname = "curvetools.outline"
+ bl_label = "Create Outline"
+ bl_options = {'REGISTER', 'UNDO'}
+ outline: bpy.props.FloatProperty(name="Amount", default=0.1)
+
+ @classmethod
+ def poll(cls, context):
+ return util.Selected1OrMoreCurves()
+
+ def execute(self, context):
+ createOutline(context.object, self.outline)
+ return {'FINISHED'}
+
+ def invoke(self, context, event):
+ return context.window_manager.invoke_props_popup(self, event)
+
+def menu_func(self, context):
+ self.layout.operator(CurveOutline.bl_idname)
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(operators)
+
+def unregister():
+ for cls in classes:
+ bpy.utils.unregister_class(operators)
+
+if __name__ == "__main__":
+ register()
+
+operators = [CurveOutline]
diff --git a/curve_tools/PathFinder.py b/curve_tools/path_finder.py
index ab11a4e7..366f9bcc 100644
--- a/curve_tools/PathFinder.py
+++ b/curve_tools/path_finder.py
@@ -41,12 +41,8 @@ from bpy_extras import object_utils, view3d_utils
from mathutils import *
from math import *
-from . import Properties
-from . import Curves
-from . import CurveIntersections
-from . import Util
-from . import Surfaces
-from . import Math
+from . import mathematics
+from . import util
def get_bezier_points(spline, matrix_world):
point_list = []
@@ -55,7 +51,7 @@ def get_bezier_points(spline, matrix_world):
for i in range(0, len_bezier_points - 1):
point_list.extend([matrix_world @ spline.bezier_points[i].co])
for t in range(0, 100, 2):
- h = Math.subdivide_cubic_bezier(spline.bezier_points[i].co,
+ h = mathematics.subdivide_cubic_bezier(spline.bezier_points[i].co,
spline.bezier_points[i].handle_right,
spline.bezier_points[i + 1].handle_left,
spline.bezier_points[i + 1].co,
@@ -64,7 +60,7 @@ def get_bezier_points(spline, matrix_world):
if spline.use_cyclic_u and len_bezier_points > 2:
point_list.extend([matrix_world @ spline.bezier_points[len_bezier_points - 1].co])
for t in range(0, 100, 2):
- h = Math.subdivide_cubic_bezier(spline.bezier_points[len_bezier_points - 1].co,
+ h = mathematics.subdivide_cubic_bezier(spline.bezier_points[len_bezier_points - 1].co,
spline.bezier_points[len_bezier_points - 1].handle_right,
spline.bezier_points[0].handle_left,
spline.bezier_points[0].co,
@@ -160,7 +156,7 @@ def click(self, context, event):
if i < len_bezier_points - 1:
for t in range(0, 100, 2):
- h = Math.subdivide_cubic_bezier(spline.bezier_points[i].co,
+ h = mathematics.subdivide_cubic_bezier(spline.bezier_points[i].co,
spline.bezier_points[i].handle_right,
spline.bezier_points[i + 1].handle_left,
spline.bezier_points[i + 1].co,
@@ -172,7 +168,7 @@ def click(self, context, event):
if spline.use_cyclic_u and len_bezier_points > 2:
for t in range(0, 100, 2):
- h = Math.subdivide_cubic_bezier(spline.bezier_points[len_bezier_points - 1].co,
+ h = mathematics.subdivide_cubic_bezier(spline.bezier_points[len_bezier_points - 1].co,
spline.bezier_points[len_bezier_points - 1].handle_right,
spline.bezier_points[0].handle_left,
spline.bezier_points[0].co,
@@ -312,14 +308,18 @@ class PathFinder(bpy.types.Operator):
@classmethod
def poll(cls, context):
- return (context.object is not None and
- context.object.type == 'CURVE')
+ return util.Selected1OrMoreCurves()
def register():
- bpy.utils.register_class(PathFinder)
+ for cls in classes:
+ bpy.utils.register_class(operators)
def unregister():
- bpy.utils.unregister_class(PathFinder)
+ for cls in classes:
+ bpy.utils.unregister_class(operators)
if __name__ == "__main__":
register()
+
+
+operators = [PathFinder]
diff --git a/curve_tools/remove_doubles.py b/curve_tools/remove_doubles.py
new file mode 100644
index 00000000..151b19b0
--- /dev/null
+++ b/curve_tools/remove_doubles.py
@@ -0,0 +1,135 @@
+import bpy, mathutils
+from . import util
+
+bl_info = {
+ 'name': 'Curve Remove Doubles',
+ 'author': 'Michael Soluyanov',
+ 'version': (1, 1),
+ 'blender': (2, 80, 0),
+ 'location': 'View3D > Context menu (W/RMB) > Remove Doubles',
+ 'description': 'Adds comand "Remove Doubles" for curves',
+ 'category': 'Add Curve'
+}
+
+def main(context, distance = 0.01):
+
+ selected_Curves = util.GetSelectedCurves()
+ if bpy.ops.object.mode_set.poll():
+ bpy.ops.object.mode_set(mode='EDIT')
+
+ for curve in selected_Curves:
+ bezier_dellist = []
+ dellist = []
+
+ for spline in curve.data.splines:
+ if spline.type == 'BEZIER':
+ if len(spline.bezier_points) > 1:
+ for i in range(0, len(spline.bezier_points)):
+
+ if i == 0:
+ ii = len(spline.bezier_points) - 1
+ else:
+ ii = i - 1
+
+ dot = spline.bezier_points[i];
+ dot1 = spline.bezier_points[ii];
+
+ while dot1 in bezier_dellist and i != ii:
+ ii -= 1
+ if ii < 0:
+ ii = len(spline.bezier_points)-1
+ dot1 = spline.bezier_points[ii]
+
+ if dot.select_control_point and dot1.select_control_point and (i!=0 or spline.use_cyclic_u):
+
+ if (dot.co-dot1.co).length < distance:
+ # remove points and recreate hangles
+ dot1.handle_right_type = "FREE"
+ dot1.handle_right = dot.handle_right
+ dot1.co = (dot.co + dot1.co) / 2
+ bezier_dellist.append(dot)
+
+ else:
+ # Handles that are on main point position converts to vector,
+ # if next handle are also vector
+ if dot.handle_left_type == 'VECTOR' and (dot1.handle_right - dot1.co).length < distance:
+ dot1.handle_right_type = "VECTOR"
+ if dot1.handle_right_type == 'VECTOR' and (dot.handle_left - dot.co).length < distance:
+ dot.handle_left_type = "VECTOR"
+ else:
+ if len(spline.points) > 1:
+ for i in range(0, len(spline.points)):
+
+ if i == 0:
+ ii = len(spline.points) - 1
+ else:
+ ii = i - 1
+
+ dot = spline.points[i];
+ dot1 = spline.points[ii];
+
+ while dot1 in dellist and i != ii:
+ ii -= 1
+ if ii < 0:
+ ii = len(spline.points)-1
+ dot1 = spline.points[ii]
+
+ if dot.select and dot1.select and (i!=0 or spline.use_cyclic_u):
+
+ if (dot.co-dot1.co).length < distance:
+ dot1.co = (dot.co + dot1.co) / 2
+ dellist.append(dot)
+
+ bpy.ops.curve.select_all(action = 'DESELECT')
+
+ for dot in bezier_dellist:
+ dot.select_control_point = True
+
+ for dot in dellist:
+ dot.select = True
+
+ bezier_count = len(bezier_dellist)
+ count = len(dellist)
+
+ bpy.ops.curve.delete(type = 'VERT')
+
+ bpy.ops.curve.select_all(action = 'DESELECT')
+
+ return bezier_count + count
+
+
+
+class CurveRemvDbs(bpy.types.Operator):
+ """Merge consecutive points that are near to each other"""
+ bl_idname = 'curvetools.remove_doubles'
+ bl_label = 'Remove Doubles'
+ bl_options = {'REGISTER', 'UNDO'}
+
+ distance: bpy.props.FloatProperty(name = 'Distance', default = 0.01)
+
+ @classmethod
+ def poll(cls, context):
+ return util.Selected1Curve()
+
+ def execute(self, context):
+ removed=main(context, self.distance)
+ self.report({'INFO'}, "Removed %d bezier points" % removed)
+ return {'FINISHED'}
+
+
+
+def menu_func(self, context):
+ self.layout.operator(CurveRemvDbs.bl_idname, text='Remove Doubles')
+
+def register():
+ bpy.utils.register_class(CurveRemvDbs)
+ bpy.types.VIEW3D_MT_edit_curve_context_menu.append(menu_func)
+
+def unregister():
+ bpy.utils.unregister_class(CurveRemvDbs)
+ bpy.types.VIEW3D_MT_edit_curve_context_menu.remove(menu_func)
+
+if __name__ == "__main__":
+ register()
+
+operators = [CurveRemvDbs]
diff --git a/curve_tools/ShowCurveResolution.py b/curve_tools/show_resolution.py
index 6386fe4d..78acc1e3 100644
--- a/curve_tools/ShowCurveResolution.py
+++ b/curve_tools/show_resolution.py
@@ -18,7 +18,7 @@
#
-# LOAD MODULE #
+# LOAD MODUL #
import bpy
from bpy import *
from bpy.props import *
@@ -79,7 +79,7 @@ def draw(self, context, splines, curve_vertcolor, matrix_world):
class ShowCurveResolution(bpy.types.Operator):
- bl_idname = "curve.show_resolution"
+ bl_idname = "curvetools.show_resolution"
bl_label = "Show Curve Resolution"
bl_description = "Show curve Resolution / [ESC] - remove"
@@ -129,3 +129,18 @@ class ShowCurveResolution(bpy.types.Operator):
def poll(cls, context):
return (context.object is not None and
context.object.type == 'CURVE')
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(operators)
+
+def unregister():
+ for cls in classes:
+ bpy.utils.unregister_class(operators)
+
+if __name__ == "__main__":
+ register()
+
+operators = [
+ ShowCurveResolution,
+ ]
diff --git a/curve_tools/SplinesSequence.py b/curve_tools/splines_sequence.py
index c64ceec1..5d11aedf 100644
--- a/curve_tools/SplinesSequence.py
+++ b/curve_tools/splines_sequence.py
@@ -89,7 +89,7 @@ def draw(self, context, splines, sequence_color, font_thickness, font_size, matr
i += font_size + font_size * 0.5
class ShowSplinesSequence(bpy.types.Operator):
- bl_idname = "curve.show_splines_sequence"
+ bl_idname = "curvetools.show_splines_sequence"
bl_label = "Show Splines Sequence"
bl_description = "Show Splines Sequence / [ESC] - remove"
@@ -229,7 +229,7 @@ def rearrange(dataCurve, select_spline, command):
rearrangesplines(dataCurve, select_spline, select_spline - 1)
class RearrangeSpline(bpy.types.Operator):
- bl_idname = "curve.rearrange_spline"
+ bl_idname = "curvetools.rearrange_spline"
bl_label = "Rearrange Spline"
bl_description = "Rearrange Spline"
@@ -273,3 +273,16 @@ class RearrangeSpline(bpy.types.Operator):
def poll(cls, context):
return (context.object is not None and
context.object.type == 'CURVE')
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(operators)
+
+def unregister():
+ for cls in classes:
+ bpy.utils.unregister_class(operators)
+
+if __name__ == "__main__":
+ register()
+
+operators = [ShowSplinesSequence, RearrangeSpline]
diff --git a/curve_tools/toolpath.py b/curve_tools/toolpath.py
index 8353f7ac..2b422280 100644
--- a/curve_tools/toolpath.py
+++ b/curve_tools/toolpath.py
@@ -21,7 +21,7 @@ from mathutils import Vector, Matrix
from . import internal
class OffsetCurve(bpy.types.Operator):
- bl_idname = 'curve.add_toolpath_offset_curve'
+ bl_idname = 'curvetools.add_toolpath_offset_curve'
bl_description = bl_label = 'Offset Curve'
bl_options = {'REGISTER', 'UNDO'}
@@ -64,7 +64,7 @@ class OffsetCurve(bpy.types.Operator):
return {'FINISHED'}
class SliceMesh(bpy.types.Operator):
- bl_idname = 'curve.add_toolpath_slice_mesh'
+ bl_idname = 'curvetools.add_toolpath_slice_mesh'
bl_description = bl_label = 'Slice Mesh'
bl_options = {'REGISTER', 'UNDO'}
@@ -116,7 +116,7 @@ class SliceMesh(bpy.types.Operator):
return {'FINISHED'}
class DiscretizeCurve(bpy.types.Operator):
- bl_idname = 'curve.add_toolpath_discretize_curve'
+ bl_idname = 'curvetools.add_toolpath_discretize_curve'
bl_description = bl_label = 'Discretize Curve'
bl_options = {'REGISTER', 'UNDO'}
@@ -151,7 +151,7 @@ class DiscretizeCurve(bpy.types.Operator):
return {'FINISHED'}
class Truncate(bpy.types.Operator):
- bl_idname = 'curve.add_toolpath_truncate'
+ bl_idname = 'curvetools.add_toolpath_truncate'
bl_description = bl_label = 'Truncate'
bl_options = {'REGISTER', 'UNDO'}
@@ -197,7 +197,7 @@ class Truncate(bpy.types.Operator):
return {'FINISHED'}
class RectMacro(bpy.types.Operator):
- bl_idname = 'curve.add_toolpath_rect_macro'
+ bl_idname = 'curvetools.add_toolpath_rect_macro'
bl_description = bl_label = 'Rect Macro'
bl_options = {'REGISTER', 'UNDO'}
@@ -233,7 +233,7 @@ class RectMacro(bpy.types.Operator):
return {'FINISHED'}
class DrillMacro(bpy.types.Operator):
- bl_idname = 'curve.add_toolpath_drill_macro'
+ bl_idname = 'curvetools.add_toolpath_drill_macro'
bl_description = bl_label = 'Drill Macro'
bl_options = {'REGISTER', 'UNDO'}
@@ -284,4 +284,15 @@ class DrillMacro(bpy.types.Operator):
internal.addPolygonSpline(bpy.context.object, False, vertices, weights)
return {'FINISHED'}
+def register():
+ for cls in classes:
+ bpy.utils.register_class(operators)
+
+def unregister():
+ for cls in classes:
+ bpy.utils.unregister_class(operators)
+
+if __name__ == "__main__":
+ register()
+
operators = [OffsetCurve, SliceMesh, DiscretizeCurve, Truncate, RectMacro, DrillMacro]