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 '''\r
25 import inkex
26 import simplestyle, sys
27 from math import *
29 def log_N(base, x): #computes the base-n log of x
30 return log(x)/log(base)
32 def draw_SVG_line(x1, y1, x2, y2, width, name, parent):
33 style = { 'stroke': '#000000', 'stroke-width':str(width), 'fill': 'none' }
34 line_attribs = {'style':simplestyle.formatStyle(style),
35 'inkscape:label':name,
36 'd':'M '+str(x1)+','+str(y1)+' L '+str(x2)+','+str(y2)}
37 inkex.etree.SubElement(parent, inkex.addNS('path','svg'), line_attribs )
39 def draw_SVG_rect(x,y,w,h, width, fill, name, parent):
40 style = { 'stroke': '#000000', 'stroke-width':str(width), 'fill':fill}
41 rect_attribs = {'style':simplestyle.formatStyle(style),
42 'inkscape:label':name,
43 'x':str(x), 'y':str(y), 'width':str(w), 'height':str(h)}
44 inkex.etree.SubElement(parent, inkex.addNS('rect','svg'), rect_attribs )
46 class Grid_Polar(inkex.Effect):
47 def __init__(self):
48 inkex.Effect.__init__(self)
49 self.OptionParser.add_option("--x_divs",
50 action="store", type="int",
51 dest="x_divs", default=5,
52 help="Major X Divisions")
53 self.OptionParser.add_option("--dx",
54 action="store", type="float",
55 dest="dx", default=100.0,
56 help="Major X divison Spacing")
57 self.OptionParser.add_option("--x_subdivs",
58 action="store", type="int",
59 dest="x_subdivs", default=2,
60 help="Subdivisions per Major X division")
61 self.OptionParser.add_option("--x_log",
62 action="store", type="inkbool",
63 dest="x_log", default=False,
64 help="Logarithmic x subdivisions if true")
65 self.OptionParser.add_option("--x_subsubdivs",
66 action="store", type="int",
67 dest="x_subsubdivs", default=5,
68 help="Subsubdivisions per Minor X division")
69 self.OptionParser.add_option("--x_half_freq",
70 action="store", type="int",
71 dest="x_half_freq", default=4,
72 help="Halve Subsubdiv. Frequency after 'n' Subdivs. (log only)")
73 self.OptionParser.add_option("--x_divs_th",
74 action="store", type="float",
75 dest="x_divs_th", default=2,
76 help="Major X Division Line thickness")
77 self.OptionParser.add_option("--x_subdivs_th",
78 action="store", type="float",
79 dest="x_subdivs_th", default=1,
80 help="Minor X Division Line thickness")
81 self.OptionParser.add_option("--x_subsubdivs_th",
82 action="store", type="float",
83 dest="x_subsubdivs_th", default=1,
84 help="Subminor X Division Line thickness")
85 self.OptionParser.add_option("--y_divs",
86 action="store", type="int",
87 dest="y_divs", default=6,
88 help="Major Y Divisions")
89 self.OptionParser.add_option("--dy",
90 action="store", type="float",
91 dest="dy", default=100.0,
92 help="Major Gridline Increment")
93 self.OptionParser.add_option("--y_subdivs",
94 action="store", type="int",
95 dest="y_subdivs", default=2,
96 help="Minor Divisions per Major Y division")
97 self.OptionParser.add_option("--y_log",
98 action="store", type="inkbool",
99 dest="y_log", default=False,
100 help="Logarithmic y subdivisions if true")
101 self.OptionParser.add_option("--y_subsubdivs",
102 action="store", type="int",
103 dest="y_subsubdivs", default=5,
104 help="Subsubdivisions per Minor Y division")
105 self.OptionParser.add_option("--y_half_freq",
106 action="store", type="int",
107 dest="y_half_freq", default=4,
108 help="Halve Y Subsubdiv. Frequency after 'n' Subdivs. (log only)")
109 self.OptionParser.add_option("--y_divs_th",
110 action="store", type="float",
111 dest="y_divs_th", default=2,
112 help="Major Y Division Line thickness")
113 self.OptionParser.add_option("--y_subdivs_th",
114 action="store", type="float",
115 dest="y_subdivs_th", default=1,
116 help="Minor Y Division Line thickness")
117 self.OptionParser.add_option("--y_subsubdivs_th",
118 action="store", type="float",
119 dest="y_subsubdivs_th", default=1,
120 help="Subminor Y Division Line thickness")
121 self.OptionParser.add_option("--border_th",
122 action="store", type="float",
123 dest="border_th", default=3,
124 help="Border Line thickness")
127 def effect(self):
129 #find the pixel dimensions of the overall grid
130 ymax = self.options.dy * self.options.y_divs
131 xmax = self.options.dx * self.options.x_divs
133 # Embed grid in group
134 #Put in in the centre of the current view
135 t = 'translate(' + str( self.view_center[0]- xmax/2.0) + ',' + \
136 str( self.view_center[1]- ymax/2.0) + ')'
137 g_attribs = {inkex.addNS('label','inkscape'):'Grid_Polar:X' + \
138 str( self.options.x_divs )+':Y'+str( self.options.y_divs ),
139 'transform':t }
140 grid = inkex.etree.SubElement(self.current_layer, 'g', g_attribs)
142 #Group for major x gridlines
143 g_attribs = {inkex.addNS('label','inkscape'):'MajorXGridlines'}
144 majglx = inkex.etree.SubElement(grid, 'g', g_attribs)
146 #Group for major y gridlines
147 g_attribs = {inkex.addNS('label','inkscape'):'MajorYGridlines'}
148 majgly = inkex.etree.SubElement(grid, 'g', g_attribs)
150 #Group for minor x gridlines
151 if self.options.x_subdivs > 1:#if there are any minor x gridlines
152 g_attribs = {inkex.addNS('label','inkscape'):'MinorXGridlines'}
153 minglx = inkex.etree.SubElement(grid, 'g', g_attribs)
155 #Group for subminor x gridlines
156 if self.options.x_subsubdivs > 1:#if there are any minor minor x gridlines
157 g_attribs = {inkex.addNS('label','inkscape'):'SubMinorXGridlines'}
158 mminglx = inkex.etree.SubElement(grid, 'g', g_attribs)
160 #Group for minor y gridlines
161 if self.options.y_subdivs > 1:#if there are any minor y gridlines
162 g_attribs = {inkex.addNS('label','inkscape'):'MinorYGridlines'}
163 mingly = inkex.etree.SubElement(grid, 'g', g_attribs)
165 #Group for subminor y gridlines
166 if self.options.y_subsubdivs > 1:#if there are any minor minor x gridlines
167 g_attribs = {inkex.addNS('label','inkscape'):'SubMinorYGridlines'}
168 mmingly = inkex.etree.SubElement(grid, 'g', g_attribs)
171 draw_SVG_rect(0, 0, xmax, ymax, self.options.border_th,
172 'none', 'Border', grid) #border rectangle
174 #DO THE X DIVISONS======================================
175 sd = self.options.x_subdivs #sub divs per div
176 ssd = self.options.x_subsubdivs #subsubdivs per subdiv
178 for i in range(0, self.options.x_divs): #Major x divisons
179 if i>0: #dont draw first line (we made a proper border)
180 draw_SVG_line(self.options.dx*i, 0,
181 self.options.dx*i,ymax,
182 self.options.x_divs_th,
183 'MajorXDiv'+str(i), majglx)
185 if self.options.x_log: #log x subdivs
186 for j in range (1, sd):
187 if j>1: #the first loop is only for subsubdivs
188 draw_SVG_line(self.options.dx*(i+log_N(sd, j)), 0,
189 self.options.dx*(i+log_N(sd, j)), ymax,
190 self.options.x_subdivs_th,
191 'MinorXDiv'+str(i)+':'+str(j), minglx)
193 for k in range (1, ssd): #subsub divs
194 if (j <= self.options.x_half_freq) or (k%2 == 0):#only draw half the subsubdivs past the half-freq point
195 if (ssd%2 > 0) and (j > self.options.y_half_freq): #half frequency won't work with odd numbers of subsubdivs,
196 ssd2 = ssd+1 #make even
197 else:
198 ssd2 = ssd #no change
199 draw_SVG_line(self.options.dx*(i+log_N(sd, j+k/float(ssd2) )), 0,
200 self.options.dx*(i+log_N(sd, j+k/float(ssd2) )), ymax,
201 self.options.x_subsubdivs_th,'SubminorXDiv'+str(i)+':'+str(j)+':'+str(k), mminglx)
203 else: #linear x subdivs
204 for j in range (0, sd):
205 if j>0: #not for the first loop (this loop is for the subsubdivs before the first subdiv)
206 draw_SVG_line(self.options.dx*(i+j/float(sd)), 0,
207 self.options.dx*(i+j/float(sd)), ymax,
208 self.options.x_subdivs_th,
209 'MinorXDiv'+str(i)+':'+str(j), minglx)
211 for k in range (1, ssd): #subsub divs
212 draw_SVG_line(self.options.dx*(i+(j*ssd+k)/((float(sd)*ssd))) , 0,
213 self.options.dx*(i+(j*ssd+k)/((float(sd)*ssd))) , ymax,
214 self.options.x_subsubdivs_th,
215 'SubminorXDiv'+str(i)+':'+str(j)+':'+str(k), mminglx)
217 #DO THE Y DIVISONS========================================
218 sd = self.options.y_subdivs #sub divs per div
219 ssd = self.options.y_subsubdivs #subsubdivs per subdiv
221 for i in range(0, self.options.y_divs): #Major y divisons
222 if i>0:#dont draw first line (we will make a border)
223 draw_SVG_line(0, self.options.dy*i,
224 xmax, self.options.dy*i,
225 self.options.y_divs_th,
226 'MajorYDiv'+str(i), majgly)
228 if self.options.y_log: #log y subdivs
229 for j in range (1, sd):
230 if j>1: #the first loop is only for subsubdivs
231 draw_SVG_line(0, self.options.dy*(i+1-log_N(sd, j)),
232 xmax, self.options.dy*(i+1-log_N(sd, j)),
233 self.options.y_subdivs_th,
234 'MinorXDiv'+str(i)+':'+str(j), mingly)
236 for k in range (1, ssd): #subsub divs
237 if (j <= self.options.y_half_freq) or (k%2 == 0):#only draw half the subsubdivs past the half-freq point
238 if (ssd%2 > 0) and (j > self.options.y_half_freq): #half frequency won't work with odd numbers of subsubdivs,
239 ssd2 = ssd+1
240 else:
241 ssd2 = ssd #no change
242 draw_SVG_line(0, self.options.dx*(i+1-log_N(sd, j+k/float(ssd2) )),
243 xmax, self.options.dx*(i+1-log_N(sd, j+k/float(ssd2) )),
244 self.options.y_subsubdivs_th,
245 'SubminorXDiv'+str(i)+':'+str(j)+':'+str(k), mmingly)
246 else: #linear y subdivs
247 for j in range (0, self.options.y_subdivs):
248 if j>0:#not for the first loop (this loop is for the subsubdivs before the first subdiv)
249 draw_SVG_line(0, self.options.dy*(i+j/float(sd)),
250 xmax, self.options.dy*(i+j/float(sd)),
251 self.options.y_subdivs_th,
252 'MinorXYiv'+str(i)+':'+str(j), mingly)
254 for k in range (1, ssd): #subsub divs
255 draw_SVG_line(0, self.options.dy*(i+(j*ssd+k)/((float(sd)*ssd))),
256 xmax, self.options.dy*(i+(j*ssd+k)/((float(sd)*ssd))),
257 self.options.y_subsubdivs_th,
258 'SubminorXDiv'+str(i)+':'+str(j)+':'+str(k), mmingly)
262 e = Grid_Polar()
263 e.affect()