summaryrefslogtreecommitdiffstats
path: root/share/extensions/grid_cartesian.py
diff options
context:
space:
mode:
Diffstat (limited to 'share/extensions/grid_cartesian.py')
-rwxr-xr-xshare/extensions/grid_cartesian.py297
1 files changed, 297 insertions, 0 deletions
diff --git a/share/extensions/grid_cartesian.py b/share/extensions/grid_cartesian.py
new file mode 100755
index 0000000..e2082c1
--- /dev/null
+++ b/share/extensions/grid_cartesian.py
@@ -0,0 +1,297 @@
+#!/usr/bin/env python
+# coding=utf-8
+#
+# Copyright (C) 2007 John Beard john.j.beard@gmail.com
+#
+# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+"""
+This extension allows you to draw a Cartesian grid in Inkscape.
+
+There is a wide range of options including subdivision, subsubdivions and
+logarithmic scales. Custom line widths are also possible.
+
+All elements are grouped with similar elements (eg all x-subdivs)
+"""
+
+from math import log
+
+import inkex
+from inkex import Group, PathElement, Rectangle
+
+
+def draw_line(x1, y1, x2, y2, width, name, parent):
+ """Draw an SVG line"""
+ line = parent.add(PathElement())
+ line.style = {"stroke": "#000000", "stroke-width": str(width), "fill": "none"}
+ line.path = "M {},{} L {},{}".format(x1, y1, x2, y2)
+ line.label = name
+
+
+def draw_rect(x, y, w, h, width, fill, name, parent):
+ """Draw an SVG Rectangle"""
+ rect = parent.add(Rectangle(x=str(x), y=str(y), width=str(w), height=str(h)))
+ rect.style = {"stroke": "#000000", "stroke-width": str(width), "fill": fill}
+ rect.label = name
+
+
+class GridCartesian(inkex.GenerateExtension):
+ def add_arguments(self, pars):
+ pars.add_argument("--border_th", type=float, default=1.0)
+ pars.add_argument("--border_th_unit", default="px")
+ pars.add_argument("--tab", default="x_tab")
+ pars.add_argument("--x_divs", type=int, default=6)
+ pars.add_argument("--dx", type=float, default=5.0)
+ pars.add_argument("--dx_unit", default="mm")
+ pars.add_argument("--x_subdivs", type=int, default=1)
+ pars.add_argument("--x_log", type=inkex.Boolean, default=False)
+ pars.add_argument("--x_subsubdivs", type=int, default=1)
+ pars.add_argument("--x_half_freq", type=int, default=1)
+ pars.add_argument("--x_divs_th", type=float, default=1.0)
+ pars.add_argument("--x_subdivs_th", type=float, default=0.5)
+ pars.add_argument("--x_subsubdivs_th", type=float, default=0.3)
+ pars.add_argument("--x_div_unit", default="px")
+ pars.add_argument("--y_divs", type=int, default=5)
+ pars.add_argument("--dy", type=float, default=5.0)
+ pars.add_argument("--dy_unit", default="mm")
+ pars.add_argument("--y_subdivs", type=int, default=1)
+ pars.add_argument("--y_log", type=inkex.Boolean, default=False)
+ pars.add_argument("--y_subsubdivs", type=int, default=5)
+ pars.add_argument("--y_half_freq", type=int, default=1)
+ pars.add_argument("--y_divs_th", type=float, default=1.0)
+ pars.add_argument("--y_subdivs_th", type=float, default=0.5)
+ pars.add_argument("--y_subsubdivs_th", type=float, default=0.3)
+ pars.add_argument("--y_div_unit", default="px")
+
+ def generate(self):
+ self.options.border_th = self.svg.unittouu(
+ str(self.options.border_th) + self.options.border_th_unit
+ )
+
+ self.options.dx = self.svg.unittouu(str(self.options.dx) + self.options.dx_unit)
+ self.options.x_divs_th = self.svg.unittouu(
+ str(self.options.x_divs_th) + self.options.x_div_unit
+ )
+ self.options.x_subdivs_th = self.svg.unittouu(
+ str(self.options.x_subdivs_th) + self.options.x_div_unit
+ )
+ self.options.x_subsubdivs_th = self.svg.unittouu(
+ str(self.options.x_subsubdivs_th) + self.options.x_div_unit
+ )
+
+ self.options.dy = self.svg.unittouu(str(self.options.dy) + self.options.dy_unit)
+ self.options.y_divs_th = self.svg.unittouu(
+ str(self.options.y_divs_th) + self.options.y_div_unit
+ )
+ self.options.y_subdivs_th = self.svg.unittouu(
+ str(self.options.y_subdivs_th) + self.options.y_div_unit
+ )
+ self.options.y_subsubdivs_th = self.svg.unittouu(
+ str(self.options.y_subsubdivs_th) + self.options.y_div_unit
+ )
+
+ # find the pixel dimensions of the overall grid
+ ymax = self.options.dy * self.options.y_divs
+ xmax = self.options.dx * self.options.x_divs
+
+ # Embed grid in group
+ # Put in in the centre of the current view
+
+ grid = Group.new("GridCartesian:X{0.x_divs}:Y{0.y_divs}".format(self.options))
+
+ (pos_x, pos_y) = self.svg.namedview.center
+ grid.transform.add_translate(pos_x - xmax / 2.0, pos_y - ymax / 2.0)
+
+ # Group for major x gridlines
+ majglx = grid.add(Group.new("MajorXGridlines"))
+ # Group for major y gridlines
+ majgly = grid.add(Group.new("MajorYGridlines"))
+
+ # Group for minor x gridlines
+ if self.options.x_subdivs > 1: # if there are any minor x gridlines
+ minglx = grid.add(Group.new("MinorXGridlines"))
+
+ # Group for subminor x gridlines
+ if self.options.x_subsubdivs > 1: # if there are any minor minor x gridlines
+ mminglx = grid.add(Group.new("SubMinorXGridlines"))
+
+ # Group for minor y gridlines
+ if self.options.y_subdivs > 1: # if there are any minor y gridlines
+ mingly = grid.add(Group.new("MinorYGridlines"))
+
+ # Group for subminor y gridlines
+ if self.options.y_subsubdivs > 1: # if there are any minor minor x gridlines
+ mmingly = grid.add(Group.new("SubMinorYGridlines"))
+
+ draw_rect(
+ 0, 0, xmax, ymax, self.options.border_th, "none", "Border", grid
+ ) # border rectangle
+
+ # DO THE X DIVISIONS======================================
+ sd = self.options.x_subdivs # sub divs per div
+ ssd = self.options.x_subsubdivs # subsubdivs per subdiv
+
+ for i in range(0, self.options.x_divs): # Major x divisions
+ if i > 0: # don't draw first line (we made a proper border)
+ draw_line(
+ self.options.dx * i,
+ 0,
+ self.options.dx * i,
+ ymax,
+ self.options.x_divs_th,
+ "MajorXDiv" + str(i),
+ majglx,
+ )
+
+ if self.options.x_log: # log x subdivs
+ for j in range(1, sd):
+ if j > 1: # the first loop is only for subsubdivs
+ draw_line(
+ self.options.dx * (i + log(j, sd)),
+ 0,
+ self.options.dx * (i + log(j, sd)),
+ ymax,
+ self.options.x_subdivs_th,
+ "MinorXDiv" + str(i) + ":" + str(j),
+ minglx,
+ )
+
+ for k in range(1, ssd): # subsub divs
+ if (j <= self.options.x_half_freq) or (
+ k % 2 == 0
+ ): # only draw half the subsubdivs past the half-freq point
+ if (ssd % 2 > 0) and (
+ j > self.options.y_half_freq
+ ): # half frequency won't work with odd numbers of subsubdivs,
+ ssd2 = ssd + 1 # make even
+ else:
+ ssd2 = ssd # no change
+ draw_line(
+ self.options.dx * (i + log(j + k / float(ssd2), sd)),
+ 0,
+ self.options.dx * (i + log(j + k / float(ssd2), sd)),
+ ymax,
+ self.options.x_subsubdivs_th,
+ "SubminorXDiv" + str(i) + ":" + str(j) + ":" + str(k),
+ mminglx,
+ )
+
+ else: # linear x subdivs
+ for j in range(0, sd):
+ if (
+ j > 0
+ ): # not for the first loop (this loop is for the subsubdivs before the first subdiv)
+ draw_line(
+ self.options.dx * (i + j / float(sd)),
+ 0,
+ self.options.dx * (i + j / float(sd)),
+ ymax,
+ self.options.x_subdivs_th,
+ "MinorXDiv" + str(i) + ":" + str(j),
+ minglx,
+ )
+
+ for k in range(1, ssd): # subsub divs
+ draw_line(
+ self.options.dx * (i + (j * ssd + k) / (float(sd) * ssd)),
+ 0,
+ self.options.dx * (i + (j * ssd + k) / (float(sd) * ssd)),
+ ymax,
+ self.options.x_subsubdivs_th,
+ "SubminorXDiv" + str(i) + ":" + str(j) + ":" + str(k),
+ mminglx,
+ )
+
+ # DO THE Y DIVISIONS========================================
+ sd = self.options.y_subdivs # sub divs per div
+ ssd = self.options.y_subsubdivs # subsubdivs per subdiv
+
+ for i in range(0, self.options.y_divs): # Major y divisions
+ if i > 0: # don't draw first line (we will make a border)
+ draw_line(
+ 0,
+ self.options.dy * i,
+ xmax,
+ self.options.dy * i,
+ self.options.y_divs_th,
+ "MajorYDiv" + str(i),
+ majgly,
+ )
+
+ if self.options.y_log: # log y subdivs
+ for j in range(1, sd):
+ if j > 1: # the first loop is only for subsubdivs
+ draw_line(
+ 0,
+ self.options.dy * (i + 1 - log(j, sd)),
+ xmax,
+ self.options.dy * (i + 1 - log(j, sd)),
+ self.options.y_subdivs_th,
+ "MinorXDiv" + str(i) + ":" + str(j),
+ mingly,
+ )
+
+ for k in range(1, ssd): # subsub divs
+ if (j <= self.options.y_half_freq) or (
+ k % 2 == 0
+ ): # only draw half the subsubdivs past the half-freq point
+ if (ssd % 2 > 0) and (
+ j > self.options.y_half_freq
+ ): # half frequency won't work with odd numbers of subsubdivs,
+ ssd2 = ssd + 1
+ else:
+ ssd2 = ssd # no change
+ draw_line(
+ 0,
+ self.options.dx
+ * (i + 1 - log(j + k / float(ssd2), sd)),
+ xmax,
+ self.options.dx
+ * (i + 1 - log(j + k / float(ssd2), sd)),
+ self.options.y_subsubdivs_th,
+ "SubminorXDiv" + str(i) + ":" + str(j) + ":" + str(k),
+ mmingly,
+ )
+ else: # linear y subdivs
+ for j in range(0, self.options.y_subdivs):
+ if (
+ j > 0
+ ): # not for the first loop (this loop is for the subsubdivs before the first subdiv)
+ draw_line(
+ 0,
+ self.options.dy * (i + j / float(sd)),
+ xmax,
+ self.options.dy * (i + j / float(sd)),
+ self.options.y_subdivs_th,
+ "MinorXYiv" + str(i) + ":" + str(j),
+ mingly,
+ )
+
+ for k in range(1, ssd): # subsub divs
+ draw_line(
+ 0,
+ self.options.dy * (i + (j * ssd + k) / (float(sd) * ssd)),
+ xmax,
+ self.options.dy * (i + (j * ssd + k) / (float(sd) * ssd)),
+ self.options.y_subsubdivs_th,
+ "SubminorXDiv" + str(i) + ":" + str(j) + ":" + str(k),
+ mmingly,
+ )
+
+ return grid
+
+
+if __name__ == "__main__":
+ GridCartesian().run()