update purge_duplicate_path_nodes

This commit is contained in:
Mario Voigt 2023-02-14 23:50:53 +01:00
parent 77ca62a00e
commit ef6ea6f782

View File

@ -15,428 +15,55 @@
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
"""
Remove duplicate nodes or interpolate nodes with distance less than specified.
Optionally:
join start and end node of each subpath if distance < threshold
join separate subpaths if end nodes closer than threshold
Joining subpaths can be done either by interpolating or straight line segment.
Optionally join start node with end node of each subpath if distance less than specified = close the subpath
Optionally join separate subpaths if end nodes closer than a specified distance.
Joining subpaths can be done either by interpolating or straight line segment.
"""
import inkex
from inkex import bezier, CubicSuperPath
from inkex import bezier, PathElement, CubicSuperPath
import numpy as np
from tkinter import messagebox
def join_search(xdiff, ydiff, limDist, idsIncluded):
"""Search for loose ends to join if within limDist."""
joinFlag = False
idJoin = -1
dist = np.sqrt(np.add(np.power(xdiff, 2), np.power(ydiff, 2)))
minDist = np.amin(dist)
def joinTest(xdiff,ydiff,limDist,idsIncluded):
joinFlag=False
idJoin=-1
dist=np.sqrt(np.add(np.power(xdiff,2),np.power(ydiff,2)))
minDist=np.amin(dist)
if minDist < limDist:
joinFlag = True
idMins = np.where(dist == minDist)
idMin = idMins[0]
idJoin = idsIncluded[idMin[0]]
joinFlag=True
idMins=np.where(dist==minDist)
idMin=idMins[0]
idJoin=idsIncluded[idMin[0]]
return [joinFlag,idJoin]
return [joinFlag, idJoin]
def reverse_sub(subPath):
"""Reverse sub path."""
subPath = subPath[::-1]
def revSub(subPath):
subPath=subPath[::-1]
for i, s in enumerate(subPath):
subPath[i] = s[::-1]
subPath[i]=s[::-1]
return subPath
def join_sub(sub1, sub2, interpolate_or_line):
"""Join line segments by interpolation or straight line segment."""
if interpolate_or_line == "1":
# interpolate end nodes
p1 = sub1[-1][-1]
p2 = sub2[0][0]
joinNode = [0.5 * (p1[0] + p2[0]), 0.5 * (p1[1] + p2[1])]
# remove end/start + input join
sub1[-1][1] = joinNode
sub1[-1][2] = sub2[0][2]
def joinSub(sub1,sub2, interpOrLine):
if interpOrLine == "1":
#interpolate end nodes
p1=sub1[-1][-1]
p2=sub2[0][0]
joinNode=[0.5*(p1[0]+p2[0]),0.5*(p1[1]+p2[1])]
#remove end/start + input join
sub1[-1][1]=joinNode
sub1[-1][2]=sub2[0][2]
sub2.pop(0)
newsub = sub1 + sub2
newsub=sub1+sub2
return newsub
def remove_duplicate_nodes(
elem, minlength, maxdist, maxdist2, allowReverse, optionJoin
):
pp = elem.path.to_absolute()
# register which subpaths are closed - to reset closing after
# info are lost in to_superpath
dList = str(pp).upper().split(" M")
closed = []
li = 0
for sub in dList:
if dList[li].find("Z") > -1:
closed.append(" Z ")
else:
closed.append("")
li += 1
new = []
nSub = len(closed)
xStart = np.zeros(nSub) # x start - prepare for joining subpaths
yStart = np.copy(xStart)
xEnd = np.copy(xStart)
yEnd = np.copy(xStart)
s = 0
for sub in pp.to_superpath():
new.append([sub[0]])
if maxdist2 > -1:
xStart[s] = sub[0][0][0]
yStart[s] = sub[0][0][1]
xEnd[s] = sub[-1][-1][0]
yEnd[s] = sub[-1][-1][1]
# remove segment if segment length is less than minimum set,
# keep position
i = 1
lastCombined = False
while i <= len(sub) - 1:
length = bezier.cspseglength(new[-1][-1], sub[i]) # curve length
if length >= minlength:
new[-1].append(sub[i]) # add as is
lastCombined = False
else:
# keep including segments until total length > minlength
summedlength = length
proceed = True
e = 0 # extra segments
finishedAdding = False
while proceed and i + e + 1 <= len(sub) - 1:
nextlength = bezier.cspseglength(sub[i + e], sub[i + e + 1])
if nextlength >= minlength: # not include the next segment
proceed = False
if lastCombined == False and i > 1:
# i.e.small group between long segments,
# average over the group, first node already added
# change position to average
new[-1][-1][1][0] = 0.5 * (
new[-1][-1][1][0] + sub[i + e][1][0]
)
new[-1][-1][1][1] = 0.5 * (
new[-1][-1][1][1] + sub[i + e][1][1]
)
# change last cp to that of the last node in group
new[-1][-1][2] = sub[i + e][2]
finishedAdding = True
else:
new[-1].append(sub[i]) # add as is
if e > 0:
# end of group with many segments - average over
# all but last node (which is added separately)
# change position to average first/last
new[-1][-1][1][0] = 0.5 * (
new[-1][-1][1][0] + sub[i + e - 1][1][0]
)
new[-1][-1][1][1] = 0.5 * (
new[-1][-1][1][1] + sub[i + e - 1][1][1]
)
# change last cp to that of the last node in group
new[-1][-1][2] = sub[i + e - 1][2]
new[-1].append(sub[i + e]) # add as is
finishedAdding = True
lastCombined = True
else:
summedlength = summedlength + nextlength
if summedlength >= minlength:
proceed = False
e = e + 1
if finishedAdding == False:
if i == 1:
# if first segment keep position of first node,
# direction of last in group
new[-1][-1][2][0] = sub[i + e][2][0]
new[-1][-1][2][1] = sub[i + e][2][1]
elif i + e == len(sub) - 1:
# if last segment included keep position of last node,
# direction of previous
new[-1].append(sub[i]) # add first node in group
if e > 0:
new[-1].append(sub[i + e]) # add last node
# get first cp from i+1
new[-1][-1][0] = sub[i + 1][0]
else:
# average position over first/last in group and keep direction (controlpoint) of first/last node
# group within sequence of many close nodes - add new without averaging on previous
new[-1].append(sub[i]) # add first node in group
# change position to average
new[-1][-1][1][0] = 0.5 * (new[-1][-1][1][0] + sub[i + e][1][0])
new[-1][-1][1][1] = 0.5 * (new[-1][-1][1][1] + sub[i + e][1][1])
# change last cp to that of the last node in group
new[-1][-1][2] = sub[i + e][2]
i = i + e
i += 1
if closed[s] == " Z ":
# if new[-1][-1][1]==new[-1][-2][1]:#not always precise
new[-1].pop(-1)
# for some reason tosuperpath adds an extra node for closed paths
# close each subpath where start/end node is closer than maxdist set
# (if not already closed)
if maxdist > -1:
if closed[s] == "": # ignore already closed paths
# calculate distance between first and last node,
# if <= maxdist set closed[i] to " Z "
# last=new[-1][-1]
length = bezier.cspseglength(new[-1][-1], sub[0])
if length < maxdist:
newStartEnd = [
0.5 * (new[-1][-1][-1][0] + new[-1][0][0][0]),
0.5 * (new[-1][-1][-1][1] + new[-1][0][0][1]),
]
new[-1][0][0] = newStartEnd
new[-1][0][1] = newStartEnd
new[-1][-1][1] = newStartEnd
new[-1][-1][2] = newStartEnd
closed[s] = " Z "
s += 1
# join different subpaths?
closed = np.array(closed)
openPaths = np.where(closed == "")
closedPaths = np.where(closed == " Z ")
if maxdist2 > -1 and openPaths[0].size > 1:
# calculate distance between end nodes of the subpaths.
# If distance < maxdist2 found - join
joinStartToEnd = np.ones(nSub, dtype=bool)
joinEndToStart = np.copy(joinStartToEnd)
joinEndTo = np.full(nSub, -1)
# set higher than maxdist2 to avoid join to closedPaths
joinEndTo[closedPaths] = 2 * maxdist2
joinStartTo = np.copy(joinEndTo)
# join end node of current subpath to startnode of any other
# or start node of current to end node of other (no reverse)
s = 0
while s < nSub:
# end of current to start of other
if joinEndTo[s] == -1:
# find available start nodes
idsTest = np.where(joinStartTo == -1)
# avoid join to self
id2Test = np.delete(idsTest[0], np.where(idsTest[0] == s))
if id2Test.size > 0:
# calculate distances in x/y direction
diff_x = np.subtract(xStart[id2Test], xEnd[s])
diff_y = np.subtract(yStart[id2Test], yEnd[s])
# find shortest distance if less than minimum
res = join_search(diff_x, diff_y, maxdist2, id2Test)
if res[0] == True:
# if match found flag end of this with id of other and flag start of match to end of this
joinEndTo[s] = res[1]
joinStartTo[res[1]] = s
# start of current to end of other
if joinStartTo[s] == -1:
idsTest = np.where(joinEndTo == -1)
id2Test = np.delete(idsTest[0], np.where(idsTest[0] == s))
if id2Test.size > 0:
diff_x = np.subtract(xEnd[id2Test], xStart[s])
diff_y = np.subtract(yEnd[id2Test], yStart[s])
res = join_search(diff_x, diff_y, maxdist2, id2Test)
if res[0] == True:
joinStartTo[s] = res[1]
joinEndTo[res[1]] = s
if allowReverse == True:
# start to start - if match reverse (reverseSub[s]=True)
if joinStartTo[s] == -1:
idsTest = np.where(joinStartTo == -1)
id2Test = np.delete(idsTest[0], np.where(idsTest[0] == s))
if id2Test.size > 0:
diff_x = np.subtract(xStart[id2Test], xStart[s])
diff_y = np.subtract(yStart[id2Test], yStart[s])
res = join_search(diff_x, diff_y, maxdist2, id2Test)
if res[0] == True:
jID = res[1]
joinStartTo[s] = jID
joinStartTo[jID] = s
joinStartToEnd[s] = False # false means reverse
joinStartToEnd[jID] = False
# end to end
if joinEndTo[s] == -1:
idsTest = np.where(joinEndTo == -1)
id2Test = np.delete(idsTest[0], np.where(idsTest[0] == s))
if id2Test.size > 0:
diff_x = np.subtract(xEnd[id2Test], xEnd[s])
diff_y = np.subtract(yEnd[id2Test], yEnd[s])
res = join_search(diff_x, diff_y, maxdist2, id2Test)
if res[0] == True:
jID = res[1]
joinEndTo[s] = jID
joinEndTo[jID] = s
joinEndToStart[s] = False
joinEndToStart[jID] = False
s += 1
old = new
new = []
s = 0
movedTo = np.arange(nSub)
newClosed = []
# avoid joining to other paths if already closed
joinEndTo[closedPaths] = -1
joinStartTo[closedPaths] = -1
for s in range(0, nSub):
if movedTo[s] == s: # not joined yet
if joinEndTo[s] > -1 or joinStartTo[s] > -1:
# any join scheduled
thisSub = []
closedThis = ""
if joinEndTo[s] > -1:
# join one by one until -1 or back to s (closed)
jID = joinEndTo[s]
sub1 = old[s]
sub2 = old[jID]
rev = True if joinEndToStart[s] == False else False
sub2 = reverse_sub(sub2) if rev == True else sub2
thisSub = join_sub(sub1, sub2, optionJoin)
movedTo[jID] = s
prev = s
# continue if sub2 joined to more
if joinEndTo[jID] > -1 and joinStartTo[jID] > -1:
# already joined so both joined if continue
proceed = 1
while proceed == 1:
nID = (
joinEndTo[jID]
if joinEndTo[jID] != prev
else joinStartTo[jID]
)
if movedTo[nID] == s:
closedThis = " Z "
proceed = 0
else:
sub2 = old[nID]
if (
nID == joinEndTo[jID]
and joinStartTo[nID] == jID
) or (
nID == joinStartTo[jID]
and joinEndTo[nID] == jID
):
pass
else:
rev = not rev
sub2 = reverse_sub(sub2) if rev == True else sub2
thisSub = join_sub(thisSub, sub2, optionJoin)
movedTo[nID] = s
if joinEndTo[nID] > -1 and joinStartTo[nID] > -1:
prev = jID
jID = nID
else:
proceed = 0
if joinStartTo[s] > -1 and closedThis == "":
jID = joinStartTo[s]
sub1 = old[jID]
rev = True if joinStartToEnd[s] == False else False
sub1 = reverse_sub(sub1) if rev == True else sub1
sub2 = thisSub if len(thisSub) > 0 else old[s]
thisSub = join_sub(sub1, sub2, optionJoin)
movedTo[jID] = s
prev = s
# continue if sub1 joined to more
if joinEndTo[jID] > -1 and joinStartTo[jID] > -1:
proceed = 1
while proceed == 1:
nID = (
joinStartTo[jID]
if joinStartTo[jID] != prev
else joinEndTo[jID]
)
if movedTo[nID] == s:
closedThis = " Z "
proceed = 0
else:
sub1 = old[nID]
if (
nID == joinEndTo[jID]
and joinStartTo[nID] == jID
) or (
nID == joinStartTo[jID]
and joinEndTo[nID] == jID
):
pass
else:
rev = not rev
sub1 = reverse_sub(sub1) if rev == True else sub1
thisSub = join_sub(sub1, thisSub, optionJoin)
movedTo[nID] = s
if joinEndTo[nID] > -1 and joinStartTo[nID] > -1:
prev = jID
jID = nID
else:
proceed = 0
# close the new subpath if start/end node is closer than maxdist
# (should be handled above, but is not so this was a quick fix)
if closedThis == " Z " and optionJoin == "1":
newStartEnd = [
0.5 * (thisSub[-1][-1][0] + thisSub[0][0][0]),
0.5 * (thisSub[-1][-1][1] + thisSub[0][0][1]),
]
thisSub[0][0] = newStartEnd
thisSub[0][1] = newStartEnd
thisSub[-1][1] = newStartEnd
thisSub[-1][2] = newStartEnd
new.append(thisSub)
newClosed.append(closedThis)
else:
new.append(old[s])
newClosed.append(closed[s])
closed = newClosed
nEmpty = new.count([])
if nEmpty > 0:
for i in range(nEmpty):
idx_empty = new.index([])
new.pop(idx_empty)
closed = np.delete(closed, idx_empty)
return (new, closed)
class RemoveDuplicateNodes(inkex.EffectExtension):
def add_arguments(self, pars):
pars.add_argument("--tab", default="options")
pars.add_argument("--minlength", default="0")
@ -447,78 +74,321 @@ class RemoveDuplicateNodes(inkex.EffectExtension):
pars.add_argument("--joinEndSub", type=inkex.Boolean, default=False)
pars.add_argument("--allowReverse", type=inkex.Boolean, default=True)
pars.add_argument("--optionJoin", default="1")
"""Remove duplicate nodes"""
def effect(self):
if not self.svg.selected:
raise inkex.AbortExtension("Please select an object.")
minlength = float(self.options.minlength)
maxdist = float(self.options.maxdist)
maxdist2 = float(self.options.maxdist2)
if self.options.minUse is False:
minlength = 0
if self.options.joinEnd is False:
maxdist = -1
if self.options.joinEndSub is False:
maxdist2 = -1
nFailed = 0
nInkEffect = 0
minlength=float(self.options.minlength)
maxdist=float(self.options.maxdist)
maxdist2=float(self.options.maxdist2)
if self.options.minUse == False: minlength=0
if self.options.joinEnd == False: maxdist=-1
if self.options.joinEndSub == False: maxdist2=-1
nFailed=0
nInkEffect=0
for id, elem in self.svg.selection.id_dict().items():
thisIsPath = True
if elem.get("d") is None:
thisIsPath = False
nFailed += 1
if elem.get("inkscape:path-effect") is not None:
thisIsPath = False
nInkEffect += 1
thisIsPath=True
if elem.get('d')==None:
thisIsPath=False
nFailed+=1
if elem.get('inkscape:path-effect') != None:
thisIsPath=False
nInkEffect+=1
if thisIsPath:
new, closed = remove_duplicate_nodes(
elem,
minlength,
maxdist,
maxdist2,
self.options.allowReverse,
self.options.optionJoin,
)
pp=elem.path.to_absolute()
#register which subpaths are closed - to reset closing after missed in to_superpath
dList=str(pp).upper().split(' M')
closed=[]
l=0
for sub in dList:
if dList[l].find("Z") > -1:
closed.append(" Z ")
else:
closed.append("")
l+=1
new = []
nSub=len(closed)
xStart=np.zeros(nSub)#x start - prepare for joining subpaths
yStart=np.copy(xStart)
xEnd=np.copy(xStart)
yEnd=np.copy(xStart)
s=0
for sub in pp.to_superpath():
new.append([sub[0]])
if maxdist2 > -1:
xStart[s]=sub[0][0][0]
yStart[s]=sub[0][0][1]
xEnd[s]=sub[-1][-1][0]
yEnd[s]=sub[-1][-1][1]
#remove segment if segment length is less than minimum set, keep position
i=1
lastCombined=False
while i <= len(sub) - 1:
length = bezier.cspseglength(new[-1][-1], sub[i]) #curve length
if length >= minlength:
new[-1].append(sub[i])#add as is
lastCombined=False
else:
#keep including segments until total length > minlength
summedlength=length
proceed=True
e=0 #extra segments
finishedAdding=False
while proceed and i+e +1 <= len(sub) -1:
nextlength=bezier.cspseglength(sub[i+e], sub[i+e+1]) #curve length
if nextlength >= minlength: #don't include the next segment
proceed=False
if lastCombined == False and i>1: #i.e. this is a small group between long segments then average over the group, first node already added (new -1)
new[-1][-1][1][0]= 0.5*(new[-1][-1][1][0]+sub[i+e][1][0])#change position to average
new[-1][-1][1][1]= 0.5*(new[-1][-1][1][1]+sub[i+e][1][1])
new[-1][-1][2]=sub[i+e][2]#change last controlpoint to that of the last node in group
finishedAdding=True
else: #end of group with many segments - average over all but last node (which is added separately)
new[-1].append(sub[i])#add as is
new[-1][-1][1][0]= 0.5*(new[-1][-1][1][0]+sub[i+e-1][1][0])#change position to average first/last
new[-1][-1][1][1]= 0.5*(new[-1][-1][1][1]+sub[i+e-1][1][1])
new[-1][-1][2]=sub[i+e-1][2]#change last controlpoint to that of the last node in group
new[-1].append(sub[i+e])#add as is
finishedAdding=True
lastCombined=True
else:
summedlength=summedlength+nextlength
if summedlength >= minlength:
proceed=False
e=e+1
if finishedAdding == False:
if i == 1:# if first segment keep position of first node, direction of last in group
new[-1][-1][2][0]= sub[i+e][2][0]
new[-1][-1][2][1]= sub[i+e][2][1]
elif i + e == len(sub)-1:#if last segment included keep position of last node, direction of previous
new[-1].append(sub[i])#add first node in group
if e > 0 :
new[-1].append(sub[i+e])#add last node
new[-1][-1][0]= sub[i+1][0]#get first controlpoint from i+1
else:
#average position over first/last in group and keep direction (controlpoint) of first/last node
#group within sequence of many close nodes - add new without averaging on previous
new[-1].append(sub[i])#add first node in group
new[-1][-1][1][0]= 0.5*(new[-1][-1][1][0]+sub[i+e][1][0])#change position to average
new[-1][-1][1][1]= 0.5*(new[-1][-1][1][1]+sub[i+e][1][1])
new[-1][-1][2]=sub[i+e][2]#change last controlpoint to that of the last node in group
i=i+e
i += 1
if closed[s]==" Z ":
#if new[-1][-1][1]==new[-1][-2][1]:#not always precise
new[-1].pop(-1)#for some reason tosuperpath adds an extra node for closed paths
#close each subpath where start/end node is closer than maxdist set (if not already closed)
if maxdist > -1:
if closed[s] == "": #ignore already closed paths
#calculate distance between first and last node, if <= maxdist set closed[i] to " Z "
#last=new[-1][-1]
length = bezier.cspseglength(new[-1][-1], sub[0])
if length < maxdist:
newStartEnd=[0.5*(new[-1][-1][-1][0]+new[-1][0][0][0]),0.5*(new[-1][-1][-1][1]+new[-1][0][0][1])]
new[-1][0][0]=newStartEnd
new[-1][0][1]=newStartEnd
new[-1][-1][1]=newStartEnd
new[-1][-1][2]=newStartEnd
closed[s]=" Z "
s+=1
#join different subpaths?
closed=np.array(closed)
openPaths=np.where(closed=='')
closedPaths=np.where(closed==' Z ')
if maxdist2 > -1 and openPaths[0].size > 1:
#calculate distance between end nodes of the subpaths. If distance < maxdist2 found - join
joinStartToEnd=np.ones(nSub, dtype=bool)
joinEndToStart=np.copy(joinStartToEnd)
joinEndTo=np.full(nSub,-1)
joinEndTo[closedPaths]=2*maxdist2#set higher than maxdist2 to avoid join to closedPaths
joinStartTo=np.copy(joinEndTo)
#join end node of current subpath to startnode of any other or start node of current to end node of other (no reverse)
s=0
while s < nSub:
#end of current to start of other
if joinEndTo[s]==-1:
idsTest=np.where(joinStartTo==-1)#find available start nodes
id2Test=np.delete(idsTest[0],np.where(idsTest[0] == s))#avoid join to self
if id2Test.size > 0:
diff_x=np.subtract(xStart[id2Test],xEnd[s])#calculate distances in x direction
diff_y=np.subtract(yStart[id2Test],yEnd[s])#calculate distances in y direction
res=joinTest(diff_x,diff_y,maxdist2,id2Test)#find shortest distance if less than minimum
if res[0] == True:#if match found flag end of this with id of other and flag start of match to end of this
joinEndTo[s]=res[1]
joinStartTo[res[1]]=s
#start of current to end of other
if joinStartTo[s]==-1:
idsTest=np.where(joinEndTo==-1)
id2Test=np.delete(idsTest[0],np.where(idsTest[0] == s))
if id2Test.size > 0:
diff_x=np.subtract(xEnd[id2Test],xStart[s])
diff_y=np.subtract(yEnd[id2Test],yStart[s])
res=joinTest(diff_x,diff_y,maxdist2,id2Test)
if res[0] == True:
joinStartTo[s]=res[1]
joinEndTo[res[1]]=s
if self.options.allowReverse==True:
#start to start - if match reverse (reverseSub[s]=True)
if joinStartTo[s]==-1:
idsTest=np.where(joinStartTo==-1)
id2Test=np.delete(idsTest[0],np.where(idsTest[0] == s))
if id2Test.size > 0:
diff_x=np.subtract(xStart[id2Test],xStart[s])
diff_y=np.subtract(yStart[id2Test],yStart[s])
res=joinTest(diff_x,diff_y,maxdist2,id2Test)
if res[0] == True:
jID=res[1]
joinStartTo[s]=jID
joinStartTo[jID]=s
joinStartToEnd[s]=False #false means reverse
joinStartToEnd[jID]=False
#end to end
if joinEndTo[s]==-1:
idsTest=np.where(joinEndTo==-1)
id2Test=np.delete(idsTest[0],np.where(idsTest[0] == s))
if id2Test.size > 0:
diff_x=np.subtract(xEnd[id2Test],xEnd[s])
diff_y=np.subtract(yEnd[id2Test],yEnd[s])
res=joinTest(diff_x,diff_y,maxdist2,id2Test)
if res[0] == True:
jID=res[1]
joinEndTo[s]=jID
joinEndTo[jID]=s
joinEndToStart[s]=False
joinEndToStart[jID]=False
s+=1
old=new
new=[]
s=0
movedTo=np.arange(nSub)
newClosed=[]
joinEndTo[closedPaths]=-1#avoid joining to other paths if already closed
joinStartTo[closedPaths]=-1
for s in range(0,nSub):
if movedTo[s] == s:#not joined yet
if joinEndTo[s] > -1 or joinStartTo[s] > -1:#any join scheduled
thisSub=[]
closedThis=""
if joinEndTo[s] > -1:# join one by one until -1 or back to s (closed)
jID=joinEndTo[s]
sub1=old[s]
sub2=old[jID]
rev=True if joinEndToStart[s] == False else False
sub2=revSub(sub2) if rev == True else sub2
thisSub=joinSub(sub1,sub2,self.options.optionJoin)
movedTo[jID]=s
prev=s
#continue if sub2 joined to more
if joinEndTo[jID] > -1 and joinStartTo[jID] > -1:#already joined so both joined if continue
proceed=1
while proceed == 1:
nID=joinEndTo[jID] if joinEndTo[jID] != prev else joinStartTo[jID]
if movedTo[nID] == s:
closedThis=" Z "
proceed=0
else:
sub2=old[nID]
if (nID == joinEndTo[jID] and joinStartTo[nID] == jID) or (nID == joinStartTo[jID] and joinEndTo[nID] == jID):
pass
else:
rev = not rev
sub2=revSub(sub2) if rev == True else sub2
thisSub=joinSub(thisSub,sub2,self.options.optionJoin)
movedTo[nID]=s
if joinEndTo[nID] > -1 and joinStartTo[nID] > -1:
prev=jID
jID=nID
else:
proceed=0
if joinStartTo[s] > -1 and closedThis =="":
jID=joinStartTo[s]
sub1=old[jID]
rev=True if joinStartToEnd[s] == False else False
sub1=revSub(sub1) if rev == True else sub1
sub2=thisSub if len(thisSub) > 0 else old[s]
thisSub=joinSub(sub1,sub2,self.options.optionJoin)
movedTo[jID]=s
prev=s
#continue if sub1 joined to more
if joinEndTo[jID] > -1 and joinStartTo[jID] > -1:
proceed=1
while proceed == 1:
nID=joinStartTo[jID] if joinStartTo[jID] != prev else joinEndTo[jID]
if movedTo[nID] == s:
closedThis=" Z "
proceed=0
else:
sub1=old[nID]
if (nID == joinEndTo[jID] and joinStartTo[nID] == jID) or (nID == joinStartTo[jID] and joinEndTo[nID] == jID):
pass
else:
rev = not rev
sub1=revSub(sub1) if rev == True else sub1
thisSub=joinSub(sub1,thisSub,self.options.optionJoin)
movedTo[nID]=s
if joinEndTo[nID] > -1 and joinStartTo[nID] > -1:
prev=jID
jID=nID
else:
proceed=0
new.append(thisSub)
newClosed.append(closedThis)
else:
new.append(old[s])
newClosed.append(closed[s])
closed=newClosed
elem.path = CubicSuperPath(new).to_path(curves_only=True)
# reset z to the originally closed paths
# (z lost in cubicsuperpath)
temppath = str(elem.path.to_absolute()).split("M ")
#reset z to the originally closed paths (z lost in cubicsuperpath)
temppath=str(elem.path.to_absolute()).split('M ')
temppath.pop(0)
newPath = ""
li = 0
newPath=''
l=0
for sub in temppath:
newPath = newPath + "M " + temppath[li] + closed[li]
li += 1
elem.path = newPath
newPath=newPath+'M '+temppath[l]+closed[l]
l+=1
elem.path=newPath
if nFailed > 0:
messagebox.showwarning('Warning',str(nFailed)+' selected elements have no path specified. Groups have to be ungrouped first and paths have to be combined with Ctrl + K to be considered for joining. Shape-elements and text will be ignored.')
if nFailed > 0:
messagebox.showwarning(
"Warning",
f"""{nFailed} selected elements have no path specified.
Groups have to be ungrouped first and paths have to be
combined with Ctrl + K to be considered for joining.
Shape-elements and text will be ignored.""",
)
if nInkEffect > 0:
messagebox.showwarning(
"Warning",
f"""{nInkEffect} selected elements have an
inkscape:path-effect applied. These elements will be
ignored to avoid confusing results. Apply Paths->Object
to path (Shift+Ctrl+C) and retry .""",
)
if __name__ == "__main__":
if nInkEffect > 0:
messagebox.showwarning('Warning',str(nInkEffect)+' selected elements have an inkscape:path-effect applied. These elements will be ignored to avoid confusing results. Apply Paths->Object to path (Shift+Ctrl+C) and retry .')
if __name__ == '__main__':
RemoveDuplicateNodes().run()