--Bridge between two selected edges with mesh-geometry using a bezier curve algorithm. --by philip drake (R* Leeds) 2020 struct sRsSmoothBridge ( debugMode = false, --Sorts the edgeVerts by position fn SortEdgeVertsByPosition ObjOp obj edgeVerts = ( local verts = for vertId in edgeVerts collect objOp.getvert obj vertId if (distance verts[1] [-9999,-9999,-9999]) > (distance verts[2] [-9999,-9999,-9999]) do ( local temp = edgeVerts[1] edgeVerts[1] = edgeVerts[2] edgeVerts[2] = temp ) return #(edgeVerts[2],edgeVerts[1]) ), --Get the edge verts from the edgeId fn GetEdgeVerts ObjOp obj edgeId = ( if (ObjOp == MeshOp) then ( local edgeVerts = (MeshOp.GetVertsUsingEdge obj edgeId) as array edgeVerts = SortEdgeVertsByPosition ObjOp obj edgeVerts --sort edgeVerts return #(edgeVerts[2],edgeVerts[1]) ) else ( return (Polyop.GetEdgeVerts obj edgeId) ) ), --Function to weld verts using two verts and a position fn WeldVerts obj vertA vertB pos = ( local objOp = RsMeshPolyOp obj if (objOp == PolyOp) do ( polyOp.weldVerts obj vertA vertB pos ) if (objOp == MeshOp) do ( local verts = #(vertA,vertB) as bitarray meshop.weldVertSet obj verts weldpoint:pos ) ), --Get the face normal fn GetFaceNormalMeshPoly obj faceId = ( local objOp = RsMeshPolyOp obj if objOp == PolyOp then ( return (polyOp.getFaceNormal obj faceId) ) else ( return (getFaceNormal obj faceId) ) ), --Creates a mesh object fn CreateMesh vert_array face_array flipNormals:false = ( local mm = mesh numverts:vert_array.count name:"SmoothBridge" for v = 1 to vert_array.count do ( meshop.setvert mm v vert_array[v] ) for f = 1 to face_array.count do ( meshop.createPolygon mm face_array[f] ) update mm meshop.weldVertsByThreshold mm mm.mesh.verts 0.01 if flipNormals do addmodifier mm (Normalmodifier flip:true) smoothModif = smooth () smoothModif.autosmooth = on addmodifier mm smoothModif ui:on collapseStack mm return mm ), --Fuction that returns the points of a bezier spline (calculated from 4 position points in general) --de Casteljau's algorithm for any number of weights & point array sizes -- pts should normaly contain 4 2D or 3D points, (1 starting point, 2 mid points and 1 end point) --REF: https://gist.github.com/atomizer/1049745 fn BezierCurve t pts = ( if pts == undefined do return #() if pts.count < 2 do return #() --get the size of the dimensions, works on a #() array or point2 or point3 ... fn getArraySize arr = ( local arrSize = 0 if iskindof arr point2 then arrSize = 2 else if iskindof arr point3 then arrSize = 3 else arrSize = arr.count return arrSize ) --return point value fn setToTypePoint points arraySize= ( local pointFormat if arraySize == 2 do return (point2 points[1] points[2]) if arraySize == 3 do return (point3 points[1] points[2] points[3]) ) --bezier calculation local a = pts local ptsArrSize = getArraySize a[1] while a.count > 1 and not keyboard.escpressed do ( local b = #() for i = 1 to a.count - 1 do ( append b #() for j =1 to ptsArrSize do ( b[i][j] = a[i][j] * (1 - t) + a[i+1][j] * t -- interpolation ) ) a=b ) local result = a[1] if (iskindof result array) do ( local size = getArraySize result result = setToTypePoint a[1] size ) return result ), --Calculates the bezier mesh from two edge lists, returns a list of faces and verts fn CalculateBezierMesh pointsA pointsB num_edges vert_array: face_array: = ( if vert_array == unsupplied or face_array == unsupplied do ( vert_array = #() face_array = #() ) local vert_count = vert_array.count local startPosA = pointsA[1] local startPosB =pointsB[1] for i = 1 to num_edges do ( local t = ((i-1) as float) / (num_edges - 1) local ptA = BezierCurve t #(pointsA[1], pointsA[2], pointsA[3], pointsA[4]) local ptB = BezierCurve t #(pointsB[1], pointsB[2], pointsB[3], pointsB[4]) local v1 = startPosA local v2 = startPosB local v3 = ptA local v4 = ptB startPosA = v4 startPosB = v3 append vert_array v1 append vert_array v2 append vert_array v3 append vert_array v4 append face_array #(vert_count+1,vert_count+2,vert_count+3,vert_count+4) vert_count += 4 ) return (DataPair vert_array:vert_array face_array:face_array) ), --Get the average face normal from the verts fn GetAverageFaceNormalFromVert obj vert = ( local objOp = RsMeshPolyOp obj local faces = objOp.getFacesUsingVert obj vert local vertFaces = objOp.getVertsUsingFace obj faces local tNormal = [0,0,0] for i in faces do tNormal += GetFaceNormalMeshPoly obj i local averageNormal = (tNormal / vertFaces.numberSet) return (normalize averageNormal) ), --Get the face center fn GetFaceCenterFromVert obj vert = ( local objOp = RsMeshPolyOp obj local faces = (objOp.getFacesUsingVert obj vert) as array local tCenter = [0,0,0] for i in faces do tCenter += objOp.getFaceCenter obj i local averageCenter = (tCenter / faces.count) return averageCenter ), --Calculate the extrusion using the direction of two verts fn CalculateExtrusion obj vertIdA vertIdB unitScale = ( local objOp = RsMeshPolyOp obj --Get the average normal of the faces connected to our vertpoint local vertA = objOp.getvert obj vertIdA local vertB = objOp.getvert obj vertIdB local normal = GetAverageFaceNormalFromVert obj vertIdA local faceCenter = GetFaceCenterFromVert obj vertIdA local weightA = (normal * 1) + vertA local weightB = (normal * 1) + vertB --Calculate the extrusion by getting the perpendicular normal of the newly calculated rectangle faces local side1 = weightA - vertA local side2 = vertA - vertB local perpNormal = normalize(Cross side1 side2) --GetNormalVector --Calculate and check the extrusion derection local extrusionForwards = ((normalize perpNormal) * unitScale) + vertA local extrusionBackwards =((normalize perpNormal) * -unitScale) + vertA if (distance extrusionForwards faceCenter) >= (distance extrusionBackwards faceCenter) then ( local perpPoint1 = extrusionForwards ) else ( local perpPoint1 = extrusionBackwards ) return perpPoint1 ), --Calculate the extrusion using the average direction from three verts fn CalculateExtrusionAverage obj vertIdA vertIdB vertIdC unitScale = ( local objOp = RsMeshPolyOp obj --Get the average normal of the faces connected to our vertpoint local vertA = objOp.getvert obj vertIdA local vertB = objOp.getvert obj vertIdB local vertC = objOp.getvert obj vertIdC local normal = GetAverageFaceNormalFromVert obj vertIdB local faceCenter = GetFaceCenterFromVert obj vertIdB local weightA = (normal * 1) + vertA local weightB = (normal * 1) + vertB local weightC = (normal * 1) + vertC --Calculate the extrusion by getting the perpendicular normal of the newly calculated rectangle faces local side1 = weightB - vertB local side2 = vertB - vertA local perpNormalA = normalize(Cross side1 side2) --GetNormalVector local side3 = vertB - vertC local perpNormalB = normalize(Cross side3 side1) --GetNormalVector local perpNormal = (perpNormalA + perpNormalB) / 2 --Calculate and check the extrusion derection local extrusionForwards = ((normalize perpNormal) * unitScale) + vertB local extrusionBackwards =((normalize perpNormal) * -unitScale) + vertB if (distance extrusionForwards faceCenter) >= (distance extrusionBackwards faceCenter) then ( local perpPoint1 = extrusionForwards ) else ( local perpPoint1 = extrusionBackwards ) return perpPoint1 ), --Calculate the extrusion by mirroring the shape of the neighbouring verts fn GetEdgeExtrusion obj edgeVerts transformVert unitScale = ( local objOp = RsMeshPolyOp obj local vector1 = (polyop.getVert obj edgeVerts[1]) - (polyop.getVert obj edgeVerts[2]) local angles = #() local edges = #() local connectedEdges = objOp.getEdgesUsingVert obj transformVert for edge in (connectedEdges as array) do ( --local neighbouringEdgeVerts = (polyop.getVertsUsingEdge obj edge) as array local neighbouringEdgeVerts = this.GetEdgeVerts ObjOp obj edge if debugMode do ( Point wirecolor:(color 135 6 6) pos:(objOp.getvert obj neighbouringEdgeVerts[1]) size:2 isSelected:off Point wirecolor:(color 135 6 6) pos:(objOp.getvert obj neighbouringEdgeVerts[2]) size:2 isSelected:off ) --calculate the vector defined by the two vertices in the first edge local vector2 = (polyop.getVert obj neighbouringEdgeVerts[1]) - (polyop.getVert obj neighbouringEdgeVerts[2]) --calculate the angle between the two normalized vectors append angles (acos (dot (normalize vector1) (normalize vector2))) append edges neighbouringEdgeVerts ) --find the closest angle to 90 deg local closestAngle = 360 local perpendicularEdgeVerts = edgeVerts for i = 1 to angles.count do ( if (abs(90 - closestAngle) > abs(90 - angles[i])) do ( closestAngle = angles[i] perpendicularEdgeVerts = edges[i] ) ) perpendicularEdgeVert = (for vert in perpendicularEdgeVerts where (finditem edgeVerts vert) == 0 collect vert)[1] --Calculate the extrusion local extrusionForwards = objOp.getvert obj transformVert - ((normalize (objOp.getvert obj perpendicularEdgeVert - objOp.getvert obj transformVert)) * unitScale) local extrusionBackwards = objOp.getvert obj transformVert - ((normalize (objOp.getvert obj perpendicularEdgeVert - objOp.getvert obj transformVert)) * -unitScale) local edgeNormalExtrusion = CalculateExtrusion obj edgeVerts[1] edgeVerts[2] unitScale --fix for flipped direction if (distance extrusionForwards edgeNormalExtrusion) < (distance extrusionBackwards edgeNormalExtrusion) then ( local extrusion = extrusionForwards ) else ( local extrusion = extrusionBackwards ) return extrusion ), --Get the face normal from an edge fn GetNormalMeshPoly obj edge = ( local objOp = RsMeshPolyOp obj if objOp == PolyOp then ( local sel = selection[1] local vertFaces = polyOp.getFacesUsingEdge obj edge local tNormal = [0,0,0] for i in vertFaces do tNormal += polyOp.getFaceNormal obj i return (tNormal / vertFaces.numberSet) ) else ( return (getNormal obj edge) ) ), --Used for debug to view lines between points fn drawLineBetweenTwoPoints pointA pointB lineName:"perpLine" wirecolor:(color 8 8 136) = ( ss = SplineShape pos:pointA name:lineName addNewSpline ss addKnot ss 1 #corner #line PointA addKnot ss 1 #corner #line PointB updateShape ss ss.wirecolor = wirecolor return ss ), --Get the sorted edge verts from two edge selections fn GetSortedEdgesVertsFromClosedLoops obj precision:0.001 = ( local objOp = RsMeshPolyOp obj local edges = obj.selectedEdges edges = for edge in edges collect edge.index edges = sort edges edges = edges as bitarray local vertsGroups = #() local edgesGroups = #() while not edges.isempty and (edges != undefined) and not keyboard.escPressed do ( local vGroup = #() local eGroup = #() local currentEdge = (edges as array)[1] local currentVert = ((objOp.getvertsusingedge obj currentEdge) as array)[1] --currentVert = (GetEdgeVerts ObjOp obj currentEdge)[2] --Here we make sure to pick the fist vert in order of the position (for closed loops) local firstVertInList = (this.GetEdgeVerts ObjOp obj currentEdge)[2] while currentEdge != undefined do ( edges[currentEdge] = false append vGroup currentVert append eGroup currentEdge nextVert = objOp.getvertsusingedge obj currentEdge --nextVert = GetEdgeVerts ObjOp obj currentEdge nextVert[currentVert] = false currentVert = (nextVert as array)[1] currentEdge = (((objOp.getedgesusingvert obj currentVert)*edges) as array)[1] ) local startVert = objOp.getvert obj firstVertInList local endVert = objOp.getvert obj currentVert if (currentVert != firstVertInList) or ((distance startVert endVert) < precision) do append vGroup currentVert if (distance startVert [-9999,-9999,-9999]) > (distance endVert [-9999,-9999,-9999]) do ( vGroup = for i = vGroup.count to 1 by -1 collect vGroup[i] eGroup = for i = eGroup.count to 1 by -1 collect eGroup[i] if debugMode do print ("GetSortedEdges invered verts: " + vGroup as string) ) append vertsGroups vGroup append edgesGroups eGroup ) --if there are more than two groups check if they can be joined. if (vertsGroups.count > 2) do ( local validateVertsGroups = #() local validateEdgesGroups = #() for i = 1 to (vertsGroups.count - 1) where vertsGroups[i].count > 0 do ( for j = 2 to vertsGroups.count where vertsGroups[j].count > 0 do ( local lastVGroup = vertsGroups[i] local lastEGroup = edgesGroups[i] local vGroup = vertsGroups[j] local eGroup = edgesGroups[j] --if the last vert was the same as the previous end vert attach the groups as they are linked if lastVGroup[1] == vGroup[vGroup.count] then ( vertsGroups[i] = (makeuniquearray (join vGroup lastVGroup)) edgesGroups[i] = (makeuniquearray(join eGroup lastEGroup)) vertsGroups[j] = #() edgesGroups[j] = #() ) if lastVGroup[lastVGroup.count] == vGroup[1] then ( vertsGroups[i] = (makeuniquearray (join lastVGroup vGroup)) edgesGroups[i] = (makeuniquearray(join lastEGroup eGroup)) vertsGroups[j] = #() edgesGroups[j] = #() ) ) ) ) vertsGroups = for vgroup in vertsGroups where vgroup.count > 0 and vgroup != undefined collect vgroup edgesGroups = for egroup in edgesGroups where egroup.count > 0 and egroup != undefined collect egroup return #(edgesGroups, vertsGroups) ), --for closed edge loop fn isClosedEdgeLoop obj vertIds precision:0.001 = ( local objOp = RsMeshPolyOp obj local startVert = objOp.getvert obj vertIds[1] local endVert = objOp.getvert obj vertIds[vertIds.count] return ((distance startVert endVert) < precision) ), --Get the total distance from an array of verts fn GetTotalDistance obj verts = ( local objOp = RsMeshPolyOp obj --get edgeSizes local edgeSizes = for i = 2 to verts.count collect distance (objOp.getvert obj verts[i-1]) (objOp.getvert obj verts[i]) --get totalDistance local totalDistance = 0 for size in edgeSizes do totalDistance += size return totalDistance ), --Get the distance percentage between the start vert to a verts in the edge selection fn GetUnitDistances obj verts = ( local objOp = RsMeshPolyOp obj --get edgeSizes local edgeSizes = for i = 2 to verts.count collect distance (objOp.getvert obj verts[i-1]) (objOp.getvert obj verts[i]) --get totalDistance local totalDistance = 0 for size in edgeSizes do totalDistance += size --get unit Distances for each edge local unitDistances = #() local distanceCount = 0.0 for i = 1 to edgeSizes.count do ( local percent = (distanceCount / totalDistance) * 1.0 distanceCount += edgeSizes[i] append unitDistances percent ) append unitDistances 1.0 return unitDistances ), --Divide in vert direction fn DivideEdge objOp obj edgeId divideLocation = ( local verts = this.GetEdgeVerts objOp obj edgeId local newVert if (objOp == PolyOp) then ( newVert = (objOp.divideEdge obj edgeId divideLocation) -- objOp.setVert obj newVert newVertPos -- polyop.retriangulate obj local faceId = ((objOp.getFacesUsingVert obj newVert) as array)[1] local faceVerts = objOp.getVertsUsingFace obj faceId faceVerts = faceVerts - (#(newVert,verts[1],verts[2]) as bitarray) for vert in (faceVerts as array) do obj.createEdge newVert vert update obj ) else ( local currentVerts = getVertSelection obj objOp.divideEdge obj edgeId divideLocation visDiag1:true visDiag2:true fixNeighbors:true newVert = ((getVertSelection obj) - currentVerts)[1] -- objOp.setVert obj newVerts newVertPos update obj ) return newVert ), --Match the number of edges between two border edge selections fn MatchEdges obj groupId1 groupId2 precision:0.001 useMinimumEdgesToMatch:true = ( local objOp = RsMeshPolyOp obj local groups = GetSortedEdgesVertsFromClosedLoops obj precision:precision local vertGroups = groups[2] local edgeGroups = groups[1] local vertsA = vertGroups[groupId1] local vertsB = vertGroups[groupId2] local edgesA = edgeGroups[groupId1] local unitDistancesB = GetUnitDistances obj vertsB local totalDistance = GetTotalDistance obj vertsA for dist in unitDistancesB while unitDistancesB != totalDistance and vertsA.count != vertsB.count do ( local percent = 1 for i = vertsA.count to 2 by -1 do ( local posA = (objOp.getvert obj vertsA[i-1]) local posB = (objOp.getvert obj vertsA[i]) local thisEdgeSize = distance posA posB local thisEdgeUnitSize = (thisEdgeSize / totalDistance) * 1.0 if (dist > (percent - thisEdgeUnitSize + precision)) and (dist < (percent - precision)) and (thisEdgeUnitSize > precision) do ( if debugMode do ( print ("MatchEdges => dist: " + dist as string + " , percentA: " + (percent - thisEdgeUnitSize) as string + " , percentB: "+ percent as string + " , Edge: " + edgesA[i-1] as string) print ("MatchEdges => edgesA[i-1] " + (this.GetEdgeVerts ObjOp obj edgesA[i-1]) as string + " , vertsA[i-1] " + vertsA[i-1] as string + " , vertsA[i]" + vertsA[i] as string) ) local percentAlongEdge = ((thisEdgeUnitSize - (percent - dist))/thisEdgeUnitSize) local newVertPos = posA + (((posB - posA) / 1) * percentAlongEdge) local divideLocation if groupId1 == 1 then ( divideLocation = (percent - dist)/thisEdgeUnitSize ) else ( divideLocation = ((thisEdgeUnitSize - (percent - dist))/thisEdgeUnitSize) ) local newVert = this.DivideEdge objOp obj edgesA[i-1] divideLocation thisEdgeSize = distance newVertPos posB thisEdgeUnitSize = (thisEdgeSize / totalDistance) * 1.0 --weld any overlapping edges if (distance newVertPos posA <= precision) do this.WeldVerts obj vertsA[i-1] vertsA[i] posA if (distance newVertPos posB <= precision) do this.WeldVerts obj vertsA[i] vertsA[i-1] posB --End the looping of verts i=2 if useMinimumEdgesToMatch do ( --Sort the edges and verts order groups = GetSortedEdgesVertsFromClosedLoops obj precision:precision vertGroups = groups[2] edgeGroups = groups[1] vertsA = vertGroups[groupId1] edgesA = edgeGroups[groupId1] ) ) --Weld overlapping verts if (thisEdgeUnitSize <= precision) or (thisEdgeSize <= 0.001) then ( if (vertsA.count < 5) do return() if debugMode do print ("Weld overlapping verts => vertsA[i-1] : "+ vertsA[i-1] as string +" ,vertsA[i] : "+ vertsA[i] as string + " ,posA :" + posA as string + " ,posB :" + posB as string) if (i > 2) and (i != vertsA.count) then this.WeldVerts obj vertsA[i-1] vertsA[i] posA else this.WeldVerts obj vertsA[i] vertsA[i-1] posB update obj --Sort the edges and verts order groups = GetSortedEdgesVertsFromClosedLoops obj precision:precision vertGroups = groups[2] edgeGroups = groups[1] vertsA = vertGroups[groupId1] edgesA = edgeGroups[groupId1] vertsB = vertGroups[groupId2] ) percent -= thisEdgeUnitSize ) ) ), --Bridge the edges using a bezier spline fn BridgeEdges objSel ExtrusionUnitScale:1 Segments:10 relaxMidPointsValue:1.0 showAllEdges:true useMinimumEdgesToMatch:true edgeMatchPrecision:0.001 conformToStartEndEdges:true flipNormals:true modifyEdgeSelection:true = ( if(classof objSel != Editable_poly) and (classof objSel != Editable_mesh) do return false if objSel.selectedEdges.count < 2 do return false undo "Smooth Bridge" on ( --Copy the edge faces or edit the selected object directly if modifyEdgeSelection then ( local obj = objSel local objOp = RsMeshPolyOp obj ) else ( local obj = copy objSel local objOp = RsMeshPolyOp obj local selectedEdges = obj.selectedEdges local facesOnEdgeSelection = objOp.getFacesUsingEdge obj selectedEdges local allFaces = obj.faces as bitarray local otherFaces = allFaces - facesOnEdgeSelection objOp.DeleteFaces obj otherFaces ) --Sort the edges and verts order local groups = (GetSortedEdgesVertsFromClosedLoops obj precision:0.001) local vertGroups = groups[2] local edgeGroups = groups[1] --Auto set the precision local verts = #() for vertGroup in vertGroups do for vert in vertGroup do append verts vert local edgeUnitSizes = #() for i = 2 to verts.count do ( local dist = (distance (objOp.getvert obj verts[i-1])(objOp.getvert obj verts[i])) if dist > 0 do append edgeUnitSizes dist ) edgeUnitSizes = sort edgeUnitSizes edgeUnitSizes = for size in edgeUnitSizes where size > 0.001 collect size -- skip overlapping points local edgeMinUnitSize = edgeUnitSizes[1] local totalEdgesDistance = 0 for size in edgeUnitSizes do totalEdgesDistance += size local averageEdgeSize = totalEdgesDistance / edgeUnitSizes.count if (edgeMatchPrecision > averageEdgeSize) do ( messageBox ("The precision value is too heigh try " + edgeMinUnitSize as string) title:"Invalid Edge Match Precision" return false ) --Set the edge match precision local precision = edgeMatchPrecision if debugMode do print ("edgeMinUnitSize precision: " + precision as string) --Sort the edges and verts order groups = (GetSortedEdgesVertsFromClosedLoops obj precision:precision) vertGroups = groups[2] local edgeGroups = groups[1] --Validation of edge selection, (must have two edges selected) if vertGroups.count < 2 do ( messageBox "Please select two seperate edges on a poly or mesh object." title:"Invalid selection" return false ) --match edges by inserting points while vertGroups[1].count != vertGroups[2].count and not keyboard.escPressed do ( if vertGroups[1].count > vertGroups[2].count then ( MatchEdges obj 2 1 precision:precision useMinimumEdgesToMatch:useMinimumEdgesToMatch MatchEdges obj 1 2 precision:precision useMinimumEdgesToMatch:useMinimumEdgesToMatch ) else ( MatchEdges obj 1 2 precision:precision useMinimumEdgesToMatch:useMinimumEdgesToMatch MatchEdges obj 2 1 precision:precision useMinimumEdgesToMatch:useMinimumEdgesToMatch ) --Sort the edges and verts order again as the number of verts in the mesh has changed groups = GetSortedEdgesVertsFromClosedLoops obj precision:precision vertGroups = groups[2] edgeGroups = groups[1] --If the number of verts on each side of the edge selection still does not match we use the minimum amount of verts to match them useMinimumEdgesToMatch = true ) -- Option to show all edges obj.allEdges = showAllEdges --Make sure we use the minimum count to match the index count on each of the two edge selections local total = vertGroups[1].count if vertGroups[1].count > vertGroups[2].count do total = vertGroups[2].count --Extrued edges local borderDistances = 0 for i = 1 to total do borderDistances += (distance (objOp.getvert obj vertGroups[1][i])(objOp.getvert obj vertGroups[2][i])) / 3 local averageBorderDistance = borderDistances / total local unitScale = averageBorderDistance * ExtrusionUnitScale local splineWeightPoints = #() local vert_array = #() local face_array = #() for i = 1 to (total) do ( vertA = objOp.getvert obj vertGroups[1][i] vertB = objOp.getvert obj vertGroups[2][i] normalA = GetAverageFaceNormalFromVert obj vertGroups[1][i] weightA = (normalA * 1) + vertA normalB = GetAverageFaceNormalFromVert obj vertGroups[2][i] weightB = (normalB * 1) + vertB if (i == 1) then ( weightA = CalculateExtrusion obj vertGroups[1][i] vertGroups[1][i+1] -unitScale weightB = CalculateExtrusion obj vertGroups[2][i] vertGroups[2][i+1] unitScale ) else if (i < total) then ( weightA = CalculateExtrusionAverage obj vertGroups[1][i-1] vertGroups[1][i] vertGroups[1][i+1] -unitScale weightB = CalculateExtrusionAverage obj vertGroups[2][i-1] vertGroups[2][i] vertGroups[2][i+1] unitScale ) else if (i == total) then ( --Set the end verts --If we have a closed edge loop selection we set the last point to match the first if isClosedEdgeLoop obj vertGroups[1] precision:precision then weightA = CalculateExtrusion obj vertGroups[1][1] vertGroups[1][2] -unitScale --match with 1st point for weightC else weightA = CalculateExtrusion obj vertGroups[1][i] vertGroups[1][i-1] unitScale if isClosedEdgeLoop obj vertGroups[2] precision:precision then weightB = CalculateExtrusion obj vertGroups[2][1] vertGroups[2][2] unitScale --match with 1st point for weightD else weightB = CalculateExtrusion obj vertGroups[2][i] vertGroups[2][i-1] -unitScale ) local pointsA = #(vertA,weightA,weightB,vertB) append splineWeightPoints pointsA ) --Option to conform the start and end extrusion of the edge selection if conformToStartEndEdges do ( borderA = vertGroups[1] borderB = vertGroups[2] local startPointA = objOp.getvert obj borderA[1] local startPointB = objOp.getvert obj borderB[1] --GetEdgeExtrusion obj edgeVerts transformVert unitScale local startWeightA = GetEdgeExtrusion obj #(borderA[1], borderA[2]) borderA[1] unitScale local startWeightB = GetEdgeExtrusion obj #(borderB[1], borderB[2]) borderB[1] unitScale splineWeightPoints[1] = #(startPointA,startWeightA,startWeightB,startPointB) local endPointA = objOp.getvert obj borderA[borderA.count] local endPointB = objOp.getvert obj borderB[borderB.count] local endWeightA = (GetEdgeExtrusion obj #(borderA[(borderA.count - 1)], borderA[borderA.count]) borderA[borderA.count] unitScale) local endWeightB = (GetEdgeExtrusion obj #(borderB[(borderB.count - 1)], borderB[borderB.count]) borderB[borderB.count] unitScale) splineWeightPoints[splineWeightPoints.count] = #(endPointA,endWeightA,endWeightB,endPointB) ) if debugMode do ( drawLineBetweenTwoPoints splineWeightPoints[1][1] splineWeightPoints[1][2] lineName:"perpLine" wirecolor:(color 136 8 0) drawLineBetweenTwoPoints splineWeightPoints[splineWeightPoints.count][1] splineWeightPoints[splineWeightPoints.count][2] lineName:"perpLine" wirecolor:(color 136 8 0) drawLineBetweenTwoPoints splineWeightPoints[1][3] splineWeightPoints[1][4] lineName:"perpLine" wirecolor:(color 136 8 0) drawLineBetweenTwoPoints splineWeightPoints[splineWeightPoints.count][3] splineWeightPoints[splineWeightPoints.count][4] lineName:"perpLine" wirecolor:(color 136 8 0) ) --Relax the mid points of the bridge for i = 2 to (splineWeightPoints.count - 1) do ( -- Relax points if they cross over the mid point if (distance splineWeightPoints[i-1][2] splineWeightPoints[i][2]) >= (distance splineWeightPoints[i-1][2] splineWeightPoints[i+1][2]) do ( for j = 2 to (splineWeightPoints.count - 1) do ( splineWeightPoints[j][2] = (splineWeightPoints[j-1][2] + splineWeightPoints[j][2] + splineWeightPoints[j+1][2]) / 3 ) ) if (distance splineWeightPoints[i-1][3] splineWeightPoints[i][3]) >= (distance splineWeightPoints[i-1][3] splineWeightPoints[i+1][3]) do ( for j = 2 to (splineWeightPoints.count - 1) do ( splineWeightPoints[j][3] = (splineWeightPoints[j-1][3] + splineWeightPoints[j][3] + splineWeightPoints[j+1][3]) / 3 ) ) --Relax to average point distance if relaxMidPointsValue != 0 do ( local leaneance = relaxMidPointsValue local totalDistance = 0 for j = 1 to (splineWeightPoints.count - 1) do totalDistance += (distance splineWeightPoints[j][2] splineWeightPoints[j+1][2]) local avrageEdgeSpacingA = (totalDistance / (splineWeightPoints.count - 1)) local thisEdgeSpacingA = (distance splineWeightPoints[i-1][2] splineWeightPoints[i][2]) if thisEdgeSpacingA > (avrageEdgeSpacingA / leaneance) or thisEdgeSpacingA < (avrageEdgeSpacingA * leaneance) do ( for j = 2 to (splineWeightPoints.count - 1) do ( splineWeightPoints[j][2] = (splineWeightPoints[j-1][2] + splineWeightPoints[j][2] + splineWeightPoints[j+1][2]) / 3 splineWeightPoints[j][3] = (splineWeightPoints[j-1][3] + splineWeightPoints[j][3] + splineWeightPoints[j+1][3]) / 3 ) ) for j = 1 to (splineWeightPoints.count - 1) do totalDistance += (distance splineWeightPoints[j][3] splineWeightPoints[j+1][3]) local avrageEdgeSpacingB = (totalDistance / (splineWeightPoints.count - 1)) local thisEdgeSpacingB = (distance splineWeightPoints[i-1][3] splineWeightPoints[i][3]) if thisEdgeSpacingB > (avrageEdgeSpacingB / leaneance) or thisEdgeSpacingB < (avrageEdgeSpacingB * leaneance) do ( for j = 2 to (splineWeightPoints.count - 1) do ( splineWeightPoints[j][2] = (splineWeightPoints[j-1][2] + splineWeightPoints[j][2] + splineWeightPoints[j+1][2]) / 3 splineWeightPoints[j][3] = (splineWeightPoints[j-1][3] + splineWeightPoints[j][3] + splineWeightPoints[j+1][3]) / 3 ) ) ) ) --Calculate the Spline for i = 1 to (splineWeightPoints.count-1) do ( CalculateBezierMesh splineWeightPoints[i] splineWeightPoints[i+1] (Segments + 2) vert_array:vert_array face_array:face_array ) --Create the bridge mesh local bridgeObj = CreateMesh vert_array face_array flipNormals:flipNormals --Set the bridge mesh to match the object selection if (isKindOf objSel Editable_Poly) do ( convertToPoly bridgeObj ) --Delete the temp object if we are not modifing the selected objects edges directly if not modifyEdgeSelection do delete obj ) ) ) gRsSmoothBridge = sRsSmoothBridge() --Optional for Debug --gRsSmoothBridge.BridgeEdges selection[1]