### 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 if parP < 0.0: return None if parP > 1.0: return None parQ = rvList 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.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!