From e1dfb1f4f89f3a41de248515aa165c94afb51887 Mon Sep 17 00:00:00 2001 From: Mario Voigt Date: Wed, 12 Aug 2020 20:02:01 +0200 Subject: [PATCH] Added Triangular Grid --- extensions/fablabchemnitz_triangular_grid.inx | 47 +++ extensions/fablabchemnitz_triangular_grid.py | 267 ++++++++++++++++++ 2 files changed, 314 insertions(+) create mode 100644 extensions/fablabchemnitz_triangular_grid.inx create mode 100644 extensions/fablabchemnitz_triangular_grid.py diff --git a/extensions/fablabchemnitz_triangular_grid.inx b/extensions/fablabchemnitz_triangular_grid.inx new file mode 100644 index 00000000..a00d1567 --- /dev/null +++ b/extensions/fablabchemnitz_triangular_grid.inx @@ -0,0 +1,47 @@ + + + Triangular Grid + fablabchemnitz.de.triangular_grid + + + + + + + + + + 100.00 + 3 + 3 + 30 + 2 + 5 + + + #000000ff + 3 + + + black + 2 + + + black + 2 + + + #000000ff + 2 + + + + all + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz_triangular_grid.py b/extensions/fablabchemnitz_triangular_grid.py new file mode 100644 index 00000000..9f968f0a --- /dev/null +++ b/extensions/fablabchemnitz_triangular_grid.py @@ -0,0 +1,267 @@ +#!/usr/bin/env python 3 +''' +Copyright (C) 2013 Carl Sorensen carl.d.sorensen@gmail.com +Derived from grid_cartesion.py copyright (C) 2007 John Beard john.j.beard@gmail.com + + +##This extension allows you to draw a Triangular grid in Inkscape. +##There is a wide range of options including subdivision, subsubdivions +##and angles of the triangular axes. +##Custom line widths are also possible. +##All elements are grouped with similar elements (eg all x-subdivs) + +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 +''' + +import inkex +from lxml import etree +from math import * + +def draw_SVG_line(x1, y1, x2, y2, width, stroke, name, parent): + style = { 'stroke': stroke, 'stroke-width':str(width), 'fill': 'none' } + line_attribs = {'style':str(inkex.Style(style)), + inkex.addNS('label','inkscape'):name, + 'd':'M '+str(x1)+','+ str(y1) +' L '+ str(x2) + ',' + str(y2)} + etree.SubElement(parent, inkex.addNS('path','svg'), line_attribs ) + +def draw_SVG_rect(x,y,w,h, width, stroke, fill, name, parent): + style = { 'stroke': stroke, 'stroke-width':str(width), 'fill':fill} + rect_attribs = {'style':str(inkex.Style(style)), + inkex.addNS('label','inkscape'):name, + 'x':str(x), 'y':str(y), 'width':str(w), 'height':str(h)} + etree.SubElement(parent, inkex.addNS('rect','svg'), rect_attribs ) + +def colorString(pickerColor): + longcolor = int(pickerColor) & 0xFFFFFF00 + return '#' + format(longcolor >> 8, '06X') + + +class Grid_Triangular(inkex.Effect): + def __init__(self): + inkex.Effect.__init__(self) + self.arg_parser.add_argument("--tabs") + self.arg_parser.add_argument("--size_unit", default="mm", help="Unit for grid size") + self.arg_parser.add_argument("--y_divs", type=int, default=3, help="Major vertical divisions") + self.arg_parser.add_argument("--x_divs", type=int, default=3, help="Major horizontal divisions") + self.arg_parser.add_argument("--grid_angle", type=float, default=30.0, help="Angle between X axis and triangular grid lines") + self.arg_parser.add_argument("--dm", type=float, default=100.0, help="Major grid division spacing") + self.arg_parser.add_argument("--subdivs", type=int, default=5, help="Subdivisions per major grid division") + self.arg_parser.add_argument("--subsubdivs", type=int, default=2, help="Subsubdivisions per minor grid subdivision") + self.arg_parser.add_argument("--border_th", type=float, default=3.0, help="Border Line thickness") + self.arg_parser.add_argument("--border_color", type=int, help="Border line color") + self.arg_parser.add_argument("--major_th", type=float, default=2.0, help="Major grid division line thickness") + self.arg_parser.add_argument("--major_color", type=int, help="Major grid division line color") + self.arg_parser.add_argument("--subdiv_th", type=float, default=1.0, help="Minor grid subdivision line thickness") + self.arg_parser.add_argument("--subdiv_color", type=int, help="Minor grid subdivision line color") + self.arg_parser.add_argument("--subsubdiv_th", type=float, default=1.0, help="Subminor grid division line thickness") + self.arg_parser.add_argument("--subsubdiv_color", type=int, help="Subminor grid division line color") + + def EdgePoints(self,x0, y0, theta): + # find the intersection points of the line with the extended + # grid bounding box. + # Note that y is positive DOWN, not up + theta_r = radians(theta) + r_bot = (self.ymax-y0)/sin(theta_r) + r_top = -y0/sin(theta_r) + r_left = -x0/cos(theta_r) + r_right = (self.xmax-x0)/cos(theta_r) + return [[x0+r_left*cos(theta_r),y0+r_left*sin(theta_r)], + [x0+r_right*cos(theta_r), y0+r_right*sin(theta_r)], + [x0+r_bot*cos(theta_r), y0+r_bot*sin(theta_r)], + [x0+r_top*cos(theta_r), y0+r_top*sin(theta_r)]] + + def trimmed_coords(self, x1, y1, theta): + #find the start and end coordinates for a grid line + #starting at (x1, y1) with an angle of theta + border_points = self.EdgePoints(x1, y1, theta) + left = 0 + right = 1 + top = 3 + bottom = 2 + x=0 + y=1 + if theta > 0: + if border_points[left][y] < 0: + start_x = border_points[top][x] + start_y = border_points[top][y] + else: + start_x = border_points[left][x] + start_y = border_points[left][y] + if border_points[right][y] > self.ymax: + end_x = border_points[bottom][x] + end_y = border_points[bottom][y] + else: + end_x = border_points[right][x] + end_y = border_points[right][y] + else: + if border_points[left][y] > self.ymax: + start_x = border_points[bottom][x] + start_y = border_points[bottom][y] + else: + start_x = border_points[left][x] + start_y = border_points[left][y] + if border_points[right][y] < 0: + end_x = border_points[top][x] + end_y = border_points[top][y] + else: + end_x = border_points[right][x] + end_y = border_points[right][y] + return [[start_x,start_y],[end_x, end_y]] + + def drawAngledGridLine (self, x1, y1, theta, thickness, color, + label, groupName): + end_points = self.trimmed_coords(x1, y1, theta) + x_start = end_points[0][0] + y_start = end_points[0][1] + x_end = end_points[1][0] + y_end = end_points[1][1] + + if (x_start >= 0 and x_start <= self.xmax and + y_start >= 0 and y_start <= self.ymax and + x_end >= 0 and x_end <= self.xmax and + y_end >= 0 and y_end <= self.ymax): + draw_SVG_line(x_start, y_start, + x_end, y_end, + thickness, colorString(color), label, groupName) + + def effect(self): + + #find the pixel dimensions of the overall grid + dm = self.svg.unittouu(str(self.options.dm) + self.options.size_unit) + self.ymax = dm * self.options.y_divs #grid spacing defined along vertical + dx = dm / (2.0 * tan(radians(self.options.grid_angle))) + self.xmax = dx * self.options.x_divs + dy = dm + + # Embed grid in group + #Put in in the centre of the current view + t = 'translate(' + str( self.svg.namedview.center[0]- self.xmax/2.0) + ',' + \ + str( self.svg.namedview.center[1]- self.ymax/2.0) + ')' + g_attribs = {inkex.addNS('label','inkscape'):'Grid_Triangular:Size' + \ + str( self.options.x_divs)+'x'+str(self.options.y_divs) + + ':Angle'+str( self.options.grid_angle ), + 'transform':t } + grid = etree.SubElement(self.svg.get_current_layer(), 'g', g_attribs) + + #Group for major x gridlines + g_attribs = {inkex.addNS('label','inkscape'):'MajorXGridlines'} + majglx = etree.SubElement(grid, 'g', g_attribs) + + #Group for major positive theta gridlines + g_attribs = {inkex.addNS('label','inkscape'):'MajorPosGridlines'} + majglp = etree.SubElement(grid, 'g', g_attribs) + + #Group for major negative theta gridlines + g_attribs = {inkex.addNS('label','inkscape'):'MajorNegGridLines'} + majgln = etree.SubElement(grid, 'g', g_attribs) + + #Groups for minor gridlines + if self.options.subdivs > 1:#if there are any minor gridlines + g_attribs = {inkex.addNS('label','inkscape'):'MinorXGridlines'} + minglx = etree.SubElement(grid, 'g', g_attribs) + g_attribs = {inkex.addNS('label','inkscape'):'MinorPosGridlines'} + minglp = etree.SubElement(grid, 'g', g_attribs) + g_attribs = {inkex.addNS('label','inkscape'):'MinorNegGridlines'} + mingln = etree.SubElement(grid, 'g', g_attribs) + + #Groups for subminor gridlines + if self.options.subsubdivs > 1:#if there are any minor minor gridlines + g_attribs = {inkex.addNS('label','inkscape'):'SubMinorXGridlines'} + mminglx = etree.SubElement(grid, 'g', g_attribs) + g_attribs = {inkex.addNS('label','inkscape'):'SubMinorPosGridlines'} + mminglp = etree.SubElement(grid, 'g', g_attribs) + g_attribs = {inkex.addNS('label','inkscape'):'SubMinorNegGridlines'} + mmingln = etree.SubElement(grid, 'g', g_attribs) + + draw_SVG_rect(0, 0, self.xmax, self.ymax, + self.options.border_th, + colorString(self.options.border_color), 'none', + 'Border', grid) #border rectangle + + sd = self.options.subdivs #sub divs per div + ssd = self.options.subsubdivs #subsubdivs per subdiv + + + #DO THE HORIZONTAL DIVISONS====================================== + + for i in range(0, self.options.x_divs): #Major x divisons + if i>0: #dont draw first line (we made a proper border) + # Draw the vertical line + draw_SVG_line(dx*i, 0, + dx*i,self.ymax, + self.options.major_th, + colorString(self.options.major_color), + 'MajorDiv'+str(i), majglx) + + 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_SVG_line(dx*(i+j/float(sd)), 0, + dx*(i+j/float(sd)), self.ymax, + self.options.subdiv_th, + colorString(self.options.subdiv_color), + 'MinorDiv'+str(i)+':'+str(j), minglx) + + for k in range (1, ssd): #subsub divs + draw_SVG_line(dx*(i+(j*ssd+k)/((float(sd)*ssd))) , 0, + dx*(i+(j*ssd+k)/((float(sd)*ssd))) , self.ymax, + self.options.subsubdiv_th, + colorString(self.options.subsubdiv_color), + 'SubminorDiv'+str(i)+':'+str(j)+':'+str(k), mminglx) + + #DO THE VERTICAL DIVISONS======================================== + for i in range(-self.options.x_divs-self.options.y_divs, + self.options.x_divs+self.options.y_divs): #Major y divisons + self.drawAngledGridLine(0, dy*i, self.options.grid_angle, + self.options.major_th, + self.options.major_color, + 'MajorYDivP'+str(i), + majglp) + self.drawAngledGridLine(0, dy*i, -self.options.grid_angle, + self.options.major_th, + self.options.major_color, + 'MajorYDivN'+str(i), + majgln) + + for j in range (0, sd): #subdivs + if j>0:#not for the first loop (this loop is for the subsubdivs before the first subdiv) + self.drawAngledGridLine(0, dy*(i+j/float(sd)), + self.options.grid_angle, + self.options.subdiv_th, + self.options.subdiv_color, + 'MinorYDivP'+str(i), + minglp) + self.drawAngledGridLine(0, dy*(i+j/float(sd)), + -self.options.grid_angle, + self.options.subdiv_th, + self.options.subdiv_color, + 'MinorYDivN'+str(i), + mingln) + for k in range (1, ssd): #subsub divs + self.drawAngledGridLine(0, dy*(i+(j*ssd+k)/((float(sd)*ssd))), + self.options.grid_angle, + self.options.subsubdiv_th, + self.options.subsubdiv_color, + 'SubMinorYDivP'+str(i), + mminglp) + self.drawAngledGridLine(0, dy*(i+(j*ssd+k)/((float(sd)*ssd))), + -self.options.grid_angle, + self.options.subsubdiv_th, + self.options.subsubdiv_color, + 'SubMinorYDivN'+str(i), + mmingln) + + +if __name__ == '__main__': + Grid_Triangular().run() \ No newline at end of file