907 lines
31 KiB
Plaintext
Executable File
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] |