Files
2025-09-29 00:52:08 +02:00

907 lines
31 KiB
Plaintext
Executable File

--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 <facelist>
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]