2021-07-23 02:36:56 +02:00
#!/usr/bin/env python3
# Copyright (c) 2017, Veronika Irvine
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import os
from math import sin , cos , radians , ceil
from lxml import etree
import inkex
__author__ = ' Veronika Irvine '
__credits__ = [ ' Ben Connors ' , ' Veronika Irvine ' , ' Mark Shafer ' ]
__license__ = ' Simplified BSD '
class GroundFromTemplate ( inkex . EffectExtension ) :
def loadFile ( self ) :
# Ensure that file exists and has the proper extension
if not self . options . file :
inkex . errormsg ( ' You must specify a template file. ' )
exit ( )
self . options . file = self . options . file . strip ( )
if self . options . file == ' ' :
inkex . errormsg ( ' You must specify a template file. ' )
exit ( )
if not os . path . isfile ( self . options . file ) :
inkex . errormsg ( ' You have not specified a valid path for the template file. \n \n Your entry: ' + self . options . file )
exit ( )
extension = os . path . splitext ( self . options . file ) [ 1 ]
if extension != ' .txt ' :
inkex . errormsg ( ' The file name must end with .txt. \n \n Your entry: ' + self . options . file )
exit ( )
data = [ ]
rowCount = 0
colCount = 0
with open ( self . options . file , ' r ' ) as f :
first = True
for line in f :
if first :
# first line of file gives row count and column count
first = False
line = line . strip ( )
temp = line . split ( ' \t ' )
type = temp [ 0 ]
rowCount = int ( temp [ 1 ] )
colCount = int ( temp [ - 1 ] )
else :
line = line . strip ( )
line = line . lstrip ( ' [ ' )
line = line . rstrip ( ' ] ' )
rowData = line . split ( ' ] \t [ ' )
data . append ( [ ] )
for cell in rowData :
cell = cell . strip ( )
data [ - 1 ] . append ( [ float ( num ) for num in cell . split ( ' , ' ) ] )
return { ' type ' : type , ' rowCount ' : rowCount , ' colCount ' : colCount , ' data ' : data }
def line ( self , x1 , y1 , x2 , y2 ) :
"""
Draw a line from point at ( x1 , y1 ) to point at ( x2 , y2 ) .
Style of line is hard coded and specified by ' s ' .
"""
# define the motions
path = ' M %s , %s L %s , %s ' % ( x1 , y1 , x2 , y2 )
# define the stroke style
s = { ' stroke-linejoin ' : ' miter ' ,
2021-10-13 18:56:39 +02:00
' stroke-width ' : self . options . linewidth ,
' stroke-opacity ' : ' 1.0 ' ,
' fill-opacity ' : ' 1.0 ' ,
' stroke ' : self . options . linecolor ,
' stroke-linecap ' : ' butt ' ,
' stroke-linejoin ' : ' butt ' ,
' fill ' : ' none '
2021-07-23 02:36:56 +02:00
}
# create attributes from style and path
attribs = { ' style ' : str ( inkex . Style ( s ) ) , ' d ' : path }
# insert path object into current layer
etree . SubElement ( self . svg . get_current_layer ( ) , inkex . addNS ( ' path ' , ' svg ' ) , attribs )
def draw ( self , data , rowCount , colCount ) :
a = self . options . distance
theta = self . options . angle
deltaX = a * sin ( theta )
deltaY = a * cos ( theta )
maxRows = ceil ( self . options . height / deltaY )
maxCols = ceil ( self . options . width / deltaX )
x = 0.0
y = 0.0
repeatY = 0
repeatX = 0
while repeatY * rowCount < maxRows :
x = 0.0
repeatX = 0
while repeatX * colCount < maxCols :
for row in data :
for coords in row :
x1 = x + coords [ 0 ] * deltaX
y1 = y + coords [ 1 ] * deltaY
x2 = x + coords [ 2 ] * deltaX
y2 = y + coords [ 3 ] * deltaY
x3 = x + coords [ 4 ] * deltaX
y3 = y + coords [ 5 ] * deltaY
self . line ( x1 , y1 , x2 , y2 )
self . line ( x1 , y1 , x3 , y3 )
repeatX + = 1
x + = deltaX * colCount
repeatY + = 1
y + = deltaY * rowCount
def add_arguments ( self , pars ) :
pars . add_argument ( ' -f ' , ' --file ' , help = ' File containing lace ground description ' )
pars . add_argument ( ' --angle ' , type = float )
pars . add_argument ( ' --distance ' , type = float )
pars . add_argument ( ' --pinunits ' )
pars . add_argument ( ' --width ' , type = float )
pars . add_argument ( ' --patchunits ' )
pars . add_argument ( ' --height ' , type = float )
pars . add_argument ( ' --linewidth ' , type = float )
pars . add_argument ( ' --lineunits ' )
pars . add_argument ( ' --linecolor ' , type = inkex . Color )
def effect ( self ) :
result = self . loadFile ( )
# Convert input to universal units
self . options . width = self . svg . unittouu ( str ( self . options . width ) + self . options . patchunits )
self . options . height = self . svg . unittouu ( str ( self . options . height ) + self . options . patchunits )
self . options . linewidth = self . svg . unittouu ( str ( self . options . linewidth ) + self . options . lineunits )
self . options . distance = self . svg . unittouu ( str ( self . options . distance ) + self . options . pinunits )
# Users expect distance to be the vertical distance between footside pins
# (vertical distance between every other row) but in the script we use it
# as the diagonal distance between grid points
# therefore convert distance based on the angle chosen
self . options . angle = radians ( self . options . angle )
self . options . distance = self . options . distance / ( 2.0 * cos ( self . options . angle ) )
# Draw a ground based on file description and user inputs
self . options . linecolor = self . options . linecolor . to_rgb ( )
# For now, assume style is Checker but could change in future
self . draw ( result [ ' data ' ] , result [ ' rowCount ' ] , result [ ' colCount ' ] )
if __name__ == ' __main__ ' :
GroundFromTemplate ( ) . run ( )