1 #!/usr/bin/env python
2 '''
3 Copyright (C) 2007 John Beard john.j.beard@gmail.com
5 ##This extension allows you to draw a Cartesian grid in Inkscape.
6 ##There is a wide range of options including subdivision, subsubdivions
7 ## and logarithmic scales. Custom line widths are also possible.
8 ##All elements are grouped with similar elements (eg all x-subdivs)
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 '''
25 import inkex
26 import simplestyle, sys
27 from math import *
29 def draw_SVG_line(x1, y1, x2, y2, width, name, parent):
30 style = { 'stroke': '#000000', 'stroke-width':str(width), 'fill': 'none' }
31 line_attribs = {'style':simplestyle.formatStyle(style),
32 inkex.addNS('label','inkscape'):name,
33 'd':'M '+str(x1)+','+str(y1)+' L '+str(x2)+','+str(y2)}
34 inkex.etree.SubElement(parent, inkex.addNS('path','svg'), line_attribs )
36 def draw_SVG_rect(x,y,w,h, width, fill, name, parent):
37 style = { 'stroke': '#000000', 'stroke-width':str(width), 'fill':fill}
38 rect_attribs = {'style':simplestyle.formatStyle(style),
39 inkex.addNS('label','inkscape'):name,
40 'x':str(x), 'y':str(y), 'width':str(w), 'height':str(h)}
41 inkex.etree.SubElement(parent, inkex.addNS('rect','svg'), rect_attribs )
43 class Grid_Polar(inkex.Effect):
44 def __init__(self):
45 inkex.Effect.__init__(self)
46 self.OptionParser.add_option("--x_divs",
47 action="store", type="int",
48 dest="x_divs", default=5,
49 help="Major X Divisions")
50 self.OptionParser.add_option("--dx",
51 action="store", type="float",
52 dest="dx", default=100.0,
53 help="Major X divison Spacing")
54 self.OptionParser.add_option("--x_subdivs",
55 action="store", type="int",
56 dest="x_subdivs", default=2,
57 help="Subdivisions per Major X division")
58 self.OptionParser.add_option("--x_log",
59 action="store", type="inkbool",
60 dest="x_log", default=False,
61 help="Logarithmic x subdivisions if true")
62 self.OptionParser.add_option("--x_subsubdivs",
63 action="store", type="int",
64 dest="x_subsubdivs", default=5,
65 help="Subsubdivisions per Minor X division")
66 self.OptionParser.add_option("--x_half_freq",
67 action="store", type="int",
68 dest="x_half_freq", default=4,
69 help="Halve Subsubdiv. Frequency after 'n' Subdivs. (log only)")
70 self.OptionParser.add_option("--x_divs_th",
71 action="store", type="float",
72 dest="x_divs_th", default=2,
73 help="Major X Division Line thickness")
74 self.OptionParser.add_option("--x_subdivs_th",
75 action="store", type="float",
76 dest="x_subdivs_th", default=1,
77 help="Minor X Division Line thickness")
78 self.OptionParser.add_option("--x_subsubdivs_th",
79 action="store", type="float",
80 dest="x_subsubdivs_th", default=1,
81 help="Subminor X Division Line thickness")
82 self.OptionParser.add_option("--y_divs",
83 action="store", type="int",
84 dest="y_divs", default=6,
85 help="Major Y Divisions")
86 self.OptionParser.add_option("--dy",
87 action="store", type="float",
88 dest="dy", default=100.0,
89 help="Major Gridline Increment")
90 self.OptionParser.add_option("--y_subdivs",
91 action="store", type="int",
92 dest="y_subdivs", default=2,
93 help="Minor Divisions per Major Y division")
94 self.OptionParser.add_option("--y_log",
95 action="store", type="inkbool",
96 dest="y_log", default=False,
97 help="Logarithmic y subdivisions if true")
98 self.OptionParser.add_option("--y_subsubdivs",
99 action="store", type="int",
100 dest="y_subsubdivs", default=5,
101 help="Subsubdivisions per Minor Y division")
102 self.OptionParser.add_option("--y_half_freq",
103 action="store", type="int",
104 dest="y_half_freq", default=4,
105 help="Halve Y Subsubdiv. Frequency after 'n' Subdivs. (log only)")
106 self.OptionParser.add_option("--y_divs_th",
107 action="store", type="float",
108 dest="y_divs_th", default=2,
109 help="Major Y Division Line thickness")
110 self.OptionParser.add_option("--y_subdivs_th",
111 action="store", type="float",
112 dest="y_subdivs_th", default=1,
113 help="Minor Y Division Line thickness")
114 self.OptionParser.add_option("--y_subsubdivs_th",
115 action="store", type="float",
116 dest="y_subsubdivs_th", default=1,
117 help="Subminor Y Division Line thickness")
118 self.OptionParser.add_option("--border_th",
119 action="store", type="float",
120 dest="border_th", default=3,
121 help="Border Line thickness")
124 def effect(self):
126 #find the pixel dimensions of the overall grid
127 ymax = self.options.dy * self.options.y_divs
128 xmax = self.options.dx * self.options.x_divs
130 # Embed grid in group
131 #Put in in the centre of the current view
132 t = 'translate(' + str( self.view_center[0]- xmax/2.0) + ',' + \
133 str( self.view_center[1]- ymax/2.0) + ')'
134 g_attribs = {inkex.addNS('label','inkscape'):'Grid_Polar:X' + \
135 str( self.options.x_divs )+':Y'+str( self.options.y_divs ),
136 'transform':t }
137 grid = inkex.etree.SubElement(self.current_layer, 'g', g_attribs)
139 #Group for major x gridlines
140 g_attribs = {inkex.addNS('label','inkscape'):'MajorXGridlines'}
141 majglx = inkex.etree.SubElement(grid, 'g', g_attribs)
143 #Group for major y gridlines
144 g_attribs = {inkex.addNS('label','inkscape'):'MajorYGridlines'}
145 majgly = inkex.etree.SubElement(grid, 'g', g_attribs)
147 #Group for minor x gridlines
148 if self.options.x_subdivs > 1:#if there are any minor x gridlines
149 g_attribs = {inkex.addNS('label','inkscape'):'MinorXGridlines'}
150 minglx = inkex.etree.SubElement(grid, 'g', g_attribs)
152 #Group for subminor x gridlines
153 if self.options.x_subsubdivs > 1:#if there are any minor minor x gridlines
154 g_attribs = {inkex.addNS('label','inkscape'):'SubMinorXGridlines'}
155 mminglx = inkex.etree.SubElement(grid, 'g', g_attribs)
157 #Group for minor y gridlines
158 if self.options.y_subdivs > 1:#if there are any minor y gridlines
159 g_attribs = {inkex.addNS('label','inkscape'):'MinorYGridlines'}
160 mingly = inkex.etree.SubElement(grid, 'g', g_attribs)
162 #Group for subminor y gridlines
163 if self.options.y_subsubdivs > 1:#if there are any minor minor x gridlines
164 g_attribs = {inkex.addNS('label','inkscape'):'SubMinorYGridlines'}
165 mmingly = inkex.etree.SubElement(grid, 'g', g_attribs)
168 draw_SVG_rect(0, 0, xmax, ymax, self.options.border_th,
169 'none', 'Border', grid) #border rectangle
171 #DO THE X DIVISONS======================================
172 sd = self.options.x_subdivs #sub divs per div
173 ssd = self.options.x_subsubdivs #subsubdivs per subdiv
175 for i in range(0, self.options.x_divs): #Major x divisons
176 if i>0: #dont draw first line (we made a proper border)
177 draw_SVG_line(self.options.dx*i, 0,
178 self.options.dx*i,ymax,
179 self.options.x_divs_th,
180 'MajorXDiv'+str(i), majglx)
182 if self.options.x_log: #log x subdivs
183 for j in range (1, sd):
184 if j>1: #the first loop is only for subsubdivs
185 draw_SVG_line(self.options.dx*(i+log(j, sd)), 0,
186 self.options.dx*(i+log(j, sd)), ymax,
187 self.options.x_subdivs_th,
188 'MinorXDiv'+str(i)+':'+str(j), minglx)
190 for k in range (1, ssd): #subsub divs
191 if (j <= self.options.x_half_freq) or (k%2 == 0):#only draw half the subsubdivs past the half-freq point
192 if (ssd%2 > 0) and (j > self.options.y_half_freq): #half frequency won't work with odd numbers of subsubdivs,
193 ssd2 = ssd+1 #make even
194 else:
195 ssd2 = ssd #no change
196 draw_SVG_line(self.options.dx*(i+log(j+k/float(ssd2),sd )), 0,
197 self.options.dx*(i+log(j+k/float(ssd2),sd )), ymax,
198 self.options.x_subsubdivs_th,'SubminorXDiv'+str(i)+':'+str(j)+':'+str(k), mminglx)
200 else: #linear x subdivs
201 for j in range (0, sd):
202 if j>0: #not for the first loop (this loop is for the subsubdivs before the first subdiv)
203 draw_SVG_line(self.options.dx*(i+j/float(sd)), 0,
204 self.options.dx*(i+j/float(sd)), ymax,
205 self.options.x_subdivs_th,
206 'MinorXDiv'+str(i)+':'+str(j), minglx)
208 for k in range (1, ssd): #subsub divs
209 draw_SVG_line(self.options.dx*(i+(j*ssd+k)/((float(sd)*ssd))) , 0,
210 self.options.dx*(i+(j*ssd+k)/((float(sd)*ssd))) , ymax,
211 self.options.x_subsubdivs_th,
212 'SubminorXDiv'+str(i)+':'+str(j)+':'+str(k), mminglx)
214 #DO THE Y DIVISONS========================================
215 sd = self.options.y_subdivs #sub divs per div
216 ssd = self.options.y_subsubdivs #subsubdivs per subdiv
218 for i in range(0, self.options.y_divs): #Major y divisons
219 if i>0:#dont draw first line (we will make a border)
220 draw_SVG_line(0, self.options.dy*i,
221 xmax, self.options.dy*i,
222 self.options.y_divs_th,
223 'MajorYDiv'+str(i), majgly)
225 if self.options.y_log: #log y subdivs
226 for j in range (1, sd):
227 if j>1: #the first loop is only for subsubdivs
228 draw_SVG_line(0, self.options.dy*(i+1-log(j,sd)),
229 xmax, self.options.dy*(i+1-log(j,sd)),
230 self.options.y_subdivs_th,
231 'MinorXDiv'+str(i)+':'+str(j), mingly)
233 for k in range (1, ssd): #subsub divs
234 if (j <= self.options.y_half_freq) or (k%2 == 0):#only draw half the subsubdivs past the half-freq point
235 if (ssd%2 > 0) and (j > self.options.y_half_freq): #half frequency won't work with odd numbers of subsubdivs,
236 ssd2 = ssd+1
237 else:
238 ssd2 = ssd #no change
239 draw_SVG_line(0, self.options.dx*(i+1-log(j+k/float(ssd2),sd )),
240 xmax, self.options.dx*(i+1-log(j+k/float(ssd2),sd )),
241 self.options.y_subsubdivs_th,
242 'SubminorXDiv'+str(i)+':'+str(j)+':'+str(k), mmingly)
243 else: #linear y subdivs
244 for j in range (0, self.options.y_subdivs):
245 if j>0:#not for the first loop (this loop is for the subsubdivs before the first subdiv)
246 draw_SVG_line(0, self.options.dy*(i+j/float(sd)),
247 xmax, self.options.dy*(i+j/float(sd)),
248 self.options.y_subdivs_th,
249 'MinorXYiv'+str(i)+':'+str(j), mingly)
251 for k in range (1, ssd): #subsub divs
252 draw_SVG_line(0, self.options.dy*(i+(j*ssd+k)/((float(sd)*ssd))),
253 xmax, self.options.dy*(i+(j*ssd+k)/((float(sd)*ssd))),
254 self.options.y_subsubdivs_th,
255 'SubminorXDiv'+str(i)+':'+str(j)+':'+str(k), mmingly)
259 if __name__ == '__main__':
260 e = Grid_Polar()
261 e.affect()
264 # vim: expandtab shiftwidth=4 tabstop=8 softtabstop=4 encoding=utf-8 textwidth=99