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,23 +15,20 @@
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# Boston, MA 02110-1301, USA.
""" """
Remove duplicate nodes or interpolate nodes with distance less than specified. Remove duplicate nodes or interpolate nodes with distance less than specified.
Optionally: Optionally join start node with end node of each subpath if distance less than specified = close the subpath
join start and end node of each subpath if distance < threshold Optionally join separate subpaths if end nodes closer than a specified distance.
join separate subpaths if end nodes closer than threshold
Joining subpaths can be done either by interpolating or straight line segment. Joining subpaths can be done either by interpolating or straight line segment.
""" """
import inkex import inkex
from inkex import bezier, CubicSuperPath from inkex import bezier, PathElement, CubicSuperPath
import numpy as np import numpy as np
from tkinter import messagebox from tkinter import messagebox
def joinTest(xdiff,ydiff,limDist,idsIncluded):
def join_search(xdiff, ydiff, limDist, idsIncluded):
"""Search for loose ends to join if within limDist."""
joinFlag=False joinFlag=False
idJoin=-1 idJoin=-1
dist=np.sqrt(np.add(np.power(xdiff,2),np.power(ydiff,2))) dist=np.sqrt(np.add(np.power(xdiff,2),np.power(ydiff,2)))
@ -44,19 +41,14 @@ def join_search(xdiff, ydiff, limDist, idsIncluded):
return [joinFlag,idJoin] return [joinFlag,idJoin]
def revSub(subPath):
def reverse_sub(subPath):
"""Reverse sub path."""
subPath=subPath[::-1] subPath=subPath[::-1]
for i, s in enumerate(subPath): for i, s in enumerate(subPath):
subPath[i]=s[::-1] subPath[i]=s[::-1]
return subPath return subPath
def joinSub(sub1,sub2, interpOrLine):
def join_sub(sub1, sub2, interpolate_or_line): if interpOrLine == "1":
"""Join line segments by interpolation or straight line segment."""
if interpolate_or_line == "1":
#interpolate end nodes #interpolate end nodes
p1=sub1[-1][-1] p1=sub1[-1][-1]
p2=sub2[0][0] p2=sub2[0][0]
@ -70,24 +62,59 @@ def join_sub(sub1, sub2, interpolate_or_line):
return newsub return newsub
class RemoveDuplicateNodes(inkex.EffectExtension):
def remove_duplicate_nodes( def add_arguments(self, pars):
elem, minlength, maxdist, maxdist2, allowReverse, optionJoin pars.add_argument("--tab", default="options")
): pars.add_argument("--minlength", default="0")
pars.add_argument("--minUse", type=inkex.Boolean, default=False)
pars.add_argument("--maxdist", default="0")
pars.add_argument("--joinEnd", type=inkex.Boolean, default=False)
pars.add_argument("--maxdist2", default="0")
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 == 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')==None:
thisIsPath=False
nFailed+=1
if elem.get('inkscape:path-effect') != None:
thisIsPath=False
nInkEffect+=1
if thisIsPath:
pp=elem.path.to_absolute() pp=elem.path.to_absolute()
# register which subpaths are closed - to reset closing after #register which subpaths are closed - to reset closing after missed in to_superpath
# info are lost in to_superpath dList=str(pp).upper().split(' M')
dList = str(pp).upper().split(" M")
closed=[] closed=[]
li = 0 l=0
for sub in dList: for sub in dList:
if dList[li].find("Z") > -1: if dList[l].find("Z") > -1:
closed.append(" Z ") closed.append(" Z ")
else: else:
closed.append("") closed.append("")
li += 1 l+=1
new = [] new = []
nSub=len(closed) nSub=len(closed)
@ -105,8 +132,7 @@ def remove_duplicate_nodes(
yStart[s]=sub[0][0][1] yStart[s]=sub[0][0][1]
xEnd[s]=sub[-1][-1][0] xEnd[s]=sub[-1][-1][0]
yEnd[s]=sub[-1][-1][1] yEnd[s]=sub[-1][-1][1]
# remove segment if segment length is less than minimum set, #remove segment if segment length is less than minimum set, keep position
# keep position
i=1 i=1
lastCombined=False lastCombined=False
while i <= len(sub) - 1: while i <= len(sub) - 1:
@ -121,43 +147,23 @@ def remove_duplicate_nodes(
e=0 #extra segments e=0 #extra segments
finishedAdding=False finishedAdding=False
while proceed and i+e +1 <= len(sub) -1: while proceed and i+e +1 <= len(sub) -1:
nextlength = bezier.cspseglength(sub[i + e], sub[i + e + 1]) nextlength=bezier.cspseglength(sub[i+e], sub[i+e+1]) #curve length
if nextlength >= minlength: # not include the next segment if nextlength >= minlength: #don't include the next segment
proceed=False proceed=False
if lastCombined == False and i > 1: 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)
# i.e.small group between long segments, new[-1][-1][1][0]= 0.5*(new[-1][-1][1][0]+sub[i+e][1][0])#change position to average
# average over the group, first node already added 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
# 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 finishedAdding=True
else: 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].append(sub[i])#add as is
if e > 0: new[-1][-1][1][0]= 0.5*(new[-1][-1][1][0]+sub[i+e-1][1][0])#change position to average first/last
# end of group with many segments - average over new[-1][-1][1][1]= 0.5*(new[-1][-1][1][1]+sub[i+e-1][1][1])
# all but last node (which is added separately) new[-1][-1][2]=sub[i+e-1][2]#change last controlpoint to that of the last node in group
# 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 new[-1].append(sub[i+e])#add as is
finishedAdding=True finishedAdding=True
lastCombined=True lastCombined=True
else: else:
summedlength=summedlength+nextlength summedlength=summedlength+nextlength
if summedlength >= minlength: if summedlength >= minlength:
@ -166,31 +172,21 @@ def remove_duplicate_nodes(
if finishedAdding == False: if finishedAdding == False:
if i == 1: if i == 1:# if first segment keep position of first node, direction of last in group
# 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][0]= sub[i+e][2][0]
new[-1][-1][2][1]= sub[i+e][2][1] new[-1][-1][2][1]= sub[i+e][2][1]
elif i + e == len(sub) - 1: elif i + e == len(sub)-1:#if last segment included keep position of last node, direction of previous
# if last segment included keep position of last node,
# direction of previous
new[-1].append(sub[i])#add first node in group new[-1].append(sub[i])#add first node in group
if e > 0 : if e > 0 :
new[-1].append(sub[i+e])#add last node new[-1].append(sub[i+e])#add last node
# get first cp from i+1 new[-1][-1][0]= sub[i+1][0]#get first controlpoint from i+1
new[-1][-1][0] = sub[i + 1][0]
else: else:
#average position over first/last in group and keep direction (controlpoint) of first/last node #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 #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].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
# 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]) 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
# change last cp to that of the last node in group
new[-1][-1][2] = sub[i + e][2]
i=i+e i=i+e
@ -198,22 +194,16 @@ def remove_duplicate_nodes(
if closed[s]==" Z ": if closed[s]==" Z ":
#if new[-1][-1][1]==new[-1][-2][1]:#not always precise #if new[-1][-1][1]==new[-1][-2][1]:#not always precise
new[-1].pop(-1) new[-1].pop(-1)#for some reason tosuperpath adds an extra node for closed paths
# for some reason tosuperpath adds an extra node for closed paths
# close each subpath where start/end node is closer than maxdist set #close each subpath where start/end node is closer than maxdist set (if not already closed)
# (if not already closed)
if maxdist > -1: if maxdist > -1:
if closed[s] == "": #ignore already closed paths if closed[s] == "": #ignore already closed paths
# calculate distance between first and last node, #calculate distance between first and last node, if <= maxdist set closed[i] to " Z "
# if <= maxdist set closed[i] to " Z "
#last=new[-1][-1] #last=new[-1][-1]
length = bezier.cspseglength(new[-1][-1], sub[0]) length = bezier.cspseglength(new[-1][-1], sub[0])
if length < maxdist: if length < maxdist:
newStartEnd = [ 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])]
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][0]=newStartEnd
new[-1][0][1]=newStartEnd new[-1][0][1]=newStartEnd
new[-1][-1][1]=newStartEnd new[-1][-1][1]=newStartEnd
@ -224,36 +214,28 @@ def remove_duplicate_nodes(
#join different subpaths? #join different subpaths?
closed=np.array(closed) closed=np.array(closed)
openPaths = np.where(closed == "") openPaths=np.where(closed=='')
closedPaths = np.where(closed == " Z ") closedPaths=np.where(closed==' Z ')
if maxdist2 > -1 and openPaths[0].size > 1: if maxdist2 > -1 and openPaths[0].size > 1:
# calculate distance between end nodes of the subpaths. #calculate distance between end nodes of the subpaths. If distance < maxdist2 found - join
# If distance < maxdist2 found - join
joinStartToEnd=np.ones(nSub, dtype=bool) joinStartToEnd=np.ones(nSub, dtype=bool)
joinEndToStart=np.copy(joinStartToEnd) joinEndToStart=np.copy(joinStartToEnd)
joinEndTo=np.full(nSub,-1) joinEndTo=np.full(nSub,-1)
# set higher than maxdist2 to avoid join to closedPaths joinEndTo[closedPaths]=2*maxdist2#set higher than maxdist2 to avoid join to closedPaths
joinEndTo[closedPaths] = 2 * maxdist2
joinStartTo=np.copy(joinEndTo) joinStartTo=np.copy(joinEndTo)
# join end node of current subpath to startnode of any other #join end node of current subpath to startnode of any other or start node of current to end node of other (no reverse)
# or start node of current to end node of other (no reverse)
s=0 s=0
while s < nSub: while s < nSub:
#end of current to start of other #end of current to start of other
if joinEndTo[s]==-1: if joinEndTo[s]==-1:
# find available start nodes idsTest=np.where(joinStartTo==-1)#find available start nodes
idsTest = np.where(joinStartTo == -1) id2Test=np.delete(idsTest[0],np.where(idsTest[0] == s))#avoid join to self
# avoid join to self
id2Test = np.delete(idsTest[0], np.where(idsTest[0] == s))
if id2Test.size > 0: if id2Test.size > 0:
# calculate distances in x/y direction diff_x=np.subtract(xStart[id2Test],xEnd[s])#calculate distances in x direction
diff_x = np.subtract(xStart[id2Test], xEnd[s]) diff_y=np.subtract(yStart[id2Test],yEnd[s])#calculate distances in y direction
diff_y = np.subtract(yStart[id2Test], yEnd[s]) res=joinTest(diff_x,diff_y,maxdist2,id2Test)#find shortest distance if less than minimum
# 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
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] joinEndTo[s]=res[1]
joinStartTo[res[1]]=s joinStartTo[res[1]]=s
@ -264,12 +246,12 @@ def remove_duplicate_nodes(
if id2Test.size > 0: if id2Test.size > 0:
diff_x=np.subtract(xEnd[id2Test],xStart[s]) diff_x=np.subtract(xEnd[id2Test],xStart[s])
diff_y=np.subtract(yEnd[id2Test],yStart[s]) diff_y=np.subtract(yEnd[id2Test],yStart[s])
res = join_search(diff_x, diff_y, maxdist2, id2Test) res=joinTest(diff_x,diff_y,maxdist2,id2Test)
if res[0] == True: if res[0] == True:
joinStartTo[s]=res[1] joinStartTo[s]=res[1]
joinEndTo[res[1]]=s joinEndTo[res[1]]=s
if allowReverse == True: if self.options.allowReverse==True:
#start to start - if match reverse (reverseSub[s]=True) #start to start - if match reverse (reverseSub[s]=True)
if joinStartTo[s]==-1: if joinStartTo[s]==-1:
idsTest=np.where(joinStartTo==-1) idsTest=np.where(joinStartTo==-1)
@ -277,7 +259,7 @@ def remove_duplicate_nodes(
if id2Test.size > 0: if id2Test.size > 0:
diff_x=np.subtract(xStart[id2Test],xStart[s]) diff_x=np.subtract(xStart[id2Test],xStart[s])
diff_y=np.subtract(yStart[id2Test],yStart[s]) diff_y=np.subtract(yStart[id2Test],yStart[s])
res = join_search(diff_x, diff_y, maxdist2, id2Test) res=joinTest(diff_x,diff_y,maxdist2,id2Test)
if res[0] == True: if res[0] == True:
jID=res[1] jID=res[1]
joinStartTo[s]=jID joinStartTo[s]=jID
@ -292,7 +274,7 @@ def remove_duplicate_nodes(
if id2Test.size > 0: if id2Test.size > 0:
diff_x=np.subtract(xEnd[id2Test],xEnd[s]) diff_x=np.subtract(xEnd[id2Test],xEnd[s])
diff_y=np.subtract(yEnd[id2Test],yEnd[s]) diff_y=np.subtract(yEnd[id2Test],yEnd[s])
res = join_search(diff_x, diff_y, maxdist2, id2Test) res=joinTest(diff_x,diff_y,maxdist2,id2Test)
if res[0] == True: if res[0] == True:
jID=res[1] jID=res[1]
joinEndTo[s]=jID joinEndTo[s]=jID
@ -307,54 +289,40 @@ def remove_duplicate_nodes(
s=0 s=0
movedTo=np.arange(nSub) movedTo=np.arange(nSub)
newClosed=[] newClosed=[]
# avoid joining to other paths if already closed joinEndTo[closedPaths]=-1#avoid joining to other paths if already closed
joinEndTo[closedPaths] = -1
joinStartTo[closedPaths]=-1 joinStartTo[closedPaths]=-1
for s in range(0,nSub): for s in range(0,nSub):
if movedTo[s] == s:#not joined yet if movedTo[s] == s:#not joined yet
if joinEndTo[s] > -1 or joinStartTo[s] > -1: if joinEndTo[s] > -1 or joinStartTo[s] > -1:#any join scheduled
# any join scheduled
thisSub=[] thisSub=[]
closedThis="" closedThis=""
if joinEndTo[s] > -1: if joinEndTo[s] > -1:# join one by one until -1 or back to s (closed)
# join one by one until -1 or back to s (closed)
jID=joinEndTo[s] jID=joinEndTo[s]
sub1=old[s] sub1=old[s]
sub2=old[jID] sub2=old[jID]
rev=True if joinEndToStart[s] == False else False rev=True if joinEndToStart[s] == False else False
sub2 = reverse_sub(sub2) if rev == True else sub2 sub2=revSub(sub2) if rev == True else sub2
thisSub = join_sub(sub1, sub2, optionJoin) thisSub=joinSub(sub1,sub2,self.options.optionJoin)
movedTo[jID]=s movedTo[jID]=s
prev=s prev=s
#continue if sub2 joined to more #continue if sub2 joined to more
if joinEndTo[jID] > -1 and joinStartTo[jID] > -1: if joinEndTo[jID] > -1 and joinStartTo[jID] > -1:#already joined so both joined if continue
# already joined so both joined if continue
proceed=1 proceed=1
while proceed == 1: while proceed == 1:
nID = ( nID=joinEndTo[jID] if joinEndTo[jID] != prev else joinStartTo[jID]
joinEndTo[jID]
if joinEndTo[jID] != prev
else joinStartTo[jID]
)
if movedTo[nID] == s: if movedTo[nID] == s:
closedThis=" Z " closedThis=" Z "
proceed=0 proceed=0
else: else:
sub2=old[nID] sub2=old[nID]
if ( if (nID == joinEndTo[jID] and joinStartTo[nID] == jID) or (nID == joinStartTo[jID] and joinEndTo[nID] == jID):
nID == joinEndTo[jID]
and joinStartTo[nID] == jID
) or (
nID == joinStartTo[jID]
and joinEndTo[nID] == jID
):
pass pass
else: else:
rev = not rev rev = not rev
sub2 = reverse_sub(sub2) if rev == True else sub2 sub2=revSub(sub2) if rev == True else sub2
thisSub = join_sub(thisSub, sub2, optionJoin) thisSub=joinSub(thisSub,sub2,self.options.optionJoin)
movedTo[nID]=s movedTo[nID]=s
if joinEndTo[nID] > -1 and joinStartTo[nID] > -1: if joinEndTo[nID] > -1 and joinStartTo[nID] > -1:
prev=jID prev=jID
@ -366,9 +334,9 @@ def remove_duplicate_nodes(
jID=joinStartTo[s] jID=joinStartTo[s]
sub1=old[jID] sub1=old[jID]
rev=True if joinStartToEnd[s] == False else False rev=True if joinStartToEnd[s] == False else False
sub1 = reverse_sub(sub1) if rev == True else sub1 sub1=revSub(sub1) if rev == True else sub1
sub2=thisSub if len(thisSub) > 0 else old[s] sub2=thisSub if len(thisSub) > 0 else old[s]
thisSub = join_sub(sub1, sub2, optionJoin) thisSub=joinSub(sub1,sub2,self.options.optionJoin)
movedTo[jID]=s movedTo[jID]=s
prev=s prev=s
#continue if sub1 joined to more #continue if sub1 joined to more
@ -376,28 +344,18 @@ def remove_duplicate_nodes(
proceed=1 proceed=1
while proceed == 1: while proceed == 1:
nID = ( nID=joinStartTo[jID] if joinStartTo[jID] != prev else joinEndTo[jID]
joinStartTo[jID]
if joinStartTo[jID] != prev
else joinEndTo[jID]
)
if movedTo[nID] == s: if movedTo[nID] == s:
closedThis=" Z " closedThis=" Z "
proceed=0 proceed=0
else: else:
sub1=old[nID] sub1=old[nID]
if ( if (nID == joinEndTo[jID] and joinStartTo[nID] == jID) or (nID == joinStartTo[jID] and joinEndTo[nID] == jID):
nID == joinEndTo[jID]
and joinStartTo[nID] == jID
) or (
nID == joinStartTo[jID]
and joinEndTo[nID] == jID
):
pass pass
else: else:
rev = not rev rev = not rev
sub1 = reverse_sub(sub1) if rev == True else sub1 sub1=revSub(sub1) if rev == True else sub1
thisSub = join_sub(sub1, thisSub, optionJoin) thisSub=joinSub(sub1,thisSub,self.options.optionJoin)
movedTo[nID]=s movedTo[nID]=s
if joinEndTo[nID] > -1 and joinStartTo[nID] > -1: if joinEndTo[nID] > -1 and joinStartTo[nID] > -1:
prev=jID prev=jID
@ -405,120 +363,32 @@ def remove_duplicate_nodes(
else: else:
proceed=0 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) new.append(thisSub)
newClosed.append(closedThis) newClosed.append(closedThis)
else: else:
new.append(old[s]) new.append(old[s])
newClosed.append(closed[s]) newClosed.append(closed[s])
closed=newClosed 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")
pars.add_argument("--minUse", type=inkex.Boolean, default=False)
pars.add_argument("--maxdist", default="0")
pars.add_argument("--joinEnd", type=inkex.Boolean, default=False)
pars.add_argument("--maxdist2", default="0")
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
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
if thisIsPath:
new, closed = remove_duplicate_nodes(
elem,
minlength,
maxdist,
maxdist2,
self.options.allowReverse,
self.options.optionJoin,
)
elem.path = CubicSuperPath(new).to_path(curves_only=True) elem.path = CubicSuperPath(new).to_path(curves_only=True)
# reset z to the originally closed paths #reset z to the originally closed paths (z lost in cubicsuperpath)
# (z lost in cubicsuperpath) temppath=str(elem.path.to_absolute()).split('M ')
temppath = str(elem.path.to_absolute()).split("M ")
temppath.pop(0) temppath.pop(0)
newPath = "" newPath=''
li = 0 l=0
for sub in temppath: for sub in temppath:
newPath = newPath + "M " + temppath[li] + closed[li] newPath=newPath+'M '+temppath[l]+closed[l]
li += 1 l+=1
elem.path=newPath elem.path=newPath
if nFailed > 0: if nFailed > 0:
messagebox.showwarning( 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.')
"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: if nInkEffect > 0:
messagebox.showwarning( 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 .')
"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 __name__ == '__main__':
RemoveDuplicateNodes().run() RemoveDuplicateNodes().run()