From 9fa924d089746158ea4721a4a0e2dcab08f92121 Mon Sep 17 00:00:00 2001 From: su_v Date: Tue, 20 Oct 2015 13:49:11 +0200 Subject: Extensions. Restack - add options to reverse, shuffle Z-order of selection (bug #1498583) Fixed bugs: - https://launchpad.net/bugs/1498583 (bzr r14424) --- share/extensions/restack.inx | 62 ++++++++++++++++++++++++----------- share/extensions/restack.py | 77 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 112 insertions(+), 27 deletions(-) (limited to 'share') diff --git a/share/extensions/restack.inx b/share/extensions/restack.inx index 67f0f777a..4f5f577cb 100644 --- a/share/extensions/restack.inx +++ b/share/extensions/restack.inx @@ -4,25 +4,49 @@ org.inkscape.filter.restack restack.py inkex.py - - <_item value="lr">Left to Right (0) - <_item value="bt">Bottom to Top (90) - <_item value="rl">Right to Left (180) - <_item value="tb">Top to Bottom (270) - <_item value="ro">Radial Outward - <_item value="ri">Radial Inward - <_item value="aa">Arbitrary Angle - - 0.00 - - <_item value="l">Left - <_item value="m">Middle - <_item value="r">Right - - - <_item value="t">Top - <_item value="m">Middle - <_item value="b">Bottom + + + <_param name="desc_dir" type="description" appearance="header">Restack Direction + + + + <_item value="lr">Left to Right (0) + <_item value="bt">Bottom to Top (90) + <_item value="rl">Right to Left (180) + <_item value="tb">Top to Bottom (270) + <_item value="ro">Radial Outward + <_item value="ri">Radial Inward + + + + 0.00 + + + <_param name="desc_ref" type="description" appearance="header">Object Reference Point + + <_item value="l">Left + <_item value="m">Middle + <_item value="r">Right + + + <_item value="t">Top + <_item value="m">Middle + <_item value="b">Bottom + + + + <_param name="desc_zsort" type="description" appearance="header">Restack Mode + + <_item value="rev">Reverse Z-Order + <_item value="rand">Shuffle Z-Order + + + + <_param name="desc_help" type="description">This extension changes the z-order of objects based on their position on the canvas or their current z-order. + +Selection: +The extension restacks either objects inside a single selected group, or a selection of multiple objects on the current drawing level (layer or group). + path diff --git a/share/extensions/restack.py b/share/extensions/restack.py index dc877cd9e..b76e0f765 100755 --- a/share/extensions/restack.py +++ b/share/extensions/restack.py @@ -21,7 +21,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ -import inkex, os, csv, math +import inkex, os, csv, math, random +from pathmodifier import zSort + +inkex.localize() try: from subprocess import Popen, PIPE @@ -48,13 +51,49 @@ class Restack(inkex.Effect): action="store", type="string", dest="yanchor", default="m", help="vertical point to compare") + self.OptionParser.add_option("--zsort", + action="store", type="string", + dest="zsort", default="rev", + help="Restack mode based on Z-Order") + self.OptionParser.add_option("--tab", + action="store", type="string", + dest="tab", + help="The selected UI-tab when OK was pressed") + self.OptionParser.add_option("--nb_direction", + action="store", type="string", + dest="nb_direction", + help="The selected UI-tab when OK was pressed") + def effect(self): - if len( self.selected ) > 0: + if self.options.tab == '"help"': + pass + elif len(self.selected) > 0: + if self.options.tab == '"positional"': + self.restack_positional() + elif self.options.tab == '"z_order"': + self.restack_z_order() + else: + inkex.errormsg(_("There is no selection to restack.")) + + def restack_positional(self): + if 1: # to be removed (workaround for smaller bzr diff) + objects = {} objlist = [] - svg = self.document.getroot() - parentnode = self.current_layer file = self.args[ -1 ] + if self.options.nb_direction == '"custom"': + self.options.direction = "aa" + + # process selection to get list of objects to be arranged + firstobject = self.selected[self.options.ids[0]] + if len(self.selected) == 1 and firstobject.tag == inkex.addNS('g', 'svg'): + parentnode = firstobject + for child in parentnode.iterchildren(): + objects[child.get('id')] = child + else: + parentnode = self.current_layer + objects = self.selected + #get all bounding boxes in file by calling inkscape again with the --query-all command line option #it returns a comma separated list structured id,x,y,w,h if bsubprocess: @@ -81,13 +120,13 @@ class Restack(inkex.Effect): f.close #find the center of all selected objects **Not the average! - x,y,w,h = dimen[self.selected.keys()[0]] + x,y,w,h = dimen[objects.keys()[0]] minx = x miny = y maxx = x + w maxy = y + h - for id, node in self.selected.iteritems(): + for id, node in objects.iteritems(): # get the bounding box x,y,w,h = dimen[id] if x < minx: @@ -103,7 +142,7 @@ class Restack(inkex.Effect): midy = (miny + maxy) / 2 #calculate distances for each selected object - for id, node in self.selected.iteritems(): + for id, node in objects.iteritems(): # get the bounding box x,y,w,h = dimen[id] @@ -144,7 +183,29 @@ class Restack(inkex.Effect): objlist.sort() #move them to the top of the object stack in this order. for item in objlist: - parentnode.append( self.selected[item[1]]) + parentnode.append( objects[item[1]]) + + def restack_z_order(self): + parentnode = None + objects = [] + if len(self.selected) == 1: + firstobject = self.selected[self.options.ids[0]] + if firstobject.tag == inkex.addNS('g', 'svg'): + parentnode = firstobject + for child in parentnode.iterchildren(reversed=False): + objects.append(child) + else: + parentnode = self.current_layer + for id_ in zSort(self.document.getroot(), self.selected.keys()): + objects.append(self.selected[id_]) + if self.options.zsort == "rev": + objects.reverse() + elif self.options.zsort == "rand": + random.shuffle(objects) + if parentnode is not None: + for item in objects: + parentnode.append(item) + if __name__ == '__main__': e = Restack() -- cgit v1.2.3 From 87d0a984dec7a5f4cfce15a38b365f162ad21c49 Mon Sep 17 00:00:00 2001 From: su_v Date: Tue, 20 Oct 2015 14:09:50 +0200 Subject: Extensions. Restack - fix indentation level (follow-up to rev 14424, bug #1498583) (bzr r14425) --- share/extensions/restack.py | 215 ++++++++++++++++++++++---------------------- 1 file changed, 107 insertions(+), 108 deletions(-) (limited to 'share') diff --git a/share/extensions/restack.py b/share/extensions/restack.py index b76e0f765..67d738a13 100755 --- a/share/extensions/restack.py +++ b/share/extensions/restack.py @@ -76,114 +76,113 @@ class Restack(inkex.Effect): inkex.errormsg(_("There is no selection to restack.")) def restack_positional(self): - if 1: # to be removed (workaround for smaller bzr diff) - objects = {} - objlist = [] - file = self.args[ -1 ] - - if self.options.nb_direction == '"custom"': - self.options.direction = "aa" - - # process selection to get list of objects to be arranged - firstobject = self.selected[self.options.ids[0]] - if len(self.selected) == 1 and firstobject.tag == inkex.addNS('g', 'svg'): - parentnode = firstobject - for child in parentnode.iterchildren(): - objects[child.get('id')] = child - else: - parentnode = self.current_layer - objects = self.selected - - #get all bounding boxes in file by calling inkscape again with the --query-all command line option - #it returns a comma separated list structured id,x,y,w,h - if bsubprocess: - p = Popen('inkscape --query-all "%s"' % (file), shell=True, stdout=PIPE, stderr=PIPE) - err = p.stderr - f = p.communicate()[0] - try: - reader=csv.CSVParser().parse_string(f) #there was a module cvs.py in earlier inkscape that behaved differently - except: - reader=csv.reader(f.split( os.linesep )) - err.close() - else: - _,f,err = os.popen3('inkscape --query-all "%s"' % ( file ) ) - reader=csv.reader( f ) - err.close() - - #build a dictionary with id as the key - dimen = dict() - for line in reader: - if len(line) > 0: - dimen[line[0]] = map( float, line[1:]) - - if not bsubprocess: #close file if opened using os.popen3 - f.close - - #find the center of all selected objects **Not the average! - x,y,w,h = dimen[objects.keys()[0]] - minx = x - miny = y - maxx = x + w - maxy = y + h - - for id, node in objects.iteritems(): - # get the bounding box - x,y,w,h = dimen[id] - if x < minx: - minx = x - if (x + w) > maxx: - maxx = x + w - if y < miny: - miny = y - if (y + h) > maxy: - maxy = y + h - - midx = (minx + maxx) / 2 - midy = (miny + maxy) / 2 - - #calculate distances for each selected object - for id, node in objects.iteritems(): - # get the bounding box - x,y,w,h = dimen[id] - - # calc the comparison coords - if self.options.xanchor == "l": - cx = x - elif self.options.xanchor == "r": - cx = x + w - else: # middle - cx = x + w / 2 - - if self.options.yanchor == "t": - cy = y - elif self.options.yanchor == "b": - cy = y + h - else: # middle - cy = y + h / 2 - - #direction chosen - if self.options.direction == "tb" or (self.options.direction == "aa" and self.options.angle == 270): - objlist.append([cy,id]) - elif self.options.direction == "bt" or (self.options.direction == "aa" and self.options.angle == 90): - objlist.append([-cy,id]) - elif self.options.direction == "lr" or (self.options.direction == "aa" and (self.options.angle == 0 or self.options.angle == 360)): - objlist.append([cx,id]) - elif self.options.direction == "rl" or (self.options.direction == "aa" and self.options.angle == 180): - objlist.append([-cx,id]) - elif self.options.direction == "aa": - distance = math.hypot(cx,cy)*(math.cos(math.radians(-self.options.angle)-math.atan2(cy, cx))) - objlist.append([distance,id]) - elif self.options.direction == "ro": - distance = math.hypot(midx - cx, midy - cy) - objlist.append([distance,id]) - elif self.options.direction == "ri": - distance = -math.hypot(midx - cx, midy - cy) - objlist.append([distance,id]) - - objlist.sort() - #move them to the top of the object stack in this order. - for item in objlist: - parentnode.append( objects[item[1]]) + objects = {} + objlist = [] + file = self.args[ -1 ] + + if self.options.nb_direction == '"custom"': + self.options.direction = "aa" + + # process selection to get list of objects to be arranged + firstobject = self.selected[self.options.ids[0]] + if len(self.selected) == 1 and firstobject.tag == inkex.addNS('g', 'svg'): + parentnode = firstobject + for child in parentnode.iterchildren(): + objects[child.get('id')] = child + else: + parentnode = self.current_layer + objects = self.selected + + #get all bounding boxes in file by calling inkscape again with the --query-all command line option + #it returns a comma separated list structured id,x,y,w,h + if bsubprocess: + p = Popen('inkscape --query-all "%s"' % (file), shell=True, stdout=PIPE, stderr=PIPE) + err = p.stderr + f = p.communicate()[0] + try: + reader=csv.CSVParser().parse_string(f) #there was a module cvs.py in earlier inkscape that behaved differently + except: + reader=csv.reader(f.split( os.linesep )) + err.close() + else: + _,f,err = os.popen3('inkscape --query-all "%s"' % ( file ) ) + reader=csv.reader( f ) + err.close() + + #build a dictionary with id as the key + dimen = dict() + for line in reader: + if len(line) > 0: + dimen[line[0]] = map( float, line[1:]) + + if not bsubprocess: #close file if opened using os.popen3 + f.close + + #find the center of all selected objects **Not the average! + x,y,w,h = dimen[objects.keys()[0]] + minx = x + miny = y + maxx = x + w + maxy = y + h + + for id, node in objects.iteritems(): + # get the bounding box + x,y,w,h = dimen[id] + if x < minx: + minx = x + if (x + w) > maxx: + maxx = x + w + if y < miny: + miny = y + if (y + h) > maxy: + maxy = y + h + + midx = (minx + maxx) / 2 + midy = (miny + maxy) / 2 + + #calculate distances for each selected object + for id, node in objects.iteritems(): + # get the bounding box + x,y,w,h = dimen[id] + + # calc the comparison coords + if self.options.xanchor == "l": + cx = x + elif self.options.xanchor == "r": + cx = x + w + else: # middle + cx = x + w / 2 + + if self.options.yanchor == "t": + cy = y + elif self.options.yanchor == "b": + cy = y + h + else: # middle + cy = y + h / 2 + + #direction chosen + if self.options.direction == "tb" or (self.options.direction == "aa" and self.options.angle == 270): + objlist.append([cy,id]) + elif self.options.direction == "bt" or (self.options.direction == "aa" and self.options.angle == 90): + objlist.append([-cy,id]) + elif self.options.direction == "lr" or (self.options.direction == "aa" and (self.options.angle == 0 or self.options.angle == 360)): + objlist.append([cx,id]) + elif self.options.direction == "rl" or (self.options.direction == "aa" and self.options.angle == 180): + objlist.append([-cx,id]) + elif self.options.direction == "aa": + distance = math.hypot(cx,cy)*(math.cos(math.radians(-self.options.angle)-math.atan2(cy, cx))) + objlist.append([distance,id]) + elif self.options.direction == "ro": + distance = math.hypot(midx - cx, midy - cy) + objlist.append([distance,id]) + elif self.options.direction == "ri": + distance = -math.hypot(midx - cx, midy - cy) + objlist.append([distance,id]) + + objlist.sort() + #move them to the top of the object stack in this order. + for item in objlist: + parentnode.append( objects[item[1]]) def restack_z_order(self): parentnode = None -- cgit v1.2.3