Addon: Curve Tools: Refactoring. Added context menu.
This commit is contained in:
parent
db2c65d9e7
commit
04f482c5aa
|
@ -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]
|
||||
|
|
|
@ -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,
|
||||
]
|
||||
|
|
|
@ -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,
|
||||
]
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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("curve.bezier_cad_array", text="Array selected spline")
|
||||
row.operator("curvetools.add_toolpath_discretize_curve", text="Discretize Curve")
|
||||
row = col.row(align=True)
|
||||
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)
|
||||
|
||||
|
|
|
@ -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'}
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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()
|
|
@ -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()
|
||||
|
||||
|
||||
|
|
@ -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]
|
||||
|
|
|
@ -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]
|
|
@ -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 []
|
||||
|
|
|
@ -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
|
|
@ -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]
|
|
@ -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]
|
|
@ -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]
|
|
@ -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,
|
||||
]
|
|
@ -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]
|
|
@ -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]
|
||||
|
|
Loading…
Reference in New Issue