Code

Add extension to render a polar grid. Wishlist - LP #224522
authorozmikepittman <ozmikepittman@users.sourceforge.net>
Wed, 30 Apr 2008 15:44:34 +0000 (15:44 +0000)
committerozmikepittman <ozmikepittman@users.sourceforge.net>
Wed, 30 Apr 2008 15:44:34 +0000 (15:44 +0000)
share/extensions/grid_polar.inx [new file with mode: 0644]
share/extensions/grid_polar.py [new file with mode: 0644]

diff --git a/share/extensions/grid_polar.inx b/share/extensions/grid_polar.inx
new file mode 100644 (file)
index 0000000..6425af6
--- /dev/null
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">\r
+    <_name>Polar Grid</_name>\r
+    <id>grids.polar</id>\r
+    <dependency type="executable" location="extensions">grid_polar.py</dependency>\r
+    <dependency type="executable" location="extensions">inkex.py</dependency>\r
+    <param name="r_divs"       type="int"   min="1" max="1000" _gui-text="Major Circular Divisions">5</param>\r
+    <param name="dr"           type="float" min="1" max="1000" _gui-text="Major Circular Divsion Spacing / px">50.0</param>\r
+    <param name="r_subdivs"    type="int"   min="1" max="1000" _gui-text="Subdivisions per Major Circular Division">3</param>\r
+    <param name="r_log"        type="boolean" min="0" max="1"   _gui-text="Logarithmic Subdiv. (Base given by entry above)"></param>\r
+    <param name="r_divs_th"    type="float" min="0" max="1000" _gui-text="Major Circular Division Thickness / px">2</param>\r
+    <param name="r_subdivs_th" type="float" min="0" max="1000" _gui-text="Minor Circular Division Thickness / px">1</param>\r
+    <param name="a_divs"       type="int"   min="1" max="1000" _gui-text="Angle Divisions">24</param>\r
+    <param name="a_divs_cent"  type="int"   min="1" max="1000" _gui-text="Angle Divisions at Centre">4</param>\r
+    <param name="a_subdivs"    type="int"   min="1" max="1000" _gui-text="Subdivisions per Major Angular Division">1</param>\r
+    <param name="a_subdivs_cent"  type="int"   min="0" max="1000" _gui-text="Minor Angle Division End 'n' Divs. Before Centre">2</param>\r
+    <param name="a_divs_th"    type="float" min="0" max="1000" _gui-text="Major Angular Division Thickness / px">2</param>\r
+    <param name="a_subdivs_th" type="float" min="0" max="1000" _gui-text="Minor Angular Division Thickness / px">1</param>\r
+    <param name="c_dot_dia"    type="int"   min="1" max="1000" _gui-text="Centre Dot Diameter / px">5.0</param>\r
+    <param name="a_labels"     type="optiongroup"              _gui-text="Circumferential Labels">\r
+        <_option value="none">None</_option>\r
+        <_option value="deg">Degrees</_option></param>\r
+    <param name="a_label_size"   type="int"   min="1" max="1000" _gui-text="Circumferential Label Size / px">18</param>\r
+    <param name="a_label_outset" type="float" min="0" max="1000" _gui-text="Circumferential Label Outset / px">24</param>\r
+    \r
+    <effect>\r
+        <object-type>all</object-type>\r
+                <effects-menu>\r
+                    <submenu _name="Render"/>\r
+                </effects-menu>\r
+    </effect>\r
+    <script>\r
+        <command reldir="extensions" interpreter="python">grid_polar.py</command>\r
+    </script>\r
+</inkscape-extension>
\ No newline at end of file
diff --git a/share/extensions/grid_polar.py b/share/extensions/grid_polar.py
new file mode 100644 (file)
index 0000000..93871f1
--- /dev/null
@@ -0,0 +1,201 @@
+#!/usr/bin/env python 
+'''
+Copyright (C) 2007 John Beard john.j.beard@gmail.com
+
+##This extension allows you to draw a polar grid in Inkscape.
+##There is a wide range of options including subdivision and labels.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+'''\r
+
+import inkex
+import simplestyle, sys
+from math import *
+
+def log_N(base, x): #computes the base-n log of x
+    return log(x)/log(base)
+
+def draw_SVG_ellipse(rx, ry, cx, cy, width, fill, name, parent):
+    style = { 'stroke': '#000000', 'stroke-width':str(width), 'fill': fill }
+    circ_attribs = {'style':simplestyle.formatStyle(style),
+                    'inkscape:label':name,
+                    'sodipodi:cx':str(cx), 'sodipodi:cy':str(cy), 
+                    'sodipodi:rx':str(rx), 'sodipodi:ry':str(ry), 
+                    'sodipodi:type':'arc'}
+    inkex.etree.SubElement(parent, inkex.addNS('path','svg'), circ_attribs )
+
+def draw_SVG_line(x1, y1, x2, y2, width, name, parent):
+    style = { 'stroke': '#000000', 'stroke-width':str(width), 'fill': 'none' }
+    line_attribs = {'style':simplestyle.formatStyle(style),
+                    'inkscape:label':name,
+                    'd':'M '+str(x1)+','+str(y1)+' L '+str(x2)+','+str(y2)}
+    inkex.etree.SubElement(parent, inkex.addNS('path','svg'), line_attribs )
+    
+def draw_SVG_label_centred(x, y, string, font_size, name, parent):
+    style = {'text-align': 'center', 'vertical-align': 'top',
+             'text-anchor': 'middle', 'font-size': str(font_size)+'px',
+             'fill-opacity': '1.0', 'stroke': 'none',
+             'font-weight': 'normal', 'font-style': 'normal', 'fill': '#000000'}
+    label_attribs = {'style':simplestyle.formatStyle(style),
+                     'inkscape:label':name,
+                     'x':str(x), 'y':str(y)}
+    label = inkex.etree.SubElement(parent, inkex.addNS('text','svg'), label_attribs)
+    label.text = string
+
+class Grid_Polar(inkex.Effect):
+    def __init__(self):
+        inkex.Effect.__init__(self)
+        self.OptionParser.add_option("--r_divs",
+                        action="store", type="int", 
+                        dest="r_divs", default=5,
+                        help="Circular Divisions")
+        self.OptionParser.add_option("--dr",
+                        action="store", type="float", 
+                        dest="dr", default=50,
+                        help="Circular Division Spacing")
+        self.OptionParser.add_option("--r_subdivs",
+                        action="store", type="int", 
+                        dest="r_subdivs", default=3,
+                        help="Circular Subdivisions per Major division")
+        self.OptionParser.add_option("--r_log",
+                        action="store", type="inkbool", 
+                        dest="r_log", default=False,
+                        help="Logarithmic subdivisions if true")
+        self.OptionParser.add_option("--r_divs_th",
+                        action="store", type="float", 
+                        dest="r_divs_th", default=2,
+                        help="Major Circular Division Line thickness")
+        self.OptionParser.add_option("--r_subdivs_th",
+                        action="store", type="float", 
+                        dest="r_subdivs_th", default=1,
+                        help="Minor Circular Division Line thickness")
+        self.OptionParser.add_option("--a_divs",
+                        action="store", type="int", 
+                        dest="a_divs", default=24,
+                        help="Angle Divisions")
+        self.OptionParser.add_option("--a_divs_cent",
+                        action="store", type="int", 
+                        dest="a_divs_cent", default=4,
+                        help="Angle Divisions at Centre")
+        self.OptionParser.add_option("--a_subdivs",
+                        action="store", type="int", 
+                        dest="a_subdivs", default=1,
+                        help="Angcular Subdivisions per Major division")
+        self.OptionParser.add_option("--a_subdivs_cent",
+                        action="store", type="int", 
+                        dest="a_subdivs_cent", default=1,
+                        help="Angular Subdivisions end 'n' major circular divisions before the centre")
+        self.OptionParser.add_option("--a_divs_th",
+                        action="store", type="float", 
+                        dest="a_divs_th", default=2,
+                        help="Major Angular Division Line thickness")
+        self.OptionParser.add_option("--a_subdivs_th",
+                        action="store", type="float", 
+                        dest="a_subdivs_th", default=1,
+                        help="Minor Angular Division Line thickness")
+        self.OptionParser.add_option("--c_dot_dia",
+                        action="store", type="float", 
+                        dest="c_dot_dia", default=5.0,
+                        help="Diameter of Centre Dot")
+        self.OptionParser.add_option("--a_labels",
+                        action="store", type="string", 
+                        dest="a_labels", default='deg',
+                        help="The kind of labels to apply")
+        self.OptionParser.add_option("--a_label_size",
+                        action="store", type="int", 
+                        dest="a_label_size", default=18,
+                        help="The nominal pixel size of the circumferential labels")
+        self.OptionParser.add_option("--a_label_outset",
+                        action="store", type="float", 
+                        dest="a_label_outset", default=24,
+                        help="The radial outset of the circumferential labels")
+
+    def effect(self):
+    
+        # Embed grid in group
+        #Put in in the centre of the current view
+        t = 'translate(' + str( self.view_center[0] ) + ',' + str( self.view_center[1] ) + ')'
+        g_attribs = {inkex.addNS('label','inkscape'):'Grid_Polar:R' +
+                                 str( self.options.r_divs )+':A'+str( self.options.a_divs ),
+                   'transform':t }
+        grid = inkex.etree.SubElement(self.current_layer, 'g', g_attribs)
+
+        dr = self.options.dr                        #Distance between neighbouring circles
+        dtheta = 2 * pi / self.options.a_divs_cent  #Angular change between adjacent radial lines at centre
+        rmax = self.options.r_divs * dr
+        
+        #Create SVG circles
+        for i in range(1, self.options.r_divs+1):
+            draw_SVG_ellipse(i*dr, i*dr, 0, 0, #major div circles
+                             self.options.r_divs_th, 'none',
+                             'MajorDivCircle'+str(i)+':R'+str(i*dr), grid)
+            
+            if self.options.r_log: #logarithmic subdivisions
+                for j in range (2, self.options.r_subdivs):
+                    draw_SVG_ellipse(i*dr-(1-log_N(self.options.r_subdivs, j))*dr, #minor div circles
+                                     i*dr-(1-log_N(self.options.r_subdivs, j))*dr, 0, 0, 
+                                     self.options.r_subdivs_th, 'none',
+                                     'MinorDivCircle'+str(i)+':Log'+str(j), grid)
+            else: #linear subdivs
+                for j in range (1, self.options.r_subdivs):
+                    draw_SVG_ellipse(i*dr-j*dr/self.options.r_subdivs, #minor div circles
+                                     i*dr-j*dr/self.options.r_subdivs, 0, 0, 
+                                     self.options.r_subdivs_th, 'none',
+                                     'MinorDivCircle'+str(i)+':R'+str(i*dr), grid)
+        
+        if self.options.a_divs == self.options.a_divs_cent: #the lines can go from the centre to the edge
+            for i in range(0, self.options.a_divs):
+                draw_SVG_line(0, 0, rmax*sin(i*dtheta), rmax*cos(i*dtheta), 
+                              self.options.a_divs_th, 'RadialGridline'+str(i), grid)
+        
+        else: #we need separate lines
+            for i in range(0, self.options.a_divs_cent): #lines that go to the first circle
+                draw_SVG_line(0, 0, dr*sin(i*dtheta), dr*cos(i*dtheta), 
+                              self.options.a_divs_th, 'RadialGridline'+str(i), grid)
+        
+            dtheta = 2 * pi / self.options.a_divs #work out the angle change for outer lines
+            
+            for i in range(0, self.options.a_divs): #lines that go from there to the edge
+                draw_SVG_line(  dr*sin(i*dtheta+pi/2.0),   dr*cos(i*dtheta+pi/2.0), 
+                              rmax*sin(i*dtheta+pi/2.0), rmax*cos(i*dtheta+pi/2.0), 
+                              self.options.a_divs_th, 'RadialGridline'+str(i), grid)
+        
+        if self.options.a_subdivs > 1: #draw angular subdivs
+            for i in range(0, self.options.a_divs): #for each major divison
+                for j in range(1, self.options.a_subdivs): #draw the subdivisions
+                    angle = i*dtheta-j*dtheta/self.options.a_subdivs+pi/2.0 # the angle of the subdivion line
+                    draw_SVG_line(dr*self.options.a_subdivs_cent*sin(angle),
+                                  dr*self.options.a_subdivs_cent*cos(angle), 
+                                  rmax*sin(angle), rmax*cos(angle), 
+                                  self.options.a_subdivs_th, 'RadialMinorGridline'+str(i), grid)
+        
+        if self.options.c_dot_dia <> 0: #if a non-zero diameter, draw the centre dot
+            draw_SVG_ellipse(self.options.c_dot_dia /2.0, self.options.c_dot_dia/2.0,
+                             0, 0, 0, '#000000', 'CentreDot', grid)
+        
+        if self.options.a_labels == 'deg':
+            label_radius = rmax+self.options.a_label_outset  #radius of label centres
+            label_size = self.options.a_label_size
+            numeral_size = 0.73*label_size #numerals appear to be 0.73 the height of the nominal pixel size of the font in "Sans"
+            
+            for i in range(0, self.options.a_divs):#self.options.a_divs): #radial line labels
+                draw_SVG_label_centred(sin(i*dtheta+pi/2.0)*label_radius,        #0 at the RHS, mathematical style
+                                       cos(i*dtheta+pi/2.0)*label_radius+ numeral_size/2.0, #centre the text vertically 
+                                       str(i*360/self.options.a_divs), 
+                                       label_size, 'Label'+str(i), grid)
+
+e = Grid_Polar()
+e.affect()
+