summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--po/POTFILES.in3
-rw-r--r--share/extensions/measure.inx7
-rwxr-xr-xshare/extensions/measure.py86
-rw-r--r--src/color-profile.cpp10
4 files changed, 78 insertions, 28 deletions
diff --git a/po/POTFILES.in b/po/POTFILES.in
index af73041bd..082321236 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,6 +1,6 @@
# List of source files containing translatable strings.
# Please keep this file sorted alphabetically.
-# Generated by ./generate_POTFILES.sh at Fri Mar 28 00:52:30 CET 2014
+# Generated by ./generate_POTFILES.sh at Thu Apr 3 00:36:24 CEST 2014
[encoding: UTF-8]
inkscape.desktop.in
share/filters/filters.svg.h
@@ -385,6 +385,7 @@ share/extensions/jessyInk_video.py
share/extensions/jessyInk_view.py
share/extensions/launch_webbrowser.py
share/extensions/markers_strokepaint.py
+share/extensions/measure.py
share/extensions/pathalongpath.py
share/extensions/pathmodifier.py
share/extensions/pathscatter.py
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',
diff --git a/src/color-profile.cpp b/src/color-profile.cpp
index ed4b9029e..aa0750c00 100644
--- a/src/color-profile.cpp
+++ b/src/color-profile.cpp
@@ -322,17 +322,15 @@ void ColorProfile::set(unsigned key, gchar const *value) {
}
//# 1. Get complete URI of document
gchar const *docbase = doc->getURI();
- if (!docbase)
- {
- // Normal for files that have not yet been saved.
- docbase = "";
- }
gchar* escaped = g_uri_escape_string(this->href, "!*'();:@=+$,/?#[]", TRUE);
//g_message("docbase:%s\n", docbase);
//org::w3c::dom::URI docUri(docbase);
- Inkscape::URI docUri(docbase);
+ Inkscape::URI docUri("");
+ if (docbase) { // The file has already been saved
+ docUri = Inkscape::URI::from_native_filename(docbase);
+ }
//# 2. Get href of icc file. we don't care if it's rel or abs
//org::w3c::dom::URI hrefUri(escaped);