Commit 2da100e4 authored by Brendon Murphy's avatar Brendon Murphy

major update to curve tools.

code rewrite thanks to guy lateur.
parent 8e8163bc
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
from mathutils import *
def IsSamePoint(v31, v32, limitDistance):
if (v31 - v32).magnitude < limitDistance: return True
return False
class Plane:
@staticmethod
def XY():
p1 = Vector((0, 0, 0))
p2 = Vector((1, 0, 0))
p3 = Vector((0, 1, 0))
return Plane(p1, p2, p3)
# plane equation: (p - position).dot(normal) = 0
def __init__(self, P1, P2, P3):
self.normal = (P2 - P1).cross(P3 - P1)
self.normal.normalize()
self.position = P1
def CalcIntersectionPointLineSegment(self, PL1, PL2):
DL = PL2 - PL1
try: rvPar = ((self.position - PL1).dot(self.normal)) / (DL.dot(self.normal))
except: return None
return rvPar
def CalcNormalParameter(self, vector):
return (vector - self.position).dot(self.normal)
def CalcProjection(self, vector):
normalParameter = self.CalcNormalParameter(vector)
rvv3 = vector - (self.normal * normalParameter)
return [normalParameter, rvv3]
# http://geomalgorithms.com/a07-_distance.html
def CalcClosestPointLineSegments(v3P0, v3P1, v3Q0, v3Q1):
u = v3P1 - v3P0
v = v3Q1 - v3Q0
w0 = v3P0 - v3Q0
a = u.dot(u)
b = u.dot(v)
c = v.dot(v)
d = u.dot(w0)
e = v.dot(w0)
try: parP = (b * e - c * d) / (a * c - b * b)
except: return None
try: parQ = (a * e - b * d) / (a * c - b * b)
except: return None
return [parP, parQ]
def CalcIntersectionPointLineSegments(v3P0, v3P1, v3Q0, v3Q1, limitDistance):
rvList = CalcClosestPointLineSegments(v3P0, v3P1, v3Q0, v3Q1)
if rvList is None: return None
parP = rvList[0]
if parP < 0.0: return None
if parP > 1.0: return None
parQ = rvList[1]
if parQ < 0.0: return None
if parQ > 1.0: return None
pointP = v3P0 + ((v3P1 - v3P0) * parP)
pointQ = v3Q0 + ((v3Q1 - v3Q0) * parQ)
if not IsSamePoint(pointP, pointQ, limitDistance): return None
return [parP, parQ, pointP, pointQ]
def CalcIntersectionPointsLineSegmentsPOV(v3P0, v3P1, v3Q0, v3Q1, v3POV):
planeQ = Plane(v3POV, v3Q0, v3Q1)
parP = planeQ.CalcIntersectionPointLineSegment(v3P0, v3P1)
if parP is None: return None
if parP < 0.0: return None
if parP > 1.0: return None
planeP = Plane(v3POV, v3P0, v3P1)
parQ = planeP.CalcIntersectionPointLineSegment(v3Q0, v3Q1)
if parQ is None: return None
if parQ < 0.0: return None
if parQ > 1.0: return None
return [parP, parQ]
def CalcIntersectionPointsLineSegmentsDIR(v3P0, v3P1, v3Q0, v3Q1, v3DIR):
v3POV = v3Q0 + v3DIR
planeQ = Plane(v3POV, v3Q0, v3Q1)
parP = planeQ.CalcIntersectionPointLineSegment(v3P0, v3P1)
if parP is None: return None
if parP < 0.0: return None
if parP > 1.0: return None
v3POV = v3P0 + v3DIR
planeP = Plane(v3POV, v3P0, v3P1)
parQ = planeP.CalcIntersectionPointLineSegment(v3Q0, v3Q1)
if parQ is None: return None
if parQ < 0.0: return None
if parQ > 1.0: return None
return [parP, parQ]
def CalcRotationMatrix(v3From, v3To):
cross = v3From.cross(v3To)
try: angle = v3From.angle(v3To)
except: return Matrix.Identity(4)
return Matrix.Rotation(angle, 4, cross) # normalize axis?
This diff is collapsed.
import bpy
from . import Operators
class Panel(bpy.types.Panel):
bl_label = "Curve Tools 2"
bl_space_type = "VIEW_3D"
bl_region_type = "TOOLS"
bl_options = {'DEFAULT_CLOSED'}
bl_category = "Addons"
@classmethod
def poll(cls, context):
if len(context.selected_objects) > 0:
return (context.active_object.type == "CURVE")
def draw(self, context):
# Z. selection
self.layout.label(text = "selection:")
boxSelection = self.layout.box()
# A.1 curve info/length
row = boxSelection.row(align = True)
row.operator("curvetools2.operatorselectioninfo", text = "Selection Info")
row.prop(context.scene.curvetools, "NrSelectedObjects", text = "")
# A. 1 curve
self.layout.label(text = "1 curve:")
box1Curve = self.layout.box()
# A.1 curve info/length
row = box1Curve.row(align = True)
row.operator("curvetools2.operatorcurveinfo", text = "Curve info")
row = box1Curve.row(align = True)
row.operator("curvetools2.operatorcurvelength", text = "Calc Length")
row.prop(context.scene.curvetools, "CurveLength", text = "")
# A.2 splines info
row = box1Curve.row(align = True)
row.operator("curvetools2.operatorsplinesinfo", text = "Curve splines info")
# A.3 segments info
row = box1Curve.row(align = True)
row.operator("curvetools2.operatorsegmentsinfo", text = "Curve segments info")
# A.4 origin to spline0start
row = box1Curve.row(align = True)
row.operator("curvetools2.operatororigintospline0start", text = "Set origin to spline start")
# B. 2 curves
self.layout.label(text = "2 curves:")
box2Curves = self.layout.box()
# B.1 curve intersections
boxIntersect = box2Curves.box()
row = boxIntersect.row(align = True)
row.operator("curvetools2.operatorintersectcurves", text = "Intersect curves")
row = boxIntersect.row(align = True)
row.prop(context.scene.curvetools, "LimitDistance", text = "LimitDistance")
#row.active = (context.scene.curvetools.IntersectCurvesAlgorithm == '3D')
row = boxIntersect.row(align = True)
row.prop(context.scene.curvetools, "IntersectCurvesAlgorithm", text = "Algorithm")
row = boxIntersect.row(align = True)
row.prop(context.scene.curvetools, "IntersectCurvesMode", text = "Mode")
row = boxIntersect.row(align = True)
row.prop(context.scene.curvetools, "IntersectCurvesAffect", text = "Affect")
# B.2 surface generation
boxSurface = box2Curves.box()
row = boxSurface.row(align = True)
row.operator("curvetools2.operatorloftcurves", text = "Loft curves")
row = boxSurface.row(align = True)
row.operator("curvetools2.operatorsweepcurves", text = "Sweep curves")
# C. 3 curves
self.layout.label(text = "3 curves:")
box3Curves = self.layout.box()
row = box3Curves.row(align = True)
row.operator("curvetools2.operatorbirail", text = "Birail")
# D. 1 or more curves
self.layout.label(text = "1 or more curves:")
box1OrMoreCurves = self.layout.box()
# D.1 set spline resolution
boxSplineRes = box1OrMoreCurves.box()
row = boxSplineRes.row(align = True)
row.operator("curvetools2.operatorsplinessetresolution", text = "Set resolution")
row.prop(context.scene.curvetools, "SplineResolution", text = "")
# D.2 remove splines
boxSplineRemove = box1OrMoreCurves.box()
row = boxSplineRemove.row(align = True)
row.operator("curvetools2.operatorsplinesremovezerosegment", text = "Remove 0-segments splines")
row = boxSplineRemove.row(align = True)
row.operator("curvetools2.operatorsplinesremoveshort", text = "Remove short splines")
row = boxSplineRemove.row(align = True)
row.prop(context.scene.curvetools, "SplineRemoveLength", text = "Threshold remove")
# D.3 join splines
boxSplineJoin = box1OrMoreCurves.box()
row = boxSplineJoin.row(align = True)
row.operator("curvetools2.operatorsplinesjoinneighbouring", text = "Join neighbouring splines")
row = boxSplineJoin.row(align = True)
row.prop(context.scene.curvetools, "SplineJoinDistance", text = "Threshold join")
row = boxSplineJoin.row(align = True)
row.prop(context.scene.curvetools, "SplineJoinStartEnd", text = "Only at start & end")
row = boxSplineJoin.row(align = True)
row.prop(context.scene.curvetools, "SplineJoinMode", text = "Join mode")
import time
import bpy
from bpy.props import *
class CurveTools2SelectedObjectHeader(bpy.types.Header):
bl_label = "Selection"
bl_space_type = "VIEW_3D"
def __init__(self):
self.update()
def update(self):
blenderSelectedObjects = bpy.context.selected_objects
selectedObjects = bpy.context.scene.curvetools.SelectedObjects
selectedObjectsToRemove = []
for selectedObject in selectedObjects:
if not selectedObject.IsElementOf(blenderSelectedObjects): selectedObjectsToRemove.append(selectedObject)
for selectedObject in selectedObjectsToRemove: selectedObjects.remove(selectedObject)
blenderObjectsToAdd = []
for blenderObject in blenderSelectedObjects:
if not CurveTools2SelectedObject.ListContains(selectedObjects, blenderObject): blenderObjectsToAdd.append(blenderObject)
for blenderObject in blenderObjectsToAdd:
newSelectedObject = CurveTools2SelectedObject(blenderObject)
selectedObjects.append(newSelectedObject)
def draw(self, context):
selectedObjects = bpy.context.scene.curvetools.SelectedObjects
nrSelectedObjects = len(selectedObjects)
layout = self.layout
row = layout.row()
row.label("Sel:", nrSelectedObjects)
class CurveTools2SelectedObject(bpy.types.PropertyGroup):
name = StringProperty(name = "name", default = "??")
@staticmethod
def UpdateThreadTarget(lock, sleepTime, selectedObjectNames, selectedBlenderObjectNames):
time.sleep(sleepTime)
newSelectedObjectNames = []
for name in selectedObjectNames:
if name in selectedBlenderObjectNames: newSelectedObjectNames.append(name)
for name in selectedBlenderObjectNames:
if not (name in selectedObjectNames): newSelectedObjectNames.append(name)
# sometimes it still complains about the context
try:
nrNewSelectedObjects = len(newSelectedObjectNames)
bpy.context.scene.curvetools.NrSelectedObjects = nrNewSelectedObjects
selectedObjects = bpy.context.scene.curvetools.SelectedObjects
selectedObjects.clear()
for i in range(nrNewSelectedObjects): selectedObjects.add()
for i, newSelectedObjectName in enumerate(newSelectedObjectNames):
selectedObjects[i].name = newSelectedObjectName
except: pass
@staticmethod
def GetSelectedObjectNames():
selectedObjects = bpy.context.scene.curvetools.SelectedObjects
rvNames = []
selectedObjectValues = selectedObjects.values()
for selectedObject in selectedObjectValues: rvNames.append(selectedObject.name)
return rvNames
@staticmethod
def GetSelectedBlenderObjectNames():
blenderSelectedObjects = bpy.context.selected_objects
rvNames = []
for blObject in blenderSelectedObjects: rvNames.append(blObject.name)
return rvNames
This diff is collapsed.
import bpy
from mathutils import *
def GetSelectedCurves():
rvList = []
for obj in bpy.context.selected_objects:
if obj.type == "CURVE": rvList.append(obj)
return rvList
def Selected1Curve():
if len(GetSelectedCurves()) == 1:
return (bpy.context.active_object.type == "CURVE")
return False
def Selected1SingleSplineCurve():
if Selected1Curve():
return (len(bpy.context.active_object.data.splines) == 1)
return False
def Selected2Curves():
if len(GetSelectedCurves()) == 2:
return (bpy.context.active_object.type == "CURVE")
return False
def Selected3Curves():
if len(GetSelectedCurves()) == 3:
return (bpy.context.active_object.type == "CURVE")
return False
def Selected1OrMoreCurves():
if len(GetSelectedCurves()) > 0:
return (bpy.context.active_object.type == "CURVE")
return False
def GetToolsRegion():
for area in bpy.context.screen.areas:
if area.type == 'VIEW_3D':
for region in area.regions:
if region.type == 'TOOLS': return region
return None
def GetFirstRegionView3D():
for area in bpy.context.screen.areas:
if area.type == 'VIEW_3D':
return area.spaces[0].region_3d
return None
def LogFirstRegionView3D():
print("LogFirstRegionView3D()")
regionView3D = GetFirstRegionView3D()
if regionView3D is None:
print("--", "ERROR:", "regionView3D is None")
return
print("--", "view_matrix:")
print("--", "--", regionView3D.view_matrix)
print("--", "view_location:")
print("--", "--", regionView3D.view_location)
class Intersection:
# listIP: list of BezierSplineIntersectionPoint
# return: list of splines
@staticmethod
def GetBezierSplines(listIP):
rvList = []
for ip in listIP:
if not (ip.spline in rvList): rvList.append(ip.spline)
return rvList
# listIP: list of BezierSplineIntersectionPoint
# return: list of segments
@staticmethod
def GetBezierSegments(listIP, spline):
rvList = []
for ip in listIP:
if not ip.spline is spline: continue
segIP = ip.bezierSegmentIntersectionPoint
if not (segIP.segment in rvList): rvList.append(segIP.segment)
return rvList
# listIP: list of BezierSplineIntersectionPoint
# return: list of floats (not necessarily ordered)
@staticmethod
def GetBezierSegmentParameters(listIP, segment):
rvList = []
for ip in listIP:
segIP = ip.bezierSegmentIntersectionPoint
if not segIP.segment is segment: continue
rvList.append(segIP.parameter)
return rvList
\ No newline at end of file
bl_info = {
"name": "Curve Tools 2",
"description": "Adds some functionality for bezier/nurbs curve/surface modeling",
"author": "Mackraken, guy lateur",
"version": (0, 2, 0),
"blender": (2, 71, 0),
"location": "View3D > Tool Shelf > Addons Tab",
"warning": "WIP",
"wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"
"Scripts/Curve/Curve_Tools",
"tracker_url": "https://developer.blender.org/T27720",
"category": "Add Curve"}
import bpy
from bpy.props import *
from . import Properties
from . import Operators
from . import Panel
def UpdateDummy(object, context):
pass
class CurveTools2Settings(bpy.types.PropertyGroup):
# selection
SelectedObjects = CollectionProperty(type = Properties.CurveTools2SelectedObject)
NrSelectedObjects = IntProperty(name = "NrSelectedObjects", default = 0, description = "Number of selected objects", update = UpdateDummy)
# NrSelectedObjects = IntProperty(name = "NrSelectedObjects", default = 0, description = "Number of selected objects")
# curve
CurveLength = FloatProperty(name = "CurveLength", default = 0.0, precision = 6)
#splines
SplineResolution = IntProperty(name = "SplineResolution", default = 64, min = 2, max = 1024, soft_min = 2, description = "Spline resolution will be set to this value")
SplineRemoveLength = FloatProperty(name = "SplineRemoveLength", default = 0.001, precision = 6, description = "Splines shorter than this threshold length will be removed")
SplineJoinDistance = FloatProperty(name = "SplineJoinDistance", default = 0.001, precision = 6, description = "Splines with starting/ending points closer to each other than this threshold distance will be joined")
SplineJoinStartEnd = BoolProperty(name = "SplineJoinStartEnd", default = False, description = "Only join splines at the starting point of one and the ending point of the other")
splineJoinModeItems = (('At midpoint', 'At midpoint', 'Join splines at midpoint of neighbouring points'), ('Insert segment', 'Insert segment', 'Insert segment between neighbouring points'))
SplineJoinMode = EnumProperty(items = splineJoinModeItems, name = "SplineJoinMode", default = 'At midpoint', description = "Determines how the splines will be joined")
# curve intersection
LimitDistance = FloatProperty(name = "LimitDistance", default = 0.0001, precision = 6, description = "Displays the result of the curve length calculation")
intAlgorithmItems = (('3D', '3D', 'Detect where curves intersect in 3D'), ('From View', 'From View', 'Detect where curves intersect in the RegionView3D'))
IntersectCurvesAlgorithm = EnumProperty(items = intAlgorithmItems, name = "IntersectCurvesAlgorithm", description = "Determines how the intersection points will be detected", default = '3D')
intModeItems = (('Insert', 'Insert', 'Insert points into the existing spline(s)'), ('Split', 'Split', 'Split the existing spline(s) into 2'), ('Empty', 'Empty', 'Add empty at intersections'))
IntersectCurvesMode = EnumProperty(items = intModeItems, name = "IntersectCurvesMode", description = "Determines what happens at the intersection points", default = 'Split')
intAffectItems = (('Both', 'Both', 'Insert points into both curves'), ('Active', 'Active', 'Insert points into active curve only'), ('Other', 'Other', 'Insert points into other curve only'))
IntersectCurvesAffect = EnumProperty(items = intAffectItems, name = "IntersectCurvesAffect", description = "Determines which of the selected curves will be affected by the operation", default = 'Both')
def register():
bpy.utils.register_class(Properties.CurveTools2SelectedObject)
bpy.utils.register_class(CurveTools2Settings)
bpy.types.Scene.curvetools = bpy.props.PointerProperty(type=CurveTools2Settings)
bpy.utils.register_class(Operators.OperatorSelectionInfo)
#bpy.utils.register_class(Properties.CurveTools2SelectedObjectHeader)
bpy.utils.register_class(Operators.OperatorCurveInfo)
bpy.utils.register_class(Operators.OperatorCurveLength)
bpy.utils.register_class(Operators.OperatorSplinesInfo)
bpy.utils.register_class(Operators.OperatorSegmentsInfo)
bpy.utils.register_class(Operators.OperatorOriginToSpline0Start)
bpy.utils.register_class(Operators.OperatorIntersectCurves)
bpy.utils.register_class(Operators.OperatorLoftCurves)
bpy.utils.register_class(Operators.OperatorSweepCurves)
bpy.utils.register_class(Operators.OperatorBirail)
bpy.utils.register_class(Operators.OperatorSplinesSetResolution)
bpy.utils.register_class(Operators.OperatorSplinesRemoveZeroSegment)
bpy.utils.register_class(Operators.OperatorSplinesRemoveShort)
bpy.utils.register_class(Operators.OperatorSplinesJoinNeighbouring)
# bpy.app.handlers.scene_update_pre.append(SceneUpdatePreHandler)
bpy.utils.register_class(Panel.Panel)
def unregister():
bpy.utils.unregister_class(Panel.Panel)
# bpy.app.handlers.scene_update_pre.remove(SceneUpdatePreHandler)
bpy.utils.unregister_class(Operators.OperatorSplinesJoinNeighbouring)
bpy.utils.unregister_class(Operators.OperatorSplinesRemoveShort)
bpy.utils.unregister_class(Operators.OperatorSplinesRemoveZeroSegment)
bpy.utils.unregister_class(Operators.OperatorSplinesSetResolution)
bpy.utils.unregister_class(Operators.OperatorBirail)
bpy.utils.unregister_class(Operators.OperatorSweepCurves)
bpy.utils.unregister_class(Operators.OperatorLoftCurves)
bpy.utils.unregister_class(Operators.OperatorIntersectCurves)
bpy.utils.unregister_class(Operators.OperatorOriginToSpline0Start)
bpy.utils.unregister_class(Operators.OperatorSegmentsInfo)
bpy.utils.unregister_class(Operators.OperatorSplinesInfo)
bpy.utils.unregister_class(Operators.OperatorCurveLength)
bpy.utils.unregister_class(Operators.OperatorCurveInfo)
#bpy.utils.unregister_class(Operators.CurveTools2SelectedObjectHeader)
bpy.utils.unregister_class(Operators.OperatorSelectionInfo)
bpy.utils.unregister_class(CurveTools2Settings)
bpy.utils.unregister_class(Properties.CurveTools2SelectedObject)
if __name__ == "__main__":
register()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment