diff options
| author | Liam P. White <inkscapebronyat-signgmaildotcom> | 2014-04-03 02:15:12 +0000 |
|---|---|---|
| committer | Liam P. White <inkscapebronyat-signgmaildotcom> | 2014-04-03 02:15:12 +0000 |
| commit | b6f77fe152a8d73055c8484b2839ade02dc2a4bb (patch) | |
| tree | f48d65c27a9e2bb464d2f35eb9b2b4c29f59c34b /share | |
| parent | Fix some minor issues (diff) | |
| parent | Fix make check (diff) | |
| download | inkscape-b6f77fe152a8d73055c8484b2839ade02dc2a4bb.tar.gz inkscape-b6f77fe152a8d73055c8484b2839ade02dc2a4bb.zip | |
Update to trunk
(bzr r13090.1.45)
Diffstat (limited to 'share')
| -rw-r--r-- | share/extensions/measure.inx | 7 | ||||
| -rwxr-xr-x | share/extensions/measure.py | 86 |
2 files changed, 72 insertions, 21 deletions
diff --git a/share/extensions/measure.inx b/share/extensions/measure.inx index cfbc3fce7..8fae020e0 100644 --- a/share/extensions/measure.inx +++ b/share/extensions/measure.inx @@ -9,6 +9,7 @@ <param name="type" type="enum" _gui-text="Measurement Type: "> <_item value="length">Length</_item> <_item msgctxt="measure extension" value="area">Area</_item> + <_item msgctxt="measure extension" value="cofm">Center of Mass</_item> </param> <param name="format" type="enum" _gui-text="Text Orientation: "> <_item msgctxt="measure extension" value="textonpath">Text On Path</_item> @@ -32,9 +33,9 @@ </param> </page> <page name="desc" _gui-text="Help"> - <_param name="measurehelp" type="description" xml:space="preserve">This effect measures the length, or area, of the selected paths and adds it as a text object with the selected units. - - * Display format can be either Text-On-Path, or stand-alone text at a specified angle. + <_param name="measurehelp" type="description" xml:space="preserve">This effect measures the length, area, or center-of-mass of the selected paths. Length and area are added as a text object with the selected units. Center-of-mass is shown as a cross symbol. + + * Text display format can be either Text-On-Path, or stand-alone text at a specified angle. * The number of significant digits can be controlled by the Precision field. * The Offset field controls the distance from the text to the path. * The Scale factor can be used to make measurements in scaled drawings. For example, if 1 cm in the drawing equals 2.5 m in the real world, Scale must be set to 250. diff --git a/share/extensions/measure.py b/share/extensions/measure.py index ca471816f..5c709594f 100755 --- a/share/extensions/measure.py +++ b/share/extensions/measure.py @@ -1,8 +1,9 @@ #!/usr/bin/env python ''' This extension module can measure arbitrary path and object length -It adds a text to the selected path containing the length in a -given unit. +It adds text to the selected path containing the length in a given unit. +Area and Center of Mass calculated using Green's Theorem: +http://mathworld.wolfram.com/GreensTheorem.html Copyright (C) 2010 Alvin Penner Copyright (C) 2006 Georg Wiora @@ -30,10 +31,31 @@ TODO: 2. check direction >90 or <-90 Degrees 3. rotate by 180 degrees around text center ''' -import inkex, simplestyle, simplepath, simpletransform, sys, cubicsuperpath, bezmisc, locale -# Set current system locale +# standard library +import locale +# local library +import inkex +import simplestyle +import simpletransform +import cubicsuperpath +import bezmisc + +inkex.localize() locale.setlocale(locale.LC_ALL, '') +# third party +try: + import numpy +except: + inkex.errormsg(_("Failed to import the numpy modules. These modules are required by this extension. Please install them and try again. On a Debian-like system this can be done with the command, sudo apt-get install python-numpy.")) + exit() + +mat_area = numpy.matrix([[ 0, 2, 1, -3],[ -2, 0, 1, 1],[ -1, -1, 0, 2],[ 3, -1, -2, 0]]) +mat_cofm_0 = numpy.matrix([[ 0, 35, 10,-45],[-35, 0, 12, 23],[-10,-12, 0, 22],[ 45,-23,-22, 0]]) +mat_cofm_1 = numpy.matrix([[ 0, 15, 3,-18],[-15, 0, 9, 6],[ -3, -9, 0, 12],[ 18, -6,-12, 0]]) +mat_cofm_2 = numpy.matrix([[ 0, 12, 6,-18],[-12, 0, 9, 3],[ -6, -9, 0, 15],[ 18, -3,-15, 0]]) +mat_cofm_3 = numpy.matrix([[ 0, 22, 23,-45],[-22, 0, 12, 10],[-23,-12, 0, 35],[ 45,-10,-35, 0]]) + def numsegs(csp): return sum([len(p)-1 for p in csp]) def interpcoord(v1,v2,p): @@ -69,20 +91,37 @@ def csplength(csp): for i in xrange(1,len(sp)): l = cspseglength(sp[i-1],sp[i]) lengths[-1].append(l) - total += l + total += l return lengths, total def csparea(csp): area = 0.0 for sp in csp: + if len(sp) < 2: continue for i in range(len(sp)): # calculate polygon area area += 0.5*sp[i-1][1][0]*(sp[i][1][1] - sp[i-2][1][1]) for i in range(1, len(sp)): # add contribution from cubic Bezier - bezarea = ( 0.0*sp[i-1][1][1] + 2.0*sp[i-1][2][1] + 1.0*sp[i][0][1] - 3.0*sp[i][1][1])*sp[i-1][1][0] - bezarea += (-2.0*sp[i-1][1][1] + 0.0*sp[i-1][2][1] + 1.0*sp[i][0][1] + 1.0*sp[i][1][1])*sp[i-1][2][0] - bezarea += (-1.0*sp[i-1][1][1] - 1.0*sp[i-1][2][1] + 0.0*sp[i][0][1] + 2.0*sp[i][1][1])*sp[i][0][0] - bezarea += ( 3.0*sp[i-1][1][1] - 1.0*sp[i-1][2][1] - 2.0*sp[i][0][1] + 0.0*sp[i][1][1])*sp[i][1][0] - area += 0.15*bezarea - return abs(area) + vec_x = numpy.matrix([sp[i-1][1][0], sp[i-1][2][0], sp[i][0][0], sp[i][1][0]]) + vec_y = numpy.matrix([sp[i-1][1][1], sp[i-1][2][1], sp[i][0][1], sp[i][1][1]]) + area += 0.15*(vec_x*mat_area*vec_y.T)[0,0] + return -area # require positive area for CCW +def cspcofm(csp): + area = csparea(csp) + xc = 0.0 + yc = 0.0 + if abs(area) < 1.e-8: + inkex.errormsg(_("Area is zero, cannot calculate Center of Mass")) + return 0, 0 + for sp in csp: + for i in range(len(sp)): # calculate polygon moment + xc += sp[i-1][1][1]*(sp[i-2][1][0] - sp[i][1][0])*(sp[i-2][1][0] + sp[i-1][1][0] + sp[i][1][0])/6 + yc += sp[i-1][1][0]*(sp[i][1][1] - sp[i-2][1][1])*(sp[i-2][1][1] + sp[i-1][1][1] + sp[i][1][1])/6 + for i in range(1, len(sp)): # add contribution from cubic Bezier + vec_x = numpy.matrix([sp[i-1][1][0], sp[i-1][2][0], sp[i][0][0], sp[i][1][0]]) + vec_y = numpy.matrix([sp[i-1][1][1], sp[i-1][2][1], sp[i][0][1], sp[i][1][1]]) + vec_t = numpy.matrix([(vec_x*mat_cofm_0*vec_y.T)[0,0], (vec_x*mat_cofm_1*vec_y.T)[0,0], (vec_x*mat_cofm_2*vec_y.T)[0,0], (vec_x*mat_cofm_3*vec_y.T)[0,0]]) + xc += (vec_x*vec_t.T)[0,0]/280 + yc += (vec_y*vec_t.T)[0,0]/280 + return -xc/area, -yc/area def appendSuperScript(node, text): super = inkex.etree.SubElement(node, inkex.addNS('tspan', 'svg'), {'style': 'font-size:65%;baseline-shift:super'}) super.text = text @@ -133,12 +172,13 @@ class Length(inkex.Effect): self.OptionParser.add_option("--measurehelp", action="store", type="string", dest="measurehelp", default="", - help="dummy") - + help="dummy") + def effect(self): # get number of digits prec = int(self.options.precision) - self.options.offset *= self.unittouu('1px') + scale = self.unittouu('1px') # convert to document units + self.options.offset *= scale factor = 1.0 doc = self.document.getroot() if doc.get('viewBox'): @@ -150,16 +190,21 @@ class Length(inkex.Effect): # loop over all selected paths for id, node in self.selected.iteritems(): if node.tag == inkex.addNS('path','svg'): - self.group = inkex.etree.SubElement(node.getparent(),inkex.addNS('text','svg')) - a =[] mat = simpletransform.composeParents(node, [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]) p = cubicsuperpath.parsePath(node.get('d')) simpletransform.applyTransformToPath(mat, p) - factor *= self.unittouu('1px')/self.unittouu('1'+self.options.unit) + factor *= scale/self.unittouu('1'+self.options.unit) if self.options.type == "length": slengths, stotal = csplength(p) - else: + self.group = inkex.etree.SubElement(node.getparent(),inkex.addNS('text','svg')) + elif self.options.type == "area": stotal = csparea(p)*factor*self.options.scale + self.group = inkex.etree.SubElement(node.getparent(),inkex.addNS('text','svg')) + else: + xc, yc = cspcofm(p) + self.group = inkex.etree.SubElement(node.getparent(),inkex.addNS('path','svg')) + self.addCross(self.group, xc, yc, scale) + continue # Format the length as string lenstr = locale.format("%(len)25."+str(prec)+"f",{'len':round(stotal*factor*self.options.scale,prec)}).strip() if self.options.format == 'textonpath': @@ -173,6 +218,11 @@ class Length(inkex.Effect): else: self.addTextWithTspan(self.group, p[0][0][1][0], p[0][0][1][1], lenstr+' '+self.options.unit+'^2', id, 'start', -int(self.options.angle), -self.options.offset + self.options.fontsize/2) + def addCross(self, node, x, y, scale): + l = 3*scale # 3 pixels in document units + node.set('d', 'm %s,%s %s,0 m %s,%s 0,%s' % (str(x-l), str(y), str(2*l), str(-l), str(-l), str(2*l))) + node.set('style', 'stroke:#000000;stroke-width:%s' % str(0.5*scale)) + def addTextOnPath(self, node, x, y, text, id, anchor, startOffset, dy = 0): new = inkex.etree.SubElement(node,inkex.addNS('textPath','svg')) s = {'text-align': 'center', 'vertical-align': 'bottom', |
