Initial commit. First part of extensions. More are coming back again

soon.
This commit is contained in:
Mario Voigt 2022-09-01 10:55:46 +02:00
commit abe8989812
274 changed files with 37671 additions and 0 deletions

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -0,0 +1,10 @@
#!/bin/bash
clear
for folder in */ ; do
if [[ -f "${folder}/meta.json" ]]; then
sed -i 's/github.com\/vmario89/github.com\/eridur-de/g' ${folder}/meta.json
sed -i 's/mightyscape-1.X/mightyscape-1.2/g' ${folder}/meta.json
fi
done

View File

@ -0,0 +1,114 @@
#!/bin/bash
clear
echo "--> Validating inx files with xmllint. Only errors are printed to console"
for folder in */ ; do
xmllint --noout --relaxng ./inkscape.extension.rng ${folder}*.inx > /dev/null 2>> 000_xmllint.out
done
grep -v "validates\|warning: failed to load external entity" 000_xmllint.out; rm 000_xmllint.out
#complete set of meta information
AGGLOMERATED_JSON=""
for folder in */ ; do
if [[ ! -f "${folder}meta.json" ]]; then
echo "meta.json missing for ${folder}"
else
JSON_OKAY=$(jq -e . ${folder}meta.json)
if [ $? -eq 0 ]; then
echo ${AGGLOMERATED_JSON} > /tmp/prevJson
AGGLOMERATED_JSON=$(jq -s ".[0] + .[1]" /tmp/prevJson ${folder}meta.json)
else
echo Format error in ${folder}meta.json
fi
#DEBUG
#cat ${folder}meta.json | jq
fi
done
#print overall json
#echo $AGGLOMERATED_JSON | jq
echo "--> Show unique license kinds used:"
echo $AGGLOMERATED_JSON | jq -r '.[]|{license}|.[]' | sort | uniq -c
echo "--> show unique list of involved contributors (thanks/credits):"
#echo $AGGLOMERATED_JSON | jq -r '.[]|{main_authors}|.[]|.[]' | sort | uniq -c
echo $AGGLOMERATED_JSON | jq -r '.[]|{main_authors}|.[]|.[]' | sort | uniq
#show extensions which are in gallery
GALLERY_EXTENSIONS=$(echo $AGGLOMERATED_JSON | jq -r '.[]|{inkscape_gallery_url}|.[]' | sort | grep -v "null")
for GALLERY_EXTENSION in ${GALLERY_EXTENSIONS}; do
EXTENSION=$(echo ${AGGLOMERATED_JSON} | jq -r '.[]|select(.inkscape_gallery_url=="'$GALLERY_EXTENSION'")|{name}|.[]')
done
echo "--> Count of inx files:"
INX=$(find ./ -type f -name "*.inx" | wc -l)
echo INX: $INX
echo "--> Count of extension folders:"
FOLDERS=$(ls -d */ | wc -l)
echo FOLDERS: $FOLDERS
README="../../README.md"
#replace values in README.md
sed -i 's/\*\*.* extension folders\*\*/\*\*'${FOLDERS}' extension folders\*\*/g' ${README}
sed -i 's/\*\* with .* \.inx files\*\*/\*\* with \*\*'${INX}' \.inx files\*\*/g' ${README}
echo "Removing unrequired pyc cache files"
find . -type d -name "__pycache__" -exec rm -rf {} \; > /dev/null
read -p "Update zip files for zipmirror?" -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "Building extension zip files for zipmirror"
TARGETDIR="../../../mightyscape-1.2-zipmirror"
mkdir -p $TARGETDIR > /dev/null 2>&1
for EXTENSION in */; do
EXTENSION="${EXTENSION%/}" #strip trailing slash
EXTRA=""
DEPS=$(jq -r 'try .[]|.dependent_extensions|.[]' ${EXTENSION}/meta.json)
DEPS=$(echo $DEPS|tr -d '\n')
#if dependencies are not empty, then ...
if [[ ! -z $DEPS ]]; then
EXTRA="$DEPS"
fi
ZIPFILE=$TARGETDIR/$EXTENSION.zip
echo "--> creating/updating $ZIPFILE"
zip -ru $ZIPFILE $EXTENSION/ 000_about_fablabchemnitz.svg $EXTRA > /dev/null 2>&1
done
fi
read -p "Build local gallery extension zip files?" -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "Building Inkscape gallery extension zip files"
TARGETDIR="../000_Inkscape_Gallery"
mkdir -p $TARGETDIR > /dev/null 2>&1
#show extensions which are in gallery
GALLERY_EXTENSIONS=$(echo $AGGLOMERATED_JSON | jq -r '.[]|{inkscape_gallery_url}|.[]' | sort | grep -v "null")
for GALLERY_EXTENSION in ${GALLERY_EXTENSIONS}; do
EXTENSION="$(echo ${AGGLOMERATED_JSON} | jq -r '.[]|select(.inkscape_gallery_url=="'$GALLERY_EXTENSION'")|{path}|.[]')"
EXTRA=""
DEPS=$(jq -r 'try .[]|.dependent_extensions|.[]' ${EXTENSION}/meta.json)
DEPS=$(echo $DEPS|tr -d '\n')
#if dependencies are not empty, then ...
if [[ ! -z $DEPS ]]; then
EXTRA="$DEPS"
fi
ZIPFILE=$TARGETDIR/$EXTENSION.zip
rm $ZIPFILE > /dev/null 2>&1
echo "--> creating/updating $ZIPFILE"
zip -ru $ZIPFILE $EXTENSION/ 000_about_fablabchemnitz.svg $EXTRA > /dev/null 2>&1
done
fi

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Affine Spirals</name>
<id>fablabchemnitz.de.affine_spirals</id>
<param name="active-tab" type="notebook">
<page name="title" gui-text="Settings">
<param name="num_lines" type="int" min="1" max="100" gui-text="Depth">3</param>
<param name="num_petals" type="int" min="2" max="100" gui-text="petals">2</param>
<param name="shrink_ratio" type="float" min="0.01" max="0.99" precision="2" gui-text="shrink factor">0.8</param>
<param name="noses" type="bool" gui-text="Enabled noses">true</param>
</page>
</param>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu name="FabLab Chemnitz Shape Generators">
<submenu name="Puzzles/Mazes/Nests"/>
</submenu>
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">affine_spirals.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,100 @@
#!/usr/bin/env python3
import inkex
from lxml import etree
from math import cos, sin, pi, exp
__version__ = '0.1'
def line(npts=40, x0=0, y0=0, delta=.5, sgn=1):
#returns a list of points on a line (y = +/- x + c) starting at x0,y0
return [ (x0 + delta*t, y0 + sgn*delta*t) for t in range(npts)]
def ff(v, ww=.25, ds=.4):
#covering map from R^2 ro punctured plane
x,y = v
r,u = exp(-ds*x), cos(pi*ww*y) + 1J*sin(pi*ww*y)
return r*u
def mk_plugs(pts, noses):
#returns a list of complex representing a plug type segment
segs = [fit_plug(end_pts, noses) for end_pts in zip(pts,pts[1:]) ]
tmp = []
for seg in segs:
tmp.extend(seg)
return tmp
def fit_plug(ss, noses):
a,b = ss
rot = complex(b-a)
pts = [0,.45,.4 + .15*1J, .6 + .15*1J, .55, 1]
if noses is True:
return [rot*z + a for z in pts]
else:
return ss
def pts2curve(cplxs):
'''makes a polyline path element from a list of complex
'''
def cplx2pt(z):
return (z.real,z.imag)
scale = 200
data = [cplx2pt( scale*z ) for z in cplxs ]
pth = [ '%.2f, %.2f '%z for z in data]
return 'M '+ ''.join(pth)
class AffineSpirals(inkex.EffectExtension):
def add_arguments(self, pars):
pars.add_argument("--num_lines", type=int, default=3)
pars.add_argument("--num_petals", type=int, default=3)
pars.add_argument("--shrink_ratio", type=float, default=3)
pars.add_argument("--active-tab")
pars.add_argument("--noses", type=inkex.Boolean, default=True)
def calc_unit_factor(self):
unit_factor = self.svg.unittouu(str(1.0) + self.options.units)
return unit_factor
def effect(self):
path_stroke = '#DD0000' # take color from tab3
path_fill = 'none' # no fill - just a line
path_stroke_width = 1. # can also be in form '0.6mm'
page_id = self.options.active_tab # sometimes wrong the very first time
styles = [ {'stroke': path_stroke , 'fill': 'none', 'stroke-width': path_stroke_width},
{'stroke': 'none', 'fill': '#FFFF00', 'stroke-width': 0}]
styles = [str(inkex.Style(x)) for x in styles]
# This finds center of current view in inkscape
t = 'translate(%s,%s)' % (self.svg.namedview.center[0], self.svg.namedview.center[1])
# Make a nice useful name
g_attribs = {inkex.addNS('label','inkscape'): 'koch',
inkex.addNS('transform-center-x','inkscape'): str(0),
inkex.addNS('transform-center-y','inkscape'): str(0),
'transform': t,
'style' : styles[1],
'info':'N: '+str(self.options.num_lines) }
topgroup = etree.SubElement(self.svg.get_current_layer(), 'g', g_attribs)
NN = 2*self.options.num_lines
NP = self.options.num_petals
SF = 2*self.options.shrink_ratio
payload = []
for y in range(-NP,NP):
mpts = [ff(z,ww=1./NP, ds=SF) for z in line(npts=NN, y0=y)]
payload.append(mk_plugs(mpts, self.options.noses))
mpts = [ff(z,ww=1./NP, ds=SF) for z in line(npts=NN, y0=y,sgn=-1 )]
payload.append(mk_plugs(mpts, self.options.noses))
payload = [pts2curve(cc) for cc in payload]
payload = ' '.join(payload)
curve_attribs = { 'style': styles[0], 'd': payload}
etree.SubElement(topgroup, inkex.addNS('path','svg'), curve_attribs)
if __name__ == '__main__':
AffineSpirals().run()

View File

@ -0,0 +1,21 @@
[
{
"name": "Affine Spirals",
"id": "fablabchemnitz.de.affine_spirals",
"path": "affine_spirals",
"dependent_extensions": null,
"original_name": "Affine Torus 0.0",
"original_id": "githubacct.uniqueid.affine_torus",
"license": "MIT License",
"license_url": "https://github.com/macbuse/Affine-spirals/blob/master/LICENSE",
"comment": "",
"source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/affine_spirals",
"fork_url": "https://github.com/macbuse/Affine-spirals",
"documentation_url": "https://stadtfabrikanten.org/display/IFM/Affine+Spirals",
"inkscape_gallery_url": null,
"main_authors": [
"github.com/macbuse",
"github.com/eridur-de"
]
}
]

View File

@ -0,0 +1,194 @@
#!/usr/bin/env python3
# Generate Apollonian Gaskets -- the math part.
# Copyright (c) 2014 Ludger Sandig
# This file is part of apollon.
# Apollon 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 3 of the License, or
# (at your option) any later version.
# Apollon 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 Apollon. If not, see <http://www.gnu.org/licenses/>.
from cmath import *
import random
class Circle(object):
"""
A circle represented by center point as complex number and radius.
"""
def __init__ ( self, mx, my, r ):
"""
@param mx: x center coordinate
@type mx: int or float
@param my: y center coordinate
@type my: int or float
@param r: radius
@type r: int or float
"""
self.r = r
self.m = (mx +my*1j)
def __repr__ ( self ):
"""
Pretty printing
"""
return "Circle( self, %s, %s, %s )" % (self.m.real, self.m.imag, self.r)
def __str__ ( self ):
"""
Pretty printing
"""
return "Circle x:%.3f y:%.3f r:%.3f [cur:%.3f]" % (self.m.real, self.m.imag, self.r.real, self.curvature().real)
def curvature (self):
"""
Get circle's curvature.
@rtype: float
@return: Curvature of the circle.
"""
return 1/self.r
def outerTangentCircle( circle1, circle2, circle3 ):
"""
Takes three externally tangent circles and calculates the fourth one enclosing them.
@param circle1: first circle
@param circle2: second circle
@param circle3: third circle
@type circle1: L{Circle}
@type circle2: L{Circle}
@type circle3: L{Circle}
@return: The enclosing circle
@rtype: L{Circle}
"""
cur1 = circle1.curvature()
cur2 = circle2.curvature()
cur3 = circle3.curvature()
m1 = circle1.m
m2 = circle2.m
m3 = circle3.m
cur4 = -2 * sqrt( cur1*cur2 + cur2*cur3 + cur1 * cur3 ) + cur1 + cur2 + cur3
m4 = ( -2 * sqrt( cur1*m1*cur2*m2 + cur2*m2*cur3*m3 + cur1*m1*cur3*m3 ) + cur1*m1 + cur2*m2 + cur3*m3 ) / cur4
circle4 = Circle( m4.real, m4.imag, 1/cur4 )
return circle4
def tangentCirclesFromRadii( r2, r3, r4 ):
"""
Takes three radii and calculates the corresponding externally
tangent circles as well as a fourth one enclosing them. The enclosing
circle is the first one.
@param r2, r3, r4: Radii of the circles to calculate
@type r2: int or float
@type r3: int or float
@type r4: int or float
@return: The four circles, where the first one is the enclosing one.
@rtype: (L{Circle}, L{Circle}, L{Circle}, L{Circle})
"""
circle2 = Circle( 0, 0, r2 )
circle3 = Circle( r2 + r3, 0, r3 )
m4x = (r2*r2 + r2*r4 + r2*r3 - r3*r4) / (r2 + r3)
m4y = sqrt( (r2 + r4) * (r2 + r4) - m4x*m4x )
circle4 = Circle( m4x, m4y, r4 )
circle1 = outerTangentCircle( circle2, circle3, circle4 )
return ( circle1, circle2, circle3, circle4 )
def secondSolution( fixed, c1, c2, c3 ):
"""
If given four tangent circles, calculate the other one that is tangent
to the last three.
@param fixed: The fixed circle touches the other three, but not
the one to be calculated.
@param c1, c2, c3: Three circles to which the other tangent circle
is to be calculated.
@type fixed: L{Circle}
@type c1: L{Circle}
@type c2: L{Circle}
@type c3: L{Circle}
@return: The circle.
@rtype: L{Circle}
"""
curf = fixed.curvature()
cur1 = c1.curvature()
cur2 = c2.curvature()
cur3 = c3.curvature()
curn = 2 * (cur1 + cur2 + cur3) - curf
mn = (2 * (cur1*c1.m + cur2*c2.m + cur3*c3.m) - curf*fixed.m ) / curn
return Circle( mn.real, mn.imag, 1/curn )
class ApollonianGasket(object):
"""
Container for an Apollonian Gasket.
"""
def __init__(self, c1, c2, c3):
"""
Creates a basic apollonian Gasket with four circles.
@param c1, c2, c3: The curvatures of the three inner circles of the
starting set (i.e. depth 0 of the recursion). The fourth,
enclosing circle will be calculated from them.
@type c1: int or float
@type c2: int or float
@type c3: int or float
"""
self.start = tangentCirclesFromRadii( 1/c1, 1/c2, 1/c3 )
self.genCircles = list(self.start)
def recurse(self, circles, depth, maxDepth):
"""Recursively calculate the smaller circles of the AG up to the
given depth. Note that for depth n we get 2*3^{n+1} circles.
@param maxDepth: Maximal depth of the recursion.
@type maxDepth: int
@param circles: 4-Tuple of circles for which the second
solutions are calculated
@type circles: (L{Circle}, L{Circle}, L{Circle}, L{Circle})
@param depth: Current depth
@type depth: int
"""
if( depth == maxDepth ):
return
(c1, c2, c3, c4) = circles
if( depth == 0 ):
# First recursive step, this is the only time we need to
# calculate 4 new circles.
del self.genCircles[4:]
cspecial = secondSolution( c1, c2, c3, c4 )
self.genCircles.append( cspecial )
self.recurse( (cspecial, c2, c3, c4), 1, maxDepth )
cn2 = secondSolution( c2, c1, c3, c4 )
self.genCircles.append( cn2 )
cn3 = secondSolution( c3, c1, c2, c4 )
self.genCircles.append( cn3 )
cn4 = secondSolution( c4, c1, c2, c3 )
self.genCircles.append( cn4 )
self.recurse( (cn2, c1, c3, c4), depth+1, maxDepth )
self.recurse( (cn3, c1, c2, c4), depth+1, maxDepth )
self.recurse( (cn4, c1, c2, c3), depth+1, maxDepth )
def generate(self, depth):
"""
Wrapper for the recurse function. Generate the AG,
@param depth: Recursion depth of the Gasket
@type depth: int
"""
self.recurse(self.start, 0, depth)

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Apollonian Gasket</name>
<id>fablabchemnitz.de.apollonian_gasket</id>
<param name="active_tab" type="notebook">
<page name="settings" gui-text="Settings">
<param name="depth" type="int" min="2" max="10" gui-text="Depth" gui-description="Warning: high values might calculate really long!">3</param>
<param name="c1" type="float" min="0.1" max="10.0" precision="2" gui-text="c1">2.0</param>
<param name="c2" type="float" min="0.1" max="10.0" precision="2" gui-text="c2">3.0</param>
<param name="c3" type="float" min="0.1" max="10.0" precision="2" gui-text="c3">3.0</param>
<param name="shrink" type="bool" gui-text="shrink circles for cutting">true</param>
<param name="as_paths" type="bool" gui-text="draw svg:path instead svg:circle elements">true</param>
</page>
<page name="Usage" gui-text="Usage">
<label xml:space="preserve">Make an apollonian gasket:
Depth = depth in search tree
c1,c2,c3 = curvatures of first 3 osculating circles</label>
<label appearance="url">https://en.wikipedia.org/wiki/Apollonian_gasket</label>
</page>
</param>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu name="FabLab Chemnitz Shape Generators">
<submenu name="Puzzles/Mazes/Nests"/>
</submenu>
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">apollonian_gasket.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,101 @@
#!/usr/bin/env python3
import inkex
import apolloniangasket_func
from lxml import etree
__version__ = '0.0'
def cplxs2pts(zs):
tt = []
for z in zs:
tt.extend([z.real,z.imag])
return tt
def draw_SVG_circle(parent, r, cx, cy, name):
" structure an SVG circle entity under parent "
circ_attribs = { 'cx': str(cx), 'cy': str(cy),
'r': str(r),
inkex.addNS('label','inkscape'): name}
circle = etree.SubElement(parent, inkex.addNS('circle','svg'), circ_attribs)
def draw_SVG_circleAsPath(parent, r, cx, cy, name):
circ_attribs = {
"d": "M {:0.6f}, {:0.6f} a {:0.6f},{:0.6f} 0 1,0 {:0.6f},0 a {:0.6f},{:0.6f} 0 1,0 {:0.6f},0".format(
cx - r, cy, r, r, 2*r, r, r, -2*r),
inkex.addNS('label','inkscape'): name}
circle = etree.SubElement(parent, inkex.addNS('path','svg'), circ_attribs)
class Gasket(inkex.EffectExtension): # choose a better name
def add_arguments(self, pars):
pars.add_argument("--active_tab")
pars.add_argument("--depth",type=int, default=3)
pars.add_argument("--c1", type=float, default=2.0)
pars.add_argument("--c2", type=float, default=3.0)
pars.add_argument("--c3", type=float, default=3.0)
pars.add_argument("--shrink", type=inkex.Boolean, default=True)
pars.add_argument("--as_paths", type=inkex.Boolean, default=True)
def calc_unit_factor(self):
unit_factor = self.svg.unittouu(str(1.0) + self.options.units)
return unit_factor
### -------------------------------------------------------------------
### Main function and is called when the extension is run.
def effect(self):
#set up path styles
path_stroke = '#DD0000' # take color from tab3
path_fill = 'none' # no fill - just a line
path_stroke_width = self.svg.unittouu(str(0.1) + "mm")
page_id = self.options.active_tab # sometimes wrong the very first time
style_curve = { 'stroke': path_stroke,
'fill': 'none',
'stroke-width': path_stroke_width }
# This finds center of current view in inkscape
t = 'translate(%s,%s)' % (self.svg.namedview.center[0], self.svg.namedview.center[1])
# add a group to the document's current layer
#all the circles inherit style from this group
g_attribs = { inkex.addNS('label','inkscape'): 'zengon' + "_%d"%(self.options.depth),
inkex.addNS('transform-center-x','inkscape'): str(0),
inkex.addNS('transform-center-y','inkscape'): str(0),
'transform': t,
'style' : str(inkex.Style((style_curve))),
'info':'N: '}
topgroup = etree.SubElement(self.svg.get_current_layer(), 'g', g_attribs)
circles = apolloniangasket_func.main(c1=self.options.c1,
c2=self.options.c2,
c3=self.options.c3,
depth=self.options.depth)
#shrink the circles so they don't touch
#useful for laser cutting
if self.options.shrink:
circles = circles[1:]
for cc in circles:
cc.r = abs(cc.r)
if cc.r >.5:
cc.r -= .1
else:
cc.r *= .9
scale_factor = 200
for c in circles:
cx, cy, r = c.m.real, c.m.imag, abs(c.r)
#rescale and add circle to document
cx, cy, r = scale_factor * cx , scale_factor * cy, scale_factor * r
if self.options.as_paths is False:
draw_SVG_circle(topgroup, r, cx, cy, 'apollian')
else:
draw_SVG_circleAsPath(topgroup, r, cx, cy, 'apollian')
if __name__ == '__main__':
Gasket().run()

View File

@ -0,0 +1,112 @@
#!/usr/bin/env python3
# Command line program to create svg apollonian circles
# Copyright (c) 2014 Ludger Sandig
# This file is part of apollon.
# Apollon 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 3 of the License, or
# (at your option) any later version.
# Apollon 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 Apollon. If not, see <http://www.gnu.org/licenses/>.
import math
from apollon import ApollonianGasket
def ag_to_svg(circles, colors, tresh=0.00005):
"""
Convert a list of circles to svg, optionally color them.
@param circles: A list of L{Circle}s
@param colors: A L{ColorMap} object
@param tresh: Only circles with a radius greater than the product of tresh and maximal radius are saved
"""
svg = []
tresh = .000005
print ('>>', tresh)
# Find the biggest circle, which hopefully is the enclosing one
# and has a negative radius because of this. Note that this does
# not have to be the case if we picked an unlucky set of radii at
# the start. If that was the case, we're screwed now.
big = min(circles, key=lambda c: c.r.real)
# Move biggest circle to front so it gets drawn first
circles.remove(big)
circles.insert(0, big)
if big.r.real < 0:
# Bounding box from biggest circle, lower left corner and two
# times the radius as width
corner = big.m - ( abs(big.r) + abs(big.r) * 1j )
vbwidth = abs(big.r)*2
width = 500 # Hardcoded!
# Line width independent of circle size
lw = (vbwidth/width)
svg.append('<svg xmlns="http://www.w3.org/2000/svg" width="%f" height="%f" viewBox="%f %f %f %f">\n' % (width, width, corner.real, corner.imag, vbwidth, vbwidth))
# Keep stroke width relative
svg.append('<g stroke-width="%f">\n' % lw)
# Iterate through circle list, circles with radius<radmin
# will not be saved because they are too small for printing.
radmin = tresh * abs(big.r)
print(radmin)
for c in circles:
if abs(c.r) > radmin:
fill = colors.color_for(abs(c.r))
svg.append(( '<circle cx="%f" cy="%f" r="%f" fill="%s" stroke="black"/>\n' % (c.m.real, c.m.imag, abs(c.r), fill)))
svg.append('</g>\n')
svg.append('</svg>\n')
return ''.join(svg)
def impossible_combination(c1, c2, c3):
# If any curvatures x, y, z satisfy the equation
# x = 2*sqrt(y*z) + y + z
# then no fourth enclosing circle can be genereated, because it
# would be a line.
# We need to see for c1, c2, c3 if they could be "x".
impossible = False
sets = [(c1,c2,c3), (c2,c3,c1), (c3,c1,c2)]
for (x, y, z) in sets:
if x == 2*math.sqrt(y*z) + y + z:
impossible = True
return impossible
def main(c1=3.,c2=2.,c3=2.,depth=5):
# Sanity checks
for c in [c1, c2,c3]:
if c == 0:
print("Error: curvature or radius can't be 0")
exit(1)
if impossible_combination(c1, c2, c3):
print("Error: no apollonian gasket possible for these curvatures")
exit(1)
ag = ApollonianGasket(c1, c2, c3)
ag.generate(depth)
# Get smallest and biggest radius
smallest = abs(min(ag.genCircles, key=lambda c: abs(c.r.real)).r.real)
biggest = abs(max(ag.genCircles, key=lambda c: abs(c.r.real)).r.real)
return ag.genCircles

View File

@ -0,0 +1,21 @@
[
{
"name": "Apollonian Gasket",
"id": "fablabchemnitz.de.apollonian_gasket",
"path": "apollonian_gasket",
"dependent_extensions": null,
"original_name": "Apollonian Gasket 0.0",
"original_id": "githubacct.uniqueid.apollonian",
"license": "MIT License",
"license_url": "https://github.com/macbuse/Apollonian/blob/master/License.md",
"comment": "",
"source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/apollonian_gasket",
"fork_url": "https://github.com/macbuse/Apollonian",
"documentation_url": "https://stadtfabrikanten.org/display/IFM/Apollonian+Gasket",
"inkscape_gallery_url": null,
"main_authors": [
"github.com/macbuse",
"github.com/eridur-de"
]
}
]

View File

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Archimedes Spiral</name>
<id>fablabchemnitz.de.archimedes_spiral</id>
<param name="tab" type="notebook">
<page name="tab_settings" gui-text="Settings">
<label appearance="header">R = r + aθ</label>
<param name="r" type="int" min="0" max="1000000" gui-text="r (mm)">50</param>
<param name="a" type="float" min="0" max="1000000" gui-text="a">3</param>
<param name="step" type="int" min="1" max="300" gui-text="Step" gui-description="The higher the value the better the accuracy. If you set the step value really low the resulting length will not be precise and the curve is maybe not drawn at all">50</param>
<param name="trl" type="optiongroup" appearance="combo" gui-text="Turn direction">
<option value="0">Left</option>
<option value="1">Right</option>
</param>
<param name="length" type="float" min="0" max="1000000" gui-text="Length (mm)">0</param>
<param name="turns" type="int" min="1" max="1000000" gui-text="Turns" gui-description="Works only if you set 'length (mm)' to 0.0">5</param>
</page>
<page name="tab_about" gui-text="About">
<label appearance="header">Archimedes Spiral</label>
<label>2020 - 2021 / written by Mario Voigt (Stadtfabrikanten e.V. / FabLab Chemnitz)</label>
<spacer/>
<label appearance="header">Online Documentation</label>
<label appearance="url">https://y.stadtfabrikanten.org/archimedesspiral</label>
<spacer/>
<label appearance="header">Contributing</label>
<label appearance="url">https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.X</label>
<label appearance="url">mailto:mario.voigt@stadtfabrikanten.org</label>
<spacer/>
<label appearance="header">MightyScape Extension Collection</label>
<label>This piece of software is part of the MightyScape for Inkscape Extension Collection and is licensed under GNU GPL v3</label>
<label appearance="url">https://y.stadtfabrikanten.org/mightyscape-overview</label>
</page>
<page name="tab_donate" gui-text="Donate">
<label appearance="header">Coffee + Pizza</label>
<label>We are the Stadtfabrikanten, running the FabLab Chemnitz since 2016. A FabLab is an open workshop that gives people access to machines and digital tools like 3D printers, laser cutters and CNC milling machines.</label>
<spacer/>
<label>You like our work and want to support us? You can donate to our non-profit organization by different ways:</label>
<label appearance="url">https://y.stadtfabrikanten.org/donate</label>
<spacer/>
<label>Thanks for using our extension and helping us!</label>
<image>../000_about_fablabchemnitz.svg</image>
</page>
</param>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu name="FabLab Chemnitz">
<submenu name="Shape/Pattern from Generator"/>
</submenu>
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">archimedes_spiral.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,89 @@
#!/usr/bin/env python3
"""
Copyright (C) 2017 Panagiotis Loukas
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WAphiANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WAphiANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Version 0.2
This script was written by Panagiotis Loukas to make spiral easy.
It simply,
Have fun :)
PS.
Written on Arch.
"""
import inkex
from lxml import etree
from math import cos, sin, pi, log, sqrt
class Archimedes(inkex.EffectExtension):
def add_arguments(self, pars):
pars.add_argument('--tab')
pars.add_argument('--r', type = int, default = '50')
pars.add_argument('--a', type = float, default = '3')
pars.add_argument('--step', type = int, default = '50')
pars.add_argument('--trl', default = '1')
pars.add_argument('--turns', type = float, default = '5')
pars.add_argument('--length', type = float, default = '500')
def effect(self):
th = pi / 3
a = self.options.a
r = self.options.r
length = self.options.length
if length > 0:
turns = self.angle(a, r, length, th) / (2 * pi)
else:
turns = self.options.turns
if self.options.trl == '1':
step = -self.options.step
else:
step = self.options.step
layer = etree.SubElement(self.document.getroot(),'g')
path = etree.Element(inkex.addNS('path','svg'))
path.set('d', self.built(r, step, a, turns))
path.set('style',"fill:none;stroke:#000000;stroke-width:1px;stroke-opacity:1")
layer.append(path)
def built(self, r0, st = 4, a = 4, k = 1, th = 0):
step = 2 * pi / st
r = r0
s = "M " + str(r * cos(th)) + ", " + str(-r * sin(th))
for i in range(0, int(k * (abs(st)))):
prin = th + i * step
meta = th + (i + 1) * step
rp = r0 + abs(a * prin)# instead we put the absolute value the spiral will drift inwards
rm = r0 + abs(a * meta)# at the absolute price closes outwards
s += "a " + str(rm) + "," + str(rm) + " 0 0," + self.options.trl + " " + str(-rp * cos(prin) + rm * cos(meta)) + "," + str(rp * sin(prin) -rm * sin(meta))
return s
# see https://mathepedia.de/Archimedische_Spirale.html for formula of total arc length
def spirallength(self, a, r0, th):
phi = (r0 / a) + th
phi_sqrt = sqrt(phi ** 2 + 1)
return (a / 2) * (phi * phi_sqrt + log(phi + phi_sqrt))
def ds(self, a, r0, th):
return self.spirallength(a, r0, th) - self.spirallength(a, r0, 0)
def angle(self, a, r0, length, th):
i = 0.0
while (True):
ls=self.ds(a, r0, i)
if length - ls > 100:
i += 0.01
elif length - ls > 10: i += 0.001
elif length - ls > 1: i += 0.0001
elif length - ls > 0.1: i += 0.00001
elif length - ls > 0.01: i += 0.000001
else: break
return i
if __name__ == '__main__':
Archimedes().run()

View File

@ -0,0 +1,21 @@
[
{
"name": "Archimedes Spiral",
"id": "fablabchemnitz.de.archimedes_spiral",
"path": "archimedes_spiral",
"dependent_extensions": null,
"original_name": "speira",
"original_id": "org.ekips.filter.s2",
"license": "GNU GPL v2",
"license_url": "https://inkscape.org/de/~panos/%E2%98%85speira?c=16537",
"comment": "",
"source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/archimedes_spiral",
"fork_url": "https://inkscape.org/de/~panos/%E2%98%85speira?c=16537",
"documentation_url": "https://stadtfabrikanten.org/display/IFM/Archimedes+Spiral",
"inkscape_gallery_url": null,
"main_authors": [
"inkscape.org/panos",
"github.com/eridur-de"
]
}
]

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Barrel Distortion</name>
<id>fablabchemnitz.de.barrel_distorsion</id>
<param name="lambda_coef" type="float" min="-1000.0" max="0.0" precision="2" gui-text="Lambda parameter">-1.0</param>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu name="FabLab Chemnitz">
<submenu name="Transformations"/>
</submenu>
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">barrel_distorsion.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,103 @@
import math
import re
import inkex
from inkex import bezier
from inkex.paths import Path, CubicSuperPath
class BarrelDistorsion(inkex.EffectExtension):
def add_arguments(self, pars):
pars.add_argument("--lambda_coef", type=float, default=-5.0, help="command line help")
def distort_coordinates(self, x, y):
"""Method applies barrel distorsion to given points with distorsion center in center of image, selected to
Args:
x (float): X coordinate of given point
y (float): Y coordinate of given point
Returns:
tuple(float, float): Tuple with X,Y distorted coordinates of given point
"""
x_u = (x - self.x_c) / (self.width + self.height)
y_u = (y - self.y_c) / (self.width + self.height)
x_d = x_u / 2 / (self.q * y_u**2 + x_u**2 * self.q) * (1 - math.sqrt(1 - 4 * self.q * y_u**2 - 4 * x_u**2 * self.q))
y_d = y_u / 2 / (self.q * y_u**2 + x_u**2 * self.q) * (1 - math.sqrt(1 - 4 * self.q * y_u**2 - 4 * x_u**2 * self.q))
x_d *= self.width + self.height
y_d *= self.width + self.height
x_d += self.x_c
y_d += self.y_c
return x_d, y_d
def split_into_nodes(self, nodes_number=1000):
for id, node in self.svg.selected.items():
if node.tag == inkex.addNS('path', 'svg'):
p = CubicSuperPath(node.get('d'))
new = []
for sub in p:
new.append([sub[0][:]])
i = 1
while i <= len(sub) - 1:
length = bezier.cspseglength(
new[-1][-1], sub[i])
splits = nodes_number
for s in range(int(splits), 1, -1):
new[-1][-1], next, sub[
i] = bezier.cspbezsplitatlength(
new[-1][-1], sub[i], 1.0 / s)
new[-1].append(next[:])
new[-1].append(sub[i])
i += 1
node.set('d', str(CubicSuperPath(new)))
def effect(self):
if re.match(r'g\d+',
list(self.svg.selected.items())[0][0]) is not None:
raise SystemExit(
"You are trying to distort group of objects.\n This extension works only with path objects due to Inkscape API restrictions.\n Ungroup your objects and try again."
)
self.split_into_nodes()
self.q = self.options.lambda_coef
if self.q == 0.0:
inkex.errormsg("Invalid lambda coefficient. May not be exactly zero.")
return
nodes = []
for id, node in self.svg.selected.items():
if node.tag == inkex.addNS('path', 'svg'):
path = Path(node.get('d')).to_arrays()
nodes += path
if len(nodes) == 0:
inkex.utils.debug("Selection is invalid. Please change selection to paths only.")
exit(1)
nodes_filtered = [x for x in nodes if x[0] != 'Z']
x_coordinates = [x[-1][-2] for x in nodes_filtered]
y_coordinates = [y[-1][-1] for y in nodes_filtered]
self.width = max(x_coordinates) - min(x_coordinates)
self.height = max(y_coordinates) - min(y_coordinates)
self.x_c = sum(x_coordinates) / len(x_coordinates)
self.y_c = sum(y_coordinates) / len(y_coordinates)
for id, node in self.svg.selected.items():
if node.tag == inkex.addNS('path', 'svg'):
path = Path(node.get('d')).to_arrays()
distorted = []
first = True
for cmd, params in path:
if cmd != 'Z':
if first == True:
x = params[-2]
y = params[-1]
distorted.append(
['M',
list(self.distort_coordinates(x, y))])
first = False
else:
x = params[-2]
y = params[-1]
distorted.append(
['L', self.distort_coordinates(x, y)])
node.set('d', str(Path(distorted)))
if __name__ == '__main__':
BarrelDistorsion().run()

View File

@ -0,0 +1,21 @@
[
{
"name": "Barrel Distortion",
"id": "fablabchemnitz.de.barrel_distorsion",
"path": "barrel_distorsion",
"dependent_extensions": null,
"original_name": "Distortion",
"original_id": "org.pub.dIstorsion",
"license": "MIT License",
"license_url": "https://github.com/ucuapps/InkscapeBarrelDistortion/blob/master/LICENSE",
"comment": "",
"source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/barrel_distorsion",
"fork_url": "https://github.com/ucuapps/InkscapeBarrelDistortion",
"documentation_url": "https://stadtfabrikanten.org/display/IFM/Barrel+Distortion",
"inkscape_gallery_url": null,
"main_authors": [
"github.com/ucuapps",
"github.com/eridur-de"
]
}
]

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Bezier Envelope</name>
<id>fablabchemnitz.de.bezier_envelope</id>
<effect>
<object-type>path</object-type>
<effects-menu>
<submenu name="FabLab Chemnitz">
<submenu name="Transformations"/>
</submenu>
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">bezier_envelope.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,409 @@
#!/usr/bin/env python3
'''
Bezier Envelope extension for Inkscape
Copyright (C) 2009 Gerrit Karius
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.
About the Bezier Envelope extension:
This extension implements Bezier enveloping.
It takes an arbitrary path (the "letter") and a 4-sided path (the "envelope") as input.
The envelope must be 4 segments long. Unless the letter is to be rotated or flipped,
the envelope should begin at the upper left corner and be drawn clockwise.
The extension then attempts to squeeze the letter into the envelope
by rearranging all anchor and handle points of the letter's path.
In order to do this, the bounding box of the letter is used.
All anchor and bezier handle points get new x and y coordinates between 0% and 100%
according to their place inside the bounding box.
The 4 sides of the envelope are then interpreted as deformed axes.
Points at 0% or 100% could be placed along these axes, but because most points
are somewhere inside the bounding box, some tweening of the axes must be done.
The function mapPointsToMorph does the tweening.
Say, some point is at x=30%, y=40%.
For the tweening, the function tweenCubic first calculates a straight tween
of the y axis at the x percentage of 30%.
This tween axis now floats somewhere between the y axis keys at the x percentage,
but is not necessarily inside the envelope, because the x axes are not straight.
Now, the end points on the two x axes at 30% are calculated. The function match()
takes these points and calculates a "stretch" transform which maps the two anchor
points of the y axis tween to the two points on the x axes by rotating the tween and
stretching it along its endpoints. This transform is then applied to the handle points,
to get the entire tweened y axis to its x tweened position.
Last, the point at the y percentage 40% of this y axis tween is calculated.
That is the final point of the enveloped letter.
Finally, after all of the letter's points have been recalculated in this manner,
the resulting path is taken and replaces the letter's original path.
TODO:
* Currently, both letter and envelope must be paths to work.
-> Arbitrary other shapes like circles and rectangles should be interpreted as paths.
* It should be possible to select several letters, and squeeze them into one envelope as a group.
* It should be possible to insert a clone of the letter, instead of replacing it.
* This program was originally written in Java. Maybe for some code, Python shortcuts can be used.
I hope the comments are not too verbose. Enjoy!
'''
import inkex
from inkex import Transform
from inkex.paths import Path
import math
import sys
import ffgeom
class BezierEnvelope(inkex.EffectExtension):
segmentTypes = ["move","line","quad","cubic","close"]
def effect(self):
if len(self.options.ids) < 2:
inkex.errormsg("Two paths must be selected. The 1st is the letter, the 2nd is the envelope and must have 4 sides.")
exit()
letterElement = self.svg.selected[self.options.ids[0]]
envelopeElement = self.svg.selected[self.options.ids[1]]
if letterElement.get('inkscape:original-d') or envelopeElement.get('inkscape:original-d'):
inkex.errormsg("One or both selected paths have attribute 'inkscape:original-d' which points to Live Path Effects (LPE). Please convert to regular path.")
exit()
if letterElement.tag != inkex.addNS('path','svg') or envelopeElement.tag != inkex.addNS('path','svg'):
inkex.errormsg("Both letter and envelope must be SVG paths.")
exit()
axes = extractMorphAxes(Path( envelopeElement.get('d') ).to_arrays())
if axes is None:
inkex.errormsg("No axes found on envelope.")
exit()
axisCount = len(axes)
if axisCount < 4:
inkex.errormsg("The envelope path has less than 4 segments.")
exit()
for i in range( 0, 4 ):
if axes[i] is None:
inkex.errormsg("axis[%i] is None. Check if envelope has at least 4 nodes (closed path) or 5 nodes (open path)." % i)
exit()
# morph the enveloped element according to the axes
morph_element( letterElement, envelopeElement, axes );
def morph_element( letterElement, envelopeElement, axes ):
path = Path( letterElement.get('d') ).to_arrays()
morphedPath = morphPath( path, axes )
letterElement.set("d", str(Path(morphedPath)))
# Morphs a path into a new path, according to cubic curved bounding axes.
def morphPath(path, axes):
bounds = [y for x in list(Path(path).bounding_box()) for y in list(x)]
assert len(bounds) == 4
new_path = []
current = [ 0.0, 0.0 ]
start = [ 0.0, 0.0 ]
for cmd, params in path:
segmentType = cmd
points = params
if segmentType == "M":
start[0] = points[0]
start[1] = points[1]
segmentType = convertSegmentToCubic( current, segmentType, points, start )
percentages = [0.0]*len(points)
morphed = [0.0]*len(points)
numPts = getNumPts( segmentType )
normalizePoints( bounds, points, percentages, numPts )
mapPointsToMorph( axes, percentages, morphed, numPts )
addSegment( new_path, segmentType, morphed )
if len(points) >= 2:
current[0] = points[ len(points)-2 ]
current[1] = points[ len(points)-1 ]
return new_path
def getNumPts( segmentType ):
if segmentType == "M":
return 1
if segmentType == "L":
return 1
if segmentType == "Q":
return 2
if segmentType == "C":
return 3
if segmentType == "Z":
return 0
return -1
def addSegment( path, segmentType, points ):
path.append([segmentType,points])
# Converts visible path segments (Z,L,Q) into absolute cubic segments (C).
def convertSegmentToCubic( current, segmentType, points, start ):
if segmentType == "H":
# print(current, points, start)
assert len(points) == 1
points.insert(0, current[0])
# points[0] += current[0]
# print(segmentType, current, points, start)
return convertSegmentToCubic(current, "L", points, start)
elif segmentType == "V":
# print(points)
assert len(points) == 1
points.append(current[1])
# points[1] += current[1]
# print(segmentType, current, points, start)
return convertSegmentToCubic(current, "L", points, start)
if segmentType == "M":
return "M";
if segmentType == "C":
return "C";
elif segmentType == "Z":
for i in range(0,6):
points.append(0.0)
points[4] = start[0]
points[5] = start[1]
thirdX = (points[4] - current[0]) / 3.0
thirdY = (points[5] - current[1]) / 3.0
points[2] = points[4]-thirdX
points[3] = points[5]-thirdY
points[0] = current[0]+thirdX
points[1] = current[1]+thirdY
return "C"
elif segmentType == "L":
for i in range(0,4):
points.append(0.0)
points[4] = points[0]
points[5] = points[1]
thirdX = (points[4] - current[0]) / 3.0
thirdY = (points[5] - current[1]) / 3.0
points[2] = points[4]-thirdX
points[3] = points[5]-thirdY
points[0] = current[0]+thirdX
points[1] = current[1]+thirdY
return "C"
elif segmentType == "Q":
for i in range(0,2):
points.append(0.0)
firstThirdX = (points[0] - current[0]) * 2.0 / 3.0
firstThirdY = (points[1] - current[1]) * 2.0 / 3.0
secondThirdX = (points[2] - points[0]) * 2.0 / 3.0
secondThirdY = (points[3] - points[1]) * 2.0 / 3.0
points[4] = points[2]
points[5] = points[3]
points[0] = current[0] + firstThirdX
points[1] = current[1] + firstThirdY
points[2] = points[2] - secondThirdX
points[3] = points[3] - secondThirdY
return "C"
elif segmentType == "A":
inkex.errormsg("Sorry, arcs are not supported in envelope or letter path!")
exit()
else:
inkex.errormsg("unsupported segment type: %s" % (segmentType))
return segmentType
# Normalizes the points of a path segment, so that they are expressed as percentage coordinates
# relative to the bounding box axes of the total shape.
# @param bounds The bounding box of the shape.
# @param points The points of the segment.
# @param percentages The returned points in normalized percentage form.
# @param numPts
def normalizePoints( bounds, points, percentages, numPts ):
# bounds has structure xmin,xMax,ymin,yMax
xmin,xMax,ymin,yMax = bounds
for i in range( 0, numPts ):
x = i*2
y = i*2+1
percentages[x] = (points[x] - xmin) / (xMax-xmin)
percentages[y] = (points[y] - ymin) / (yMax-ymin)
# Extracts 4 axes from a path. It is assumed that the path starts with a move, followed by 4 cubic paths.
# The extraction reverses the last 2 axes, so that they run in parallel with the first 2.
# @param path The path that is formed by the axes.
# @return The definition points of the 4 cubic path axes as float arrays, bundled in another array.
def extractMorphAxes( path ):
points = []
current = [ 0.0, 0.0 ]
start = [ 0.0, 0.0 ]
# the curved axis definitions go in here
axes = [None]*4
i = 0
for cmd, params in path:
points = params
cmd = convertSegmentToCubic( current, cmd, points, start )
if cmd is None:
return None
if cmd == "A":
inkex.errormsg("Sorry, arcs are not supported in envelope or letter path!")
return None
elif cmd == "M":
current[0] = points[0]
current[1] = points[1]
start[0] = points[0]
start[1] = points[1]
elif cmd == "C":
# 1st cubic becomes x axis 0
# 2nd cubic becomes y axis 1
# 3rd cubic becomes x axis 2 and is reversed
# 4th cubic becomes y axis 3 and is reversed
if i % 2 == 0:
index = i
else:
index = 4-i
if( i < 2 ):
# axes 1 and 2
axes[index] = [ current[0], current[1], points[0], points[1], points[2], points[3], points[4], points[5] ]
elif( i < 4 ):
# axes 3 and 4
axes[index] = [ points[4], points[5], points[2], points[3], points[0], points[1], current[0], current[1] ]
else:
# more than 4 axes - hopefully it was an unnecessary trailing Z
{}
current[0] = points[4]
current[1] = points[5]
i = i + 1
elif cmd == "Z":
#do nothing
{}
else:
inkex.errormsg("Unsupported segment type in envelope path: %s" % cmd)
return None
return axes
# Projects points in percentage coordinates into a morphed coordinate system that is framed
# by 2 x cubic curves (along the x axis) and 2 y cubic curves (along the y axis).
# @param axes The x and y axes of the envelope.
# @param percentage The current segment of the letter in normalized percentage form.
# @param morphed The array to hold the returned morphed path.
# @param numPts The number of points to be transformed.
def mapPointsToMorph( axes, percentage, morphed, numPts ):
# rename the axes for legibility
yCubic0 = axes[1]
yCubic1 = axes[3]
xCubic0 = axes[0]
xCubic1 = axes[2]
# morph each point
for i in range( 0, numPts ):
x = i*2
y = i*2+1
# tween between the morphed y axes according to the x percentage
tweenedY = tweenCubic( yCubic0, yCubic1, percentage[x] )
# get 2 points on the morphed x axes
xSpot0 = pointOnCubic( xCubic0, percentage[x] )
xSpot1 = pointOnCubic( xCubic1, percentage[x] )
# create a transform that stretches the y axis tween between these 2 points
yAnchor0 = [ tweenedY[0], tweenedY[1] ]
yAnchor1 = [ tweenedY[6], tweenedY[7] ]
xTransform = match( yAnchor0, yAnchor1, xSpot0, xSpot1 )
# map the y axis tween to the 2 points by applying the stretch transform
for j in range(0,4):
x2 = j*2
y2 = j*2+1
pointOnY = [tweenedY[x2],tweenedY[y2]]
Transform(xTransform).apply_to_point(pointOnY)
tweenedY[x2] = pointOnY[0]
tweenedY[y2] = pointOnY[1]
# get the point on the tweened and transformed y axis according to the y percentage
morphedPoint = pointOnCubic( tweenedY, percentage[y] )
morphed[x] = morphedPoint[0]
morphed[y] = morphedPoint[1]
# Calculates the point on a cubic bezier curve at the given percentage.
def pointOnCubic( c, t ):
point = [0.0,0.0]
_t_2 = t*t
_t_3 = _t_2*t
_1_t = 1-t
_1_t_2 = _1_t*_1_t
_1_t_3 = _1_t_2*_1_t
for i in range( 0, 2 ):
point[i] = c[i]*_1_t_3 + 3*c[2+i]*_1_t_2*t + 3*c[4+i]*_1_t*_t_2 + c[6+i]*_t_3
return point
# Tweens 2 bezier curves in a straightforward way,
# i.e. each of the points on the curve is tweened along a straight line
# between the respective point on key1 and key2.
def tweenCubic( key1, key2, percentage ):
tween = [0.0]*len(key1)
for i in range ( 0, len(key1) ):
tween[i] = key1[i] + percentage * (key2[i] - key1[i])
return tween
# Calculates a transform that matches 2 points to 2 anchors
# by rotating and scaling (up or down) along the axis that is formed by
# a line between the two points.
def match( p1, p2, a1, a2 ):
x = 0
y = 1
# distances
dp = [ p2[x]-p1[x], p2[y]-p1[y] ]
da = [ a2[x]-a1[x], a2[y]-a1[y] ]
# angles
angle_p = math.atan2( dp[x], dp[y] )
angle_a = math.atan2( da[x], da[y] )
# radians
#rp = math.sqrt( dp[x]*dp[x] + dp[y]*dp[y] )
#ra = math.sqrt( da[x]*da[x] + da[y]*da[y] )
rp = math.hypot( dp[x], dp[y] )
ra = math.hypot( da[x], da[y] )
# scale
scale = ra / rp
# transforms in the order they are applied
t1 = Transform( "translate(%f,%f)"%(-p1[x],-p1[y]) ).matrix
#t2 = Transform( "rotate(%f)"%(-angle_p) ).matrix
#t3 = Transform( "scale(%f,%f)"%(scale,scale) ).matrix
#t4 = Transform( "rotate(%f)"%angle_a ).matrix
t2 = rotateTransform(-angle_p)
t3 = scale_transform( scale, scale )
t4 = rotateTransform( angle_a )
t5 = Transform( "translate(%f,%f)"%(a1[x],a1[y]) ).matrix
# transforms in the order they are multiplied
t = t5
t = Transform(t) @ Transform(t4)
t = Transform(t) @ Transform(t3)
t = Transform(t) @ Transform(t2)
t = Transform(t) @ Transform(t1)
# return the combined transform
return t
def rotateTransform( a ):
return [[math.cos(a),-math.sin(a),0],[math.sin(a),math.cos(a),0]]
def scale_transform( sx, sy ):
return [[sx,0,0],[0,sy,0]]
if __name__ == '__main__':
BezierEnvelope().run()

View File

@ -0,0 +1,21 @@
[
{
"name": "Bezier Envelope",
"id": "fablabchemnitz.de.bezier_envelope",
"path": "bezier_envelope",
"dependent_extensions": null,
"original_name": "Bezier Envelope",
"original_id": "de.gerrit_karius.envelope",
"license": "GNU GPL v2",
"license_url": "https://github.com/shlomif/Bezier-Envelope-for-Inkscape/blob/master/LICENSE",
"comment": "",
"source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/bezier_envelope",
"fork_url": "https://github.com/shlomif/Bezier-Envelope-for-Inkscape",
"documentation_url": "https://stadtfabrikanten.org/display/IFM/Bezier+Envelope",
"inkscape_gallery_url": null,
"main_authors": [
"github.com/shlomif",
"github.com/eridur-de"
]
}
]

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Blobs Texture</name>
<id>fablabchemnitz.de.blobs</id>
<param type="notebook" name="Nmain">
<page name="top" gui-text="All">
<label>Fills a box with blobs.</label>
<param max="256" name="nb" type="int" gui-text="How many blobs?">10</param>
<param name="pgsizep" type="bool" gui-text="Default rectangle to page size?">true</param>
<param max="10000" name="rx" type="int" gui-text="Work area x">1000</param>
<param max="10000" name="ry" type="int" gui-text="Work area y">1000</param>
</page>
<page name="bottom" gui-text="Each">
<label>Each blob.</label>
<param max="256" name="num" type="int" gui-text="Interior points">10</param>
<param max="10000." name="sz" type="float" gui-text="Size of a blob">50.</param>
<param max="1.0" name="cave" type="float" gui-text="Concavity">0.</param>
<param max="10.0" name="blunt" type="float" gui-text="Corner bluntness">0.3</param>
</page>
</param>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu name="FabLab Chemnitz Shape Generators">
<submenu name="Streaks And Blobs" />
</submenu>
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">blobs.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,169 @@
#!/usr/bin/env python3
# These two lines are only needed if you don't put the script directly into
# the installation directory
import math
import inkex
import random
from lxml import etree
class Blobs(inkex.EffectExtension):
"""
Creates a random blob from a convex hull over n points.
The expected degree of the polygon is sqrt(n). The corners
are blunted by the blunt parameter. 0 means sharp. 1 will
result in loopy splines.
"""
def add_arguments(self, pars):
pars.add_argument("--pgsizep", type=inkex.Boolean, default=True, help="Default rectangle to page size?")
pars.add_argument('--num', type = int, default = 25, help = 'Number of random points to start with')
pars.add_argument('--blunt', type = float, default = 0.3, help = 'Bluntness of corners. Should be < 1')
pars.add_argument('--cave', type = float, default = 0.0, help = 'Concavity. Less blobby and more splatty')
pars.add_argument('--rx', type = int, default = 1000, help = 'Size of work area x')
pars.add_argument('--ry', type = int, default = 1000, help = 'Size of work area y')
pars.add_argument('--sz', type = float, default = 50., help = 'Size of a blob')
pars.add_argument('--nb', type = int, default = 10, help = 'Total number of blobs')
pars.add_argument("--Nmain", default='top', help="Active tab.")
def effect(self):
global cave
if self.options.pgsizep:
svg = self.document.getroot()
rx = int(self.svg.unittouu(svg.get('width')))
ry = int(self.svg.unittouu(svg.attrib['height']))
else:
rx = self.options.rx
ry = self.options.ry
blunt = self.options.blunt
cave = self.options.cave
sz = self.options.sz
nb = self.options.nb
num = self.options.num
# Get access to main SVG document element and get its dimensions.
svg = self.document.getroot()
# Create a new layer.
layer = etree.SubElement(svg, 'g')
layer.set(inkex.addNS('label', 'inkscape'), 'Blob Layer')
layer.set(inkex.addNS('groupmode', 'inkscape'), 'layer')
ctrs = [(random.randrange(rx) , random.randrange(ry))
for i in range(nb) ]
for ctr in ctrs :
points = [(random.gauss(ctr[0], sz) , random.gauss(ctr[1], sz))
for i in range(num) ]
px = hull(points)
pts = [points[px[i]] for i in range(len(px))]
# Create path element
path = etree.Element(inkex.addNS('path','svg'))
path.set('style', str(inkex.Style({'fill':'#000000'})))
pathstring = 'M ' + str(pts[0][0]) + ' ' + str(pts[0][1]) + ' '
for j in range(len(pts)):
k = (j+1) % len(pts)
kk = (j+2) % len(pts)
if j==0 :
(lasth, h1) = sHandles(pts[-1], pts[0], pts[1], blunt)
(h2, hnext) = sHandles(pts[j], pts[k], pts[kk], blunt)
pathstring += "C %f %f %f %f %f %f " % (h1[0], h1[1],
h2[0], h2[1],
pts[k][0], pts[k][1])
h1 = hnext
pathstring += 'Z'
path.set('d', pathstring)
layer.append(path)
def sHandles(pre, pt, post, blunt):
'''I'm proud of this cute little construction for the
spline handles. No doubt someone has thought of it before
but, if not, its name is ACHC Andrew's Cute Handle
Construction. Note: no trig function calls.'''
try :
slope = (post[1] - pt[1]) / (post[0] - pt[0])
except ZeroDivisionError :
slope = math.copysign(1E30 , post[1] - pt[1])
lenpre = distance(pre, pt)
lenpost = distance(pt, post)
lenr = lenpre**2 / lenpost
locx = math.copysign(lenr / math.sqrt(1. + slope**2) , post[0] - pt[0])
mark = (pre[0] - locx , pre[1] - locx*slope)
try :
markslope = (pt[1] - mark[1]) / (pt[0] - mark[0])
except ZeroDivisionError :
markslope = math.copysign(1E30 , pt[1] - mark[1])
prex = math.copysign(lenpre / math.sqrt(1. + markslope**2) ,
pt[0] - mark[0])
hpre = (pt[0] - prex*blunt , pt[1] - prex*markslope*blunt)
postx = prex*lenpost/lenpre
hpost = (pt[0] + postx*blunt , pt[1] + postx*markslope*blunt)
return (hpre, hpost)
"""Blunt=0.3 makes pleasingly round, mostly convex blobs. 0.4 makes them more
concave. 0.6 - 1.0 they're getting more and more pointy. 2.0 - 10. and they
grow appendages like hot-air balloons. 0.1 makes the corners pretty sharp.
0.0 and it's down to the convex hulls that are the basis of the blobs, that
is, polygons"""
def distance(a, b) :
return math.sqrt((a[0] - b[0])**2 + (a[1] - b[1])**2 )
def hull(arg):
"""Convex hull by Graham scan."""
xarr, yarr = zip(* arg)
ymin = min(yarr)
ind = findall(yarr, lambda y: y == ymin)
if len(ind) > 1 :
xshort = [xarr[j] for j in ind]
xmin = min(xshort)
j = ind[xshort.index(xmin)]
ind = j
else :
ind = ind[0]
all = list(range(len(xarr)))
del all[ind]
all.sort(key=lambda i : (xarr[i] - xarr[ind]) /
math.sqrt((xarr[i] - xarr[ind])**2 + (yarr[i] - yarr[ind])**2),
reverse=True)
if len(all) < 3 :
all.insert(0, ind)
return all
ans = [ind]
for i in all :
if len(ans) == 1 :
ans.append(i)
else :
while rightTurn(ans[-2], ans[-1], i, arg) :
ans.pop()
ans.append(i)
return ans
def rightTurn(j, k, l, arg) :
'''Cross product: Ax*By - Ay*Bx = Cz '''
ax = (arg[k][0] - arg[j][0])
by = (arg[l][1] - arg[k][1])
ay = (arg[k][1] - arg[j][1])
bx = (arg[l][0] - arg[k][0])
p = ax*by - ay*bx
dot = ax*bx + ay*by
cos = dot / math.sqrt((ax**2 + ay**2) * (bx**2 + by**2))
crt = 1 - cave*2
if p <= 0 :
return cos < crt #We forgive right turns based on /cave/
else :
return False
def findall(a, f):
r = []
for x, j in zip(a, range(len(a))) :
if f(x) :
r.append(j)
return r
if __name__ == '__main__':
Blobs().run()

View File

@ -0,0 +1,21 @@
[
{
"name": "Blobs Texture",
"id": "fablabchemnitz.de.blobs",
"path": "blobs",
"dependent_extensions": null,
"original_name": "Blobs texture",
"original_id": "org.ekips.filter.blobsTexture",
"license": "GNU GPL v2",
"license_url": "https://inkscape.org/~kurn/%E2%98%85blobs",
"comment": "",
"source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/blobs",
"fork_url": "https://inkscape.org/~kurn/%E2%98%85blobs",
"documentation_url": "https://stadtfabrikanten.org/display/IFM/Blobs+Texture",
"inkscape_gallery_url": null,
"main_authors": [
"inkscape.org/kurn",
"github.com/eridur-de"
]
}
]

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Blueprint Maker</name>
<id>fablabchemnitz.de.blueprint_maker</id>
<param name="palette" type="optiongroup" appearance="combo" gui-text="Palette:">
<option value="blueprint">Blueprint</option>
<option value="black">Screen</option>
<option value="white">Paper</option>
<option value="laser">Laser</option>
</param>
<param name="stroke_width" precision="4" type="float" min="0.001" max="10.000" gui-text="Line thickness:">1.000</param>
<param name="stroke_units" type="optiongroup" appearance="combo" gui-text="Line units:">
<option value="millimeters">mm</option>
<option value="centimeters">cm</option>
<option value="points">pt</option>
<option value="pixels">px</option>
</param>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu name="FabLab Chemnitz">
<submenu name="Modify existing Path(s)"/>
</submenu>
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">blueprint_maker.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,117 @@
#!/usr/bin/env python3
import inkex
import copy
class bluePrintMakerData():
def __init__(self,effect):
self.effect=effect
self.stroke_units=effect.options.stroke_units
self.unit_factor=1.0
self.set_units()
self.stroke_width=effect.options.stroke_width*self.unit_factor
self.palette=effect.options.palette
self.background_color=None
self.stroke_color=None
self.set_colors()
self.selected_nodes=[]
if len(effect.options.ids)==0:
self.selected_nodes=[effect.svg.getElementById(x) for x in effect.svg.get_ids()]
self.selected_nodes=[node for node in self.selected_nodes if effect.is_geometry(node)]
else:
self.selected_nodes=[y for x,y in effect.svg.selected.items()]
self.selected_nodes=[node for node in self.selected_nodes if effect.is_geometry(node,shapes=['path','g','rect','ellipse','perspective'])]
self.allowed_ids=[]
self.allowed_nodes=[]
self.set_objects()
def set_units(self):
if self.stroke_units=='millimeters':
self.unit_factor=3.543
if self.stroke_units=='centimeters':
self.unit_factor=35.433
if self.stroke_units=='points':
self.unit_factor=1.25
if self.stroke_units=='pixels':
self.unit_factor=1.0
def set_colors(self):
if self.palette=='blueprint':
self.background_color='#006fde'
self.stroke_color='#ffffff'
if self.palette=='black':
self.background_color='#000000'
self.stroke_color='#ffffff'
if self.palette=='white':
self.background_color='#ffffff'
self.stroke_color='#000000'
if self.palette=='laser':
self.background_color='#ffffff'
self.stroke_color='#ff0000'
def set_objects(self):
for current_id in self.effect.svg.get_ids():
node=self.effect.svg.getElementById(current_id)
if self.effect.is_geometry(node):
self.allowed_ids.append(current_id)
self.allowed_nodes.append(node)
class BluePrintMaker(inkex.EffectExtension):
def __init__(self):
inkex.Effect.__init__(self)
self.arg_parser.add_argument('-p', '--palette', help='Choose the colors...')
self.arg_parser.add_argument('-s', '--stroke_width', type=float, help='Stroke size...')
self.arg_parser.add_argument('-u', '--stroke_units', help='Choose the units...')
self.data=None
def is_a_group(self, node):
data=False
if node.tag==inkex.addNS('g','svg'):
data=True
return data
def is_geometry(self, node, shapes=['path','rect','ellipse','perspective']):
data=False
for s in shapes:
if node.tag==inkex.addNS(s,'svg'):
data=True
return data
def change_page_settings(self):
namedview=self.svg.namedview
namedview.set('pagecolor',self.data.background_color)
namedview.set(inkex.addNS('pageopacity', 'inkscape'),'1')
namedview.set(inkex.addNS('pageshadow', 'inkscape'),'0')
namedview.set('bordercolor',self.data.stroke_color)
namedview.set('borderopacity','1')
return None
def change_this_object(self,node):
styles=dict(inkex.Style.parse_str(node.get('style')))
styles_copy=copy.deepcopy(styles)
styles_copy['stroke']=self.data.stroke_color
styles_copy['stroke-width']=self.data.stroke_width
styles_copy['stroke-opacity']='1'
styles_copy['fill']='none'
styles_copy['fill-opacity']='1'
styles_copy['opacity']='1'
node.set('style',str(inkex.Style(styles_copy)))
return None
def iterate_on_objects(self,node=None):
if self.is_geometry(node):
self.change_this_object(node)
if self.is_a_group(node):
for current_node in list(node):
self.iterate_on_objects(current_node)
def effect(self):
self.data=bluePrintMakerData(self)
self.change_page_settings()
for node in self.data.selected_nodes:
self.iterate_on_objects(node)
return None
if __name__ == '__main__':
BluePrintMaker().run()

View File

@ -0,0 +1,21 @@
[
{
"name": "Blueprint Maker",
"id": "fablabchemnitz.de.blueprint_maker",
"path": "blueprint_maker",
"dependent_extensions": null,
"original_name": "Blueprint Maker",
"original_id": "org.inkscape.blueprint-maker",
"license": "GNU AGPL v3",
"license_url": "https://framagit.org/SxncR0OR/inkscape-extension-blueprint/-/blob/master/LICENSE.md",
"comment": "",
"source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/blueprint_maker",
"fork_url": "https://framagit.org/SxncR0OR/inkscape-extension-blueprint",
"documentation_url": "https://stadtfabrikanten.org/display/IFM/Blueprint+Maker",
"inkscape_gallery_url": null,
"main_authors": [
"framagit.org/SxncR0OR",
"github.com/eridur-de"
]
}
]

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Bounding Box</name>
<id>fablabchemnitz.de.bounding_box</id>
<label>Draws bounding boxes around selected objects, useful for debugging. Author: Pawel Mosakowski. Modded by Mario Voigt.</label>
<param name="unit" gui-text="Unit" type="optiongroup" appearance="combo" gui-description="The unit applies to interval and thresholds">
<option value="mm">mm</option>
<option value="cm">cm</option>
<option value="px">px</option>
<option value="pt">pt</option>
<option value="pc">pc</option>
<option value="in">in</option>
</param>
<param name="offset" min="-10000.000" max="10000.000" precision="3" type="float" gui-text="Offset from object (all directions)">0.000</param>
<param name="corner_radius" type="float" min="0.000" precision="3" max="10000.000" gui-text="Corner radius" gui-description="Only applies for box type">0.000</param>
<separator/>
<param name="box" type="bool" gui-text="Draw boxes">true</param>
<param name="circle" type="bool" gui-text="Draw circles">false</param>
<separator/>
<param name="split" type="bool" gui-text="Handle selection as group">true</param>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu name="FabLab Chemnitz">
<submenu name="Shape/Pattern from existing Path(s)"/>
</submenu>
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">bounding_box.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,64 @@
#!/usr/bin/env python3
import inkex
import math
from lxml import etree
class BoundingBox(inkex.EffectExtension):
def add_arguments(self, pars):
pars.add_argument('--offset', type=float, default=0.0, help='Offset from object (all directions)')
pars.add_argument('--unit', default="mm")
pars.add_argument('--box', type=inkex.Boolean, default=0.0, help='Draw boxes')
pars.add_argument('--corner_radius', type=float, default=0.0, help='Corner radius')
pars.add_argument('--circle', type=inkex.Boolean, default=0.0, help='Draw circles')
pars.add_argument('--split', type = inkex.Boolean, default = True, help = 'Handle selection as group')
def drawBBox(self, bbox):
so = self.options
offset = self.svg.unittouu(str(so.offset) + so.unit)
if self.options.box:
attribs = {
'style' : str(inkex.Style({'stroke':'#ff0000','stroke-width':str(self.svg.unittouu("1px")),'fill':'none'})),
'x' : str(bbox.left - offset),
'y' : str(bbox.top - offset),
'width' : str(bbox.width + 2 * offset),
'height': str(bbox.height + 2 * offset),
'ry' : str(self.options.corner_radius),
'rx' : str(self.options.corner_radius)
}
etree.SubElement(self.svg.get_current_layer(), inkex.addNS('rect','svg'), attribs)
if self.options.circle:
attribs = {
'style': str(inkex.Style({'stroke':'#ff0000','stroke-width':str(self.svg.unittouu("1px")),'fill':'none'})),
'cx' : str(bbox.center_x),
'cy' : str(bbox.center_y),
#'r' : str(bbox.width / 2 + offset),
'r' : str(math.sqrt((bbox.width + 2 * offset)* (bbox.width + 2 * offset) + (bbox.height + 2 * self.options.offset) * (bbox.height + 2 * self.options.offset)) / 2),
}
etree.SubElement(self.svg.get_current_layer(), inkex.addNS('circle','svg'), attribs)
def effect(self):
scale_factor = self.svg.unittouu("1px")
if len(self.svg.selected) > 0:
if self.options.split is False:
for element in self.svg.selected.values():
self.drawBBox(element.bounding_box())
else: #combined bbox
#self.drawBBox(self.svg.get_selected_bbox()) #works for InkScape (1:1.0+devel+202008292235+eff2292935) @ Linux and for Windows (but with deprecation)
#self.drawBBox(self.svg.selection.bounding_box()) #works for InkScape 1.1dev (9b1fc87, 2020-08-27)) @ Windows
bbox = inkex.BoundingBox()
for element in self.svg.selected.values():
if isinstance (element, inkex.ShapeElement) and element.tag != inkex.addNS('use','svg') and element.get('inkscape:groupmode') != 'layer': #bbox fails for svg:use elements and layers:
bbox += element.bounding_box()
self.drawBBox(bbox)
else:
inkex.errormsg('Please select some objects first.')
return
if __name__ == '__main__':
BoundingBox().run()

View File

@ -0,0 +1,20 @@
[
{
"name": "Bounding Box",
"id": "fablabchemnitz.de.bounding_box",
"path": "bounding_box",
"dependent_extensions": null,
"original_name": "Bounding Box",
"original_id": "fablabchemnitz.de.bounding_box",
"license": "GNU GPL v3",
"license_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/LICENSE",
"comment": "",
"source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/bounding_box",
"fork_url": null,
"documentation_url": "https://stadtfabrikanten.org/display/IFM/Bounding+Box",
"inkscape_gallery_url": null,
"main_authors": [
"github.com/eridur-de"
]
}
]

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Bouwkamp Code</name>
<id>fablabchemnitz.de.bouwkamp</id>
<param name="tab" type="notebook">
<page name="settings" gui-text="Settings">
<param name="bouwkamp_code" type="string" gui-text="Bouwkamp code:">21, 112, 112, [50, 35, 27], [8, 19], [15, 17, 11], [6, 24], [29, 25, 9, 2], [7, 18], [16], [42], [4, 37], [33]</param>
<param name="wrap_in_group" type="bool" gui-text="Wrap in group">true</param>
</page>
<page name="help" gui-text="Help">
<label xml:space="preserve">This Inkscape extension allows you to generate squared squares and squared rectangles from Bouwkamp codes and table codes.
You can paste Bouwkamp codes with or without various formatting characters (like brackets) and convert them to the corresponding squares.
</label>
</page>
</param>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu name="FabLab Chemnitz Shape Generators">
<submenu name="Puzzles/Mazes/Nests"/>
</submenu>
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">bouwkamp_code.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,163 @@
#!/usr/bin/env python3
'''
BSD 3-Clause License
Copyright (c) 2019, Pascal Wagler
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.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
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 HOLDER 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.
This Inkscape extension allows you to generate squared squares and squared rectangles from
Bouwkamp codes and table codes.
'''
import re
import inkex
from lxml import etree
class BouwkampCode(inkex.EffectExtension):
"""
This Inkscape extension allows you to generate squared squares and squared rectangles from
Bouwkamp codes and table codes.
"""
def add_arguments(self, pars):
pars.add_argument('--tab')
pars.add_argument('--bouwkamp_code', default='21, 112, 112, [50, 35, 27], [8, 19], [15, 17, 11], [6, 24], [29, 25, 9, 2], [7, 18], [16], [42], [4, 37], [33]', help='The Bouwkamp code.'
)
pars.add_argument('--wrap_in_group', type=inkex.Boolean, default=True, help='Should the generated items be wrapped inside a group.'
)
def effect(self):
# compute center of the view
center = self.svg.namedview.center
# create the group that holds all the elements
container = self.svg.get_current_layer()
if self.options.wrap_in_group:
group_attributes = {
inkex.addNS('label', 'inkscape'): 'BouwkampSquares',
'transform': 'translate' + str(center)
}
group = etree.SubElement(self.svg.get_current_layer(), 'g', group_attributes)
container = group
# parse the bouwkamp code string as a list
bouwkamp_code = self.parse_bouwkamp_code_from_string(self.options.bouwkamp_code)
# show an error message and exit if the bouwkamp code is invalid
try:
self.exception_on_invalid_bouwkamp_code(bouwkamp_code)
except ValueError as exception:
inkex.errormsg(str(exception))
return
# draw the bouwkamp code
self.draw_bouwkamp_code(container, center, bouwkamp_code)
@staticmethod
def exception_on_invalid_bouwkamp_code(bouwkamp_code):
"""
Raises a ValueError if the passed list is not a valid Bouwkamp code.
"""
if not bouwkamp_code: #len(bouwkamp_code) == 0
raise ValueError('Error: Invalid Bouwkamp code.\n\nThe Bouwkamp code is emtpy. ' +
'Please specify a valid Bouwkamp code.')
if len(bouwkamp_code) - 3 != bouwkamp_code[0]:
raise ValueError('Error: Invalid Bouwkamp code.\n\nThe Bouwkamp code has the wrong ' +
'length. The first number needs to specify how many squares ' +
'should be drawn.')
@staticmethod
def parse_bouwkamp_code_from_string(bouwkamp_code_string):
"""
Converts a Bouwkamp code string into a list of integers. Any parentheses, commas and
spaces are stripped. Extended Bouwkamp codes are not supported.
"""
# replace every character (except numbers) with a space
text = re.sub('[^0-9]', ' ', bouwkamp_code_string)
# collapse all spaces to just one space
text = re.sub(' {1,}', ' ', text).strip()
# split the string into small strings and convert them to integers
numbers = [int(x) for x in text.split(" ")]
return numbers
def draw_bouwkamp_code(self, parent, center, bouwkamp_code):
"""
Draws the passed Bouwkamp code (a list of integers) with rectangles.
"""
order = bouwkamp_code[0]
width = bouwkamp_code[1]
# height = bouwkamp_code[2]
code = bouwkamp_code[3:] # cut the first three elements away
i = 0
helper = [0] * 900
for rectangle in range(0, order):
i = 0
for j in range(1, width):
if helper[j] < helper[i]:
i = j
position = (i, helper[i])
dimension = (code[rectangle], code[rectangle])
self.draw_rectangle(position, dimension, parent, center)
for j in range(0, code[rectangle]):
helper[i+j] += code[rectangle]
def draw_rectangle(self, position, dimension, parent, center):
rectangle_style = {
'opacity': '1',
'stroke': '#000000',
'stroke-width': str(self.svg.unittouu('2px')),
'fill': '#FFFFFF'
}
transform = ""
if not self.options.wrap_in_group:
transform = 'translate' + str(center)
rectangle_attributes = {
'transform': transform,
'style': str(inkex.Style(rectangle_style)),
inkex.addNS('label', 'inkscape'): "Rectangle "+str(dimension[0]),
'x': str(position[0]),
'y': str(position[1]),
'width': str(dimension[0]),
'height': str(dimension[1])
}
etree.SubElement(parent, inkex.addNS('rect', 'svg'), rectangle_attributes)
if __name__ == '__main__':
BouwkampCode().run()

View File

@ -0,0 +1,21 @@
[
{
"name": "Bouwkamp Code",
"id": "fablabchemnitz.de.bouwkamp_code",
"path": "boukamp_code",
"dependent_extensions": null,
"original_name": "Bouwkamp code",
"original_id": "de.pascalwagler.inkscape.bouwkamp",
"license": "BSD-3-Clause License",
"license_url": "https://github.com/Wandmalfarbe/bouwkamp-code-generator/blob/master/LICENSE",
"comment": "ported to Inkscape v1 by Mario Voigt",
"source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/boukamp_code",
"fork_url": "https://github.com/Wandmalfarbe/bouwkamp-code-generator",
"documentation_url": "https://stadtfabrikanten.org/display/IFM/Bouwkamp+Code",
"inkscape_gallery_url": null,
"main_authors": [
"github.com/Wandmalfarbe",
"github.com/eridur-de"
]
}
]

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Convert To Localized Braille</name>
<id>fablabchemnitz.de.braille_l18n</id>
<param name="locale" gui-text="Alphabet:" type="optiongroup" appearance="combo">
<option value="en">English (ASCII)</option>
<option value="es">Spanish</option>
<option value="fr">French</option>
<option value="de">German</option>
<option value="gl">Galician</option>
<option value="eu">Basque</option>
<option value="ca">Catalan/Valencian</option>
</param>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu name="FabLab Chemnitz">
<submenu name="Text" />
</submenu>
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">braille_l18n.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,504 @@
#!/usr/bin/env python3
import inkex
# ---------------------------------
# UTILITIES
# Common standards
UPPERCASE_PREFIXES = {
chr(15): 0x2828, # uppercase prefix: https://codepoints.net/U+000F
}
LOUIS_BRAILLE_NUMBERS_PREFIX = 0x283c # Louis Braille's numbers prefix
LOUIS_BRAILLE_NUMBERS = { # Louis Braille's original numbers codification
"0": 0x281a,
"1": 0x2801,
"2": 0x2803,
"3": 0x2809,
"4": 0x2819,
"5": 0x2811,
"6": 0x280B,
"7": 0x281b,
"8": 0x2813,
"9": 0x280a,
}
# ---------------------
# English based locales
EN_ASCII = " A1B'K2L@CIF/MSP\"E3H9O6R^DJG>NTQ,*5<-U8V.%[$+X!&;:4\\0Z7(_?W]#Y)="
# Spanish based locales
ES_LETTERS = {
"A": 0x2801,
"B": 0x2803,
"C": 0x2809,
"D": 0x2819,
"E": 0x2811,
"F": 0x280B,
"G": 0x281b,
"H": 0x2813,
"I": 0x280a,
"J": 0x281a,
"K": 0x2805,
"L": 0x2807,
"M": 0x280d,
"N": 0x281d,
"Ñ": 0x283b,
"O": 0x2815,
"P": 0x280f,
"Q": 0x281f,
"R": 0x2817,
"S": 0x280e,
"T": 0x281e,
"U": 0x2825,
"V": 0x2827,
"W": 0x283a,
"X": 0x282d,
"Y": 0x283d,
"Z": 0x2835,
}
ES_SIGNS = {
" ": 0x2800, # braille space
"ª": 0x2801, # ordinal (feminine) -> same as A
"º": 0x2815, # ordinal (masculine) -> same as O
"&": 0x282f,
".": 0x2804,
",": 0x2802,
":": 0x2812,
";": 0x2806,
"¿": 0x2822,
"?": 0x2822,
"¡": 0x2816,
"!": 0x2816,
'"': 0x2826,
"(": 0x2823,
")": 0x281c,
# "[": 0x2837, collides with "Á" (Spanish and Catalan)
# "]": 0x283e, collides with "Ú" (Spanish and Catalan)
"*": 0x2814,
# math
"-": 0x2824,
"=": 0x2836,
"×": 0x2826, # multiplication
"÷": 0x2832, # division
"+": 0x2816,
"@": 0x2810,
}
ES_ACCENT_MARKS = {
"Á": 0x2837,
"É": 0x282e,
"Í": 0x280c,
"Ó": 0x282c,
"Ú": 0x283e,
"Ü": 0x2833,
}
ES_COMBINATIONS = {
# signs
"%": (0x2838, 0x2834),
"": (0x2838, 0x2834, 0x2834), # per mile
"/": (0x2820, 0x2802),
"\\": (0x2810, 0x2804),
"<": (0x2810, 0x2805),
">": (0x2828, 0x2802),
"|": (0x2838, 0x2807),
"{": (0x2810, 0x2807),
"}": (0x2838, 0x2802),
"": (0x2824, 0x2824), # two different unicode dashes
"": (0x2824, 0x2824),
"": (0x2804, 0x2804, 0x2804),
# legal
"©": (0x2823, 0x2828, 0x2809, 0x281c), # copyright
"®": (0x2823, 0x2828, 0x2817, 0x281c), # registered
"": (0x2823, 0x2828, 0x280f, 0x281c),
"🄯": (0x2823, 0x2828, 0x2807, 0x281c),
# currencies
"": (0x2838, 0x2811),
"$": (0x2838, 0x280e),
"¢": (0x2818, 0x2809),
"£": (0x2810, 0x282e),
"¥": (0x2838, 0x283d),
"": (0x2838, 0x283d),
}
CA_ACCENT_MARKS = {
"É": 0x283f,
"Í": 0x280c,
"Ó": 0x282a,
"Ú": 0x283e,
"À": 0x2837,
"È": 0x282e,
"Ò": 0x282c,
"Ï": 0x283b,
"Ü": 0x2833,
"Ç": 0x282f,
}
# French based locales
FR_LETTERS = {
"A": 0x2801,
"B": 0x2803,
"C": 0x2809,
"D": 0x2819,
"E": 0x2811,
"F": 0x280b,
"G": 0x281b,
"H": 0x2813,
"I": 0x280a,
"J": 0x281a,
"K": 0x2805,
"L": 0x2807,
"M": 0x280d,
"N": 0x281d,
"O": 0x2815,
"P": 0x280f,
"Q": 0x281f,
"R": 0x2817,
"S": 0x280e,
"T": 0x281e,
"U": 0x2825,
"V": 0x2827,
"W": 0x283a,
"X": 0x282d,
"Y": 0x283d,
"Z": 0x2835,
}
FR_ACCENT_MARKS = {
"É": 0x283f,
"À": 0x2837,
"È": 0x282e,
"Ù": 0x283e,
"Â": 0x2821,
"Ê": 0x2823,
"Î": 0x2829,
"Ô": 0x2839,
"Û": 0x2831,
"Ë": 0x282b,
"Ï": 0x283b,
"Ü": 0x2833,
"Ç": 0x282f,
"Œ": 0x282a, # oe ligature
}
FR_SIGNS = {
" ": 0x2800, # braille space
",": 0x2802,
";": 0x2806,
":": 0x2812,
".": 0x2832,
"?": 0x2822,
"!": 0x2816,
"«": 0x2836,
"»": 0x2836,
"": 0x2836,
"": 0x2836,
'"': 0x2836,
"": 0x2836,
"": 0x2836,
"(": 0x2826,
")": 0x2834,
"'": 0x2804,
"'": 0x2804,
"/": 0x280c,
"@": 0x281c,
"^": 0x2808, # elevation exponent
"-": 0x2824,
"+": 0x2816,
"×": 0x2814, # multiplication
"÷": 0x2812, # division
"=": 0x2836,
}
FR_COMBINATIONS = {
"": (0x282a, 0x2812, 0x2815), # bidirectional arrow
"": (0x282a, 0x2812, 0x2812), # left arrow
"": (0x2812, 0x2812, 0x2815), # right arrow
"": (0x2832, 0x2832, 0x2832), # unicode ellipsis
"": (0x2824, 0x2824),
"": (0x2824, 0x2824),
"_": (0x2810, 0x2824),
"[": (0x2818, 0x2826),
"]": (0x2834, 0x2803),
"°": (0x2810, 0x2815), # degrees
"§": (0x2810, 0x280f), # paragraph/section symbol
"&": (0x2810, 0x283f),
"\\": (0x2810, 0x280c),
"#": (0x2810, 0x283c),
"{": (0x2820, 0x2820, 0x2826),
"}": (0x2834, 0x2804, 0x2804),
# math
"µ": (0x2818, 0x280d), # micron
"π": (0x2818, 0x280f),
"": (0x2818, 0x2823),
"": (0x2818, 0x281c),
"<": (0x2810, 0x2823),
">": (0x2810, 0x281c),
"~": (0x2810, 0x2822),
"*": (0x2810, 0x2814),
"%": (0x2810, 0x282c),
"": (0x2810, 0x282c, 0x282c), # per mile
# legal
"©": (0x2810, 0x2809), # copyright
"®": (0x2810, 0x2817), # registered
"": (0x2810, 0x281e), # trademark
# currencies
"¢": (0x2818, 0x2809),
"": (0x2818, 0x2811),
"£": (0x2818, 0x2807),
"$": (0x2818, 0x280e),
"¥": (0x2818, 0x283d),
"": (0x2818, 0x283d),
}
# German based locales
DE_ACCENT_MARKS = {
"Ä": 0x281c,
"Ö": 0x282a,
"Ü": 0x2833,
}
DE_SIGNS = {
" ": 0x2800, # braille space
",": 0x2802,
";": 0x2806,
":": 0x2812,
"?": 0x2822,
"!": 0x2816,
"": 0x2826,
"": 0x2834,
"§": 0x282c,
".": 0x2804,
"": 0x2824,
"": 0x2820,
}
DE_COMBINATIONS = {
# signs
"ß": (0x282e,), # converted to 'SS' if uppercased, so defined in combinations
"|": (0x2810, 0x2824),
"[": (0x2818, 0x2837),
"]": (0x2818, 0x283e),
"/": (0x2818, 0x280c),
"`": (0x2820, 0x2826),
"´": (0x2820, 0x2834),
"/": (0x2810, 0x2802),
"&": (0x2810, 0x2825),
"*": (0x2820, 0x2814),
"": (0x2812, 0x2812, 0x2815),
"": (0x282a, 0x2812, 0x2812),
"": (0x282a, 0x2812, 0x2812, 0x2815),
"%": (0x283c, 0x281a, 0x2834),
"": (0x283c, 0x281a, 0x2834, 0x2834),
"°": (0x2808, 0x2834),
"": (0x2808, 0x2814),
"": (0x2808, 0x2814, 0x2814),
"@": (0x2808, 0x281c),
"_": (0x2808, 0x2838),
"#": (0x2808, 0x283c),
# currencies
"": (0x2808, 0x2811),
"$": (0x2808, 0x280e),
"¢": (0x2808, 0x2809),
"£": (0x2808, 0x2807),
# legal
"©": (0x2836, 0x2818, 0x2809, 0x2836),
"®": (0x2836, 0x2818, 0x2817, 0x2836),
}
# END: UTILITIES
# ---------------------------------
# LOCALE FUNCTIONS
def en_char_map(char):
"""English chars mapper.
Source: https://en.wikipedia.org/wiki/Braille_ASCII#Braille_ASCII_values
"""
try:
mapint = EN_ASCII.index(char.upper())
except ValueError:
return char
return chr(mapint + 0x2800)
def numbers_singleuppers_combinations_factory(
numbers_map,
singleuppers_map,
combinations_map, # also individual characters that are modified if uppercased
number_prefix,
uppercase_prefix,
):
"""Wrapper for various character mappers implementations."""
def char_mapper(char):
if char.isnumeric():
# numeric prefix + number
return "".join([chr(number_prefix), chr(numbers_map[char])])
try:
bcharint = singleuppers_map[char.upper()]
except KeyError:
try:
# combinations
return "".join([chr(num) for num in combinations_map[char]])
except KeyError:
return char
else:
# if uppercase, add uppercase prefix before letter
if char.isupper():
return "".join([chr(uppercase_prefix), chr(bcharint)])
return chr(bcharint)
return char_mapper
def es_char_map_loader():
"""Spanish/Galician chars mappers.
Source: https://sid.usal.es/idocs/F8/FDO12069/signografiabasica.pdf
"""
return numbers_singleuppers_combinations_factory(
LOUIS_BRAILLE_NUMBERS,
{
**ES_LETTERS,
**ES_ACCENT_MARKS,
**ES_SIGNS,
**UPPERCASE_PREFIXES,
},
ES_COMBINATIONS,
0x283c,
0x2828,
)
def eu_char_map_loader():
"""Euskera chars mapper.
Uses the sample implementation as Spanish but without accent marks.
Source: https://sid.usal.es/idocs/F8/FDO12069/signografiabasica.pdf
"""
return numbers_singleuppers_combinations_factory(
LOUIS_BRAILLE_NUMBERS,
{
**ES_LETTERS,
**ES_SIGNS,
**UPPERCASE_PREFIXES,
},
ES_COMBINATIONS,
0x283c,
0x2828,
)
def ca_char_map_loader():
"""Catalan/Valencian chars mappers. Uses the same implementation as
Spanish but different accent marks.
Source: https://sid.usal.es/idocs/F8/FDO12069/signografiabasica.pdf
"""
return numbers_singleuppers_combinations_factory(
LOUIS_BRAILLE_NUMBERS,
{
**ES_LETTERS,
**CA_ACCENT_MARKS,
**ES_SIGNS,
**UPPERCASE_PREFIXES,
},
ES_COMBINATIONS,
0x283c,
0x2828,
)
def fr_char_map_loader():
"""French chars mapper.
Source: https://sid.usal.es/idocs/F8/FDO12069/signografiabasica.pdf
"""
return numbers_singleuppers_combinations_factory(
LOUIS_BRAILLE_NUMBERS,
{
**FR_LETTERS,
**FR_ACCENT_MARKS,
**FR_SIGNS,
**UPPERCASE_PREFIXES,
},
FR_COMBINATIONS,
0x283c,
0x2828,
)
def de_char_map_loader():
"""German chars mapper.
- For letters, uses the same dictionary as French implementation.
Source: http://bskdl.org/textschrift.html
"""
return numbers_singleuppers_combinations_factory(
LOUIS_BRAILLE_NUMBERS,
{
**FR_LETTERS, # Same as French implementation
**DE_ACCENT_MARKS,
**DE_SIGNS,
**UPPERCASE_PREFIXES,
},
DE_COMBINATIONS,
0x283c,
0x2828,
)
# END: LOCALE FUNCTIONS
LOCALE_CHARMAPS = {
"en": en_char_map, # English
"es": es_char_map_loader, # Spanish
"fr": fr_char_map_loader, # French
"de": de_char_map_loader, # German
"gl": es_char_map_loader, # Galician
"eu": eu_char_map_loader, # Euskera
"ca": ca_char_map_loader, # Catalan/Valencian
}
# ---------------------------------
# EXTENSION
class BrailleL18n(inkex.TextExtension):
"""Convert to Braille giving a localized map of replacements."""
def add_arguments(self, parser):
parser.add_argument(
"-l", "--locale", type=str, dest="locale", default="en",
choices=LOCALE_CHARMAPS.keys(),
help="Locale to use converting to Braille.",
)
def process_chardata(self, text):
"""Replaceable chardata method for processing the text."""
chars_mapper = LOCALE_CHARMAPS[self.options.locale]
# `chars_mapper` could be a function loader or a characters mapper
# itself, so check if the characters mapper is loaded and load it
# if is created from a factory
if "loader" in chars_mapper.__name__:
chars_mapper = chars_mapper()
return ''.join(map(chars_mapper, text))
if __name__ == '__main__':
BrailleL18n().run()

View File

@ -0,0 +1,21 @@
[
{
"name": "Convert To Localized Braille",
"id": "fablabchemnitz.de.braille_l18n",
"path": "braille_l18n",
"dependent_extensions": null,
"original_name": "Convert to localized Braille",
"original_id": "org.inkscape.text.braille-l18n",
"license": "BSD-3-Clause License",
"license_url": "https://github.com/mondeja/inkscape-braille-l18n-ext/blob/master/LICENSE",
"comment": "",
"source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/braille-l18n",
"fork_url": "https://github.com/mondeja/inkscape-braille-l18n-ext",
"documentation_url": "https://stadtfabrikanten.org/display/IFM/Convert+To+Localized+Braille",
"inkscape_gallery_url": null,
"main_authors": [
"github.com/mondeja",
"github.com/eridur-de"
]
}
]

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Can Generator</name>
<id>fablabchemnitz.de.can_generator</id>
<param name="active-tab" type="notebook">
<page name="config" gui-text="Configuration">
<param name="diameter" type="int" min="3" max="1200" gui-text="Diameter of the can in mm">30</param>
<param name="overhang" type="int" min="0" max="1200" gui-text="Overhang of the can in mm">5</param>
<param name="height" type="int" min="3" max="1000" gui-text="Height of the can in mm">50</param>
<param name="angle" type="float" min="8" max="22.5" gui-text="Angle of the segments ( number of segments divisible by 2!)">22.5</param>
<param name="material" type="float" min="0.2" max="5" gui-text="Material thickness in mm">3.6</param>
<param name="bottom" type="bool" gui-text="Top and bottom?">false</param>
</page>
<page name="hilfe" gui-text="Hilfe">
<label xml:space="preserve">The idea of the can generator is to cut plywood so deep that it can be bent without breaking. From the bent wood the side of the can is formed. The lid is not completely round, but cut in segments. The number of segments corresponds to the cuts in the side of the can. For this purpose, the wood must be cut across the grain direction. The angle determines the number of segments. For example, 22.5 degrees gives 16 segments. Less should not be less.</label>
</page>
</param>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu name="FabLab Chemnitz Boxes/Papercraft">
<submenu name="Finger-jointed/Tabbed Boxes" />
</submenu>
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">can_generator.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,371 @@
#! /usr/bin/env python3
'''
@author: mini@revollo.de
member of the erfindergarden
Inkscape Erweiterung - Dosen Generator
13.01.2019
Danke an neon22 https://github.com/Neon22/inkscape_extension_template
Nach seiner Anleitung konnte ich dieses Programm erstellen.
'''
import inkex
from lxml import etree
from math import *
__version__ = '0.2'
def points_to_svgd(p, close = True):
""" convert list of points (x,y) pairs
into a closed SVG path list
"""
f = p[0]
p = p[1:]
svgd = 'M %.2f,%.2f ' % f
for x in p:
svgd += ' %.4f,%.4f' % x
if close:
svgd += 'z'
return svgd
def punkte_erstellen(punkte, x, y):
###Schreibt die aktuellen Koordinaten in die Punkteliste
punkte.append((x, y))
class CanGenerator(inkex.EffectExtension):
def add_arguments(self, pars):
pars.add_argument("--height", type=int, default = 50, help="Höhe der Dose")
pars.add_argument("--overhang", type=int, default = 40, help="Überstand des Deckels")
pars.add_argument("--diameter", type=int, default = 40, help="diameter der Dose")
pars.add_argument("--angle", type=float, default = 22.5, help="angle der segments")
pars.add_argument("--material", type=float, default = 3.6, help="Materialstärke")
pars.add_argument("--bottom", type=inkex.Boolean, default = False, help="Deckel und bottom?")
pars.add_argument("--active-tab", default='title', help="Active tab.")
self.deckel_punkte = []
self.deckel_pfad = []
self.seite_punkte = []
self.seite_pfad = []
self.ausschnitt_punkte = []
self.ausschnitt_pfad = []
self.ausschnitt_nummer = 0
self.einschnitt_punkte = []
self.einschnitt_pfad = []
self.einschnitt_nummer = 0
self.einschnitt_breite = 0.2 #Abstand zwischen den beiden Einschnittlinien
def pfad_erstellen(self, pfad, punkte):
# Die gesammelten x und y Koordinaten der Punkte werden in Pfade (d) umgewandelt.
pfad.append(points_to_svgd(punkte))
del punkte[:]
def pfade_schreiben(self):
###Schreibt alle Pfade nacheinander in die Szene
path_stroke = '#101010' # Farbe für die Dose
path_fill = 'none' # keine Füllung, nur eine Linie
path_stroke_width = '0.4' # can also be in form '0.6mm'
for nummer in range(len(self.pfade)):
# define style using basic dictionary
pfad_attribute = {'id': "pfad%d"%nummer, 'stroke': path_stroke,
'fill': path_fill, 'stroke-width': path_stroke_width,
'd': self.pfade[nummer]}
# add path to scene
pfad = etree.SubElement(self.topgroup, inkex.addNS('path','svg'), pfad_attribute )
def deckel_erstellen(self):
###Erstellt alle Punkte für den Aussenkreis des Deckels.
angle = self.angle
segments = int(360 / angle)
for segment_nr in range(segments + 1):
#y berechnen = Gegenkathete
y = sin(radians(angle)) * self.radius_mit_overhang * -1
#Innenangle berechnen
beta = 180 - 90 - angle
#Ankathete berechnen
b = sin(radians(beta)) * self.radius_mit_overhang
#x berechnen
x = self.radius_mit_overhang - b - self.overhang
punkte_erstellen(self.deckel_punkte, x, y)
angle += self.angle
self.deckel_schreiben()
def deckel_schreiben(self):
###Schreibt den Deckel ohne Ausschnitte in die Szene
path_stroke = '#0000ff' # Farbe für den Rand
path_fill = 'none' # keine Füllung, nur eine Linie
path_stroke_width = '0.6' # can also be in form '0.6mm'
#Punkte zu Pfad umwandeln
self.deckel_pfad = points_to_svgd(self.deckel_punkte, False)
# define style using basic dictionary
deckel_attribute = {'id': "rand", 'stroke': path_stroke, 'fill': path_fill,
'stroke-width': path_stroke_width , 'x': '0', 'y': '0', 'd': self.deckel_pfad}
# add path to scene
deckel = etree.SubElement(self.topgroup, inkex.addNS('path','svg'), deckel_attribute )
def ausschnitt_erstellen(self):
###Erstellt alle Punkte für den Aussenkreis des Deckels.
angle = self.angle
for segment_nr in range(self.segments):
###Punkt 1 wird berechnet
#y berechnen = Gegenkathete
y = sin(radians(angle)) * self.radius * -1
#Innenangle berechnen
beta = 180 - 90 - angle
#Ankathete berechnen
b = sin(radians(beta)) * self.radius
#x berechnen
x = self.radius - b
punkte_erstellen(self.ausschnitt_punkte, x, y)
angle += self.angle
###Punkt 2 wird berechnet
#y berechnen = Gegenkathete
y = sin(radians(angle)) * self.radius * -1
#Innenangle berechnen
beta = 180 - 90 - angle
#Ankathete berechnen
b = sin(radians(beta)) * self.radius
#x berechnen
x = self.radius - b
punkte_erstellen(self.ausschnitt_punkte, x, y)
###Punkt 3 wird berechnet
alpha = angle - (self.angle / 2)
beta = 180 - 90 - alpha
y += sin(radians(alpha)) * self.material
x += sin(radians(beta)) * self.material
angle += self.angle
punkte_erstellen(self.ausschnitt_punkte, x, y)
### Punkt 4 wird berechnet
alpha = 180 - alpha
beta = 180 - 90 - alpha
x -= sin(radians(alpha)) * self.ausschnitt_breite
y -= sin(radians(beta)) * self.ausschnitt_breite
punkte_erstellen(self.ausschnitt_punkte, x, y)
#
self.ausschnitt_schreiben()
del self.ausschnitt_punkte[:]
def ausschnitt_schreiben(self):
###Schreibt den Ausschnitte in die Szene
path_stroke = '#ff0000' # Farbe für den Rand
path_fill = 'none' # keine Füllung, nur eine Linie
path_stroke_width = '0.6' # can also be in form '0.6mm'
#Punkte zu Pfad umwandeln
self.ausschnitt_pfad = points_to_svgd(self.ausschnitt_punkte, True)
# define style using basic dictionary
ausschnitt_attribute = {'id': "ausschnitt_%s"%self.ausschnitt_nummer, 'stroke': path_stroke, 'fill': path_fill,
'stroke-width': path_stroke_width , 'x': '0', 'y': '0', 'd': self.ausschnitt_pfad}
self.ausschnitt_nummer += 1
# add path to scene
ausschnitt = etree.SubElement(self.topgroup, inkex.addNS('path','svg'), ausschnitt_attribute )
def seite_erstellen(self):
###Erstellt die Seite der Dose mit den Zinken und den Einschnitten###
x = 0
y = self.radius_mit_overhang + 10
punkte_erstellen(self.seite_punkte, x, y)
for item in range(int(self.segments / 2)):
y -= self.material
punkte_erstellen(self.seite_punkte, x, y)
x += self.ausschnitt_breite
punkte_erstellen(self.seite_punkte, x, y)
y += self.material
punkte_erstellen(self.seite_punkte, x, y)
x += self.ausschnitt_breite
punkte_erstellen(self.seite_punkte, x, y)
if self.bottom == False:
y += self.height - self.material
punkte_erstellen(self.seite_punkte, x, y)
x -= self.segments * self.ausschnitt_breite
punkte_erstellen(self.seite_punkte, x, y)
y -= self.height - self.material
punkte_erstellen(self.seite_punkte, x, y)
else:
y += self.height - self.material - self.material
punkte_erstellen(self.seite_punkte, x, y)
for item in range(int(self.segments / 2)):
x -= self.ausschnitt_breite
punkte_erstellen(self.seite_punkte, x, y)
y += self.material
punkte_erstellen(self.seite_punkte, x, y)
x -= self.ausschnitt_breite
punkte_erstellen(self.seite_punkte, x, y)
y -= self.material
punkte_erstellen(self.seite_punkte, x, y)
y -= self.height
punkte_erstellen(self.seite_punkte, x, y)
self.seite_schreiben()
def seite_schreiben(self):
###Schreibt die Seite in die Szene
path_stroke = '#0000ff' # Farbe für den Rand
path_fill = 'none' # keine Füllung, nur eine Linie
path_stroke_width = '0.6' # can also be in form '0.6mm'
#Punkte zu Pfad umwandeln
self.seite_pfad = points_to_svgd(self.seite_punkte, True)
# define style using basic dictionary
seite_attribute = {'id': "seite", 'stroke': path_stroke, 'fill': path_fill,
'stroke-width': path_stroke_width , 'x': '0', 'y': '0', 'd': self.seite_pfad}
# add path to scene
seite = etree.SubElement(self.topgroup, inkex.addNS('path','svg'), seite_attribute )
def einschnitte_erstellen(self):
###Erstellt die Einschnitte in die Seite
x = self.einschnitt_breite / -2
y = self.radius_mit_overhang + 10 - self.material
for segment_nr in range(self.segments - 1):
###Punkt 1 wird berechnet
x += self.ausschnitt_breite
punkte_erstellen(self.einschnitt_punkte, x, y)
###Punkt 2 wird berechnet
x += self.einschnitt_breite
punkte_erstellen(self.einschnitt_punkte, x, y)
###Punkt 3 wird berechnet
y += self.height
punkte_erstellen(self.einschnitt_punkte, x, y)
### Punkt 4 wird berechnet
x -= self.einschnitt_breite
punkte_erstellen(self.einschnitt_punkte, x, y)
y -= self.height
self.einschnitte_schreiben()
del self.einschnitt_punkte[:]
def einschnitte_schreiben(self):
###Schreibt die Einschnitte in die Seite
path_stroke = '#00ff00' # Farbe für die Einschnitte
path_fill = 'none' # keine Füllung, nur eine Linie
path_stroke_width = '0.6' # can also be in form '0.6mm'
#Punkte zu Pfad umwandeln
self.einschnitt_pfad = points_to_svgd(self.einschnitt_punkte, True)
# define style using basic dictionary
einschnitt_attribute = {'id': "einschnitt_%s"%self.einschnitt_nummer, 'stroke': path_stroke, 'fill': path_fill,
'stroke-width': path_stroke_width , 'x': '0', 'y': '0', 'd': self.einschnitt_pfad}
self.einschnitt_nummer += 1
# add path to scene
einschnitt = etree.SubElement(self.undergroup, inkex.addNS('path','svg'), einschnitt_attribute )
### -------------------------------------------------------------------
### This is your main function and is called when the extension is run.
def effect(self):
###Hauptprogramm
# holt die Parameter aus Inkscape
self.height = self.options.height
self.diameter = self.options.diameter
self.overhang = self.options.overhang
self.radius = self.diameter / 2
self.radius_mit_overhang = self.radius + self.overhang
self.angle = self.options.angle
self.bottom = self.options.bottom
self.material = self.options.material
self.segments = int(360 / self.angle)
#Ausschnittbreite errechnen
y = sin(radians(self.angle)) * self.radius
beta = 180 - 90 - self.angle
b = sin(radians(beta)) * self.radius
x = self.radius - b
self.ausschnitt_breite = sqrt((x * x) + (y * y))
# what page are we on
page_id = self.options.active_tab # sometimes wrong the very first time
#Eigenschaften der SVG auslesen und die Größe der Dose anpassen
svg = self.document.getroot()
#viewbox_size = '0 0 ' + str(self.breite) + ' ' + str(self.height)
#svg.set('viewBox', viewbox_size)
#svg.set('height', str(self.height))
#svg.set('width', str(self.breite))
# Embed the path in a group to make animation easier:
# Be sure to examine the internal structure by looking in the xml editor inside inkscape
# Make a nice useful name
g_attribs = { inkex.addNS('label','inkscape'): 'dosen-gruppe', 'id': "dose",}
# add the group to the document's current layer
self.topgroup = etree.SubElement(self.svg.get_current_layer(), 'g', g_attribs )
# Create SVG Path under this top level group
# Make a nice useful name
g_attribs = { inkex.addNS('label','inkscape'): 'einschnitt-gruppe', 'id': "einschnitte",}
# add the group to the document's current layer
self.undergroup = etree.SubElement(self.svg.get_current_layer(), 'g', g_attribs )
# Create SVG Path under this top level group
self.deckel_erstellen()
self.ausschnitt_erstellen()
self.seite_erstellen()
self.einschnitte_erstellen()
# Make a nice useful name
text_g_attribs = { inkex.addNS('label','inkscape'): 'dosen-gruppe', 'id': "Branding",}
# add the group to the document's current layer
textgroup = etree.SubElement(self.svg.get_current_layer(), 'g', text_g_attribs )
line_style = {'font-size': '10px', 'font-style':'normal', 'font-weight': 'normal',
'fill': '#ff0000', 'font-family': 'Consolas',
'text-anchor': 'start'}
branding_line_attribs = {inkex.addNS('label','inkscape'): 'branding-text',
'id': 'front text',
'style': str(inkex.Style(line_style)),
'x': str(0),
'y': str(0)
}
branding_line = etree.SubElement(textgroup, inkex.addNS('text','svg'), branding_line_attribs)
branding_line.text = 'dosen-generator by mini revollo member of the erfindergarden'
# Make a nice useful name
einschnitt_text_g_attribs = { inkex.addNS('label','inkscape'): 'einschnitt-gruppe', 'id': "Einschnitte_Text",}
# add the group to the document's current layer
textgroup = etree.SubElement(self.svg.get_current_layer(), 'g', einschnitt_text_g_attribs )
line_style = {'font-size': '5px', 'font-style':'normal', 'font-weight': 'normal',
'fill': '#00ff00', 'font-family': 'Consolas',
'text-anchor': 'start'}
einschnitt_line_attribs = {inkex.addNS('label','inkscape'): 'Einschnitte_text',
'id': 'front text',
'style': str(inkex.Style(line_style)),
'x': str(0),
'y': str(self.radius_mit_overhang + self.height / 2)
}
branding_line = etree.SubElement(textgroup, inkex.addNS('text','svg'), einschnitt_line_attribs)
branding_line.text = 'Die Einschnitte nur zu 70 Prozent in das Material lasern'
if __name__ == '__main__':
CanGenerator().run()

View File

@ -0,0 +1,21 @@
[
{
"name": "Can Generator",
"id": "fablabchemnitz.de.can_generator",
"path": "can_generator",
"dependent_extensions": null,
"original_name": "Dosengenerator",
"original_id": "githubacct.uniqueid.Dosengenerator",
"license": "GNU GPL v3",
"license_url": "https://github.com/minirevollo/Inkscape-Dosen-Generator/blob/master/LICENSE",
"comment": "ported to Inkscape v1 by Mario Voigt",
"source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/can_generator",
"fork_url": "https://github.com/minirevollo/Inkscape-Dosen-Generator",
"documentation_url": "https://stadtfabrikanten.org/display/IFM/Can+Generator",
"inkscape_gallery_url": null,
"main_authors": [
"github.com/minirevollo",
"github.com/eridur-de"
]
}
]

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Cards Layout Guides</name>
<id>fablabchemnitz.de.card_layout_guides</id>
<param name="layout" type="optiongroup" appearance="combo" gui-text="Layout Type">
<option value="SIMPLE">Simple Grid</option>
<option value="FOLDING">Folding Grid</option>
</param>
<param name="card_width" type="float" precision="1" min="5" max="1000" gui-text="Card width (mm)">60</param>
<param name="card_height" type="float" precision="1" min="5" max="1000" gui-text="Card height (mm)">30</param>
<param name="orientation" type="optiongroup" appearance="combo" gui-text="Card Orientation">
<option value="HORIZONTAL">Horizontal</option>
<option value="VERTICAL">Vertical</option>
</param>
<param name="card_margin" type="float" precision="1" min="0" max="200" gui-text="Card margin(mm)">2</param>
<param name="bleed_margin" type="float" precision="1" min="0" max="200" gui-text="Bleed margin(mm)">2</param>
<param name="page_margin" type="float" precision="1" min="0" max="200" gui-text="Page margin(mm)">15</param>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu name="FabLab Chemnitz">
<submenu name="Grids/Guides"/>
</submenu>
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">card_layout_guides.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,480 @@
#!/usr/bin/env python3
# Copyright 2016 Luke Phillips (lukerazor@hotmail.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 St, Fifth Floor, Boston, MA 02110-1301 USA
# Extension dirs
# linux:~/.config/inkscape/extensions
# windows: [Drive]:\Program Files\Inkscape\share\extensions
from lxml import etree
import inkex
import copy
import math
FOLD_GAP = 5
CROP_GAP = 2
CROP_LENGTH = 3
inkex.NSS[u'cs'] = u'http://www.razorfoss.org/cardlayoutguides/'
def PrintDebug(string):
inkex.utils.debug( _(str(string)) )
def RoundAndDeduplicatePoints(points):
return sorted(list(set(map(lambda x: round(x, 3), points))))
class Point():
def __init__(self, x, y):
self.x = x
self.y = y
def rotate(self, angle, origin):
"""
Rotate a point counterclockwise by a given angle around a given origin.
The angle should be given in degrees.
"""
rads = math.radians(angle)
newX = origin.x + math.cos(rads) * (self.x - origin.x) - math.sin(rads) * (self.y - origin.y)
newY = origin.y + math.sin(rads) * (self.x - origin.x) + math.cos(rads) * (self.y - origin.y)
return Point(newX, newY)
def add(self, point):
return Point(self.x + point.x, self.y + point.y)
@staticmethod
def parsePoint(pointString):
x, y = map(lambda v: float(v), pointString.split(","))
return Point(x, y)
@staticmethod
def parse(pointString, orientationString=None):
p1 = Point.parsePoint(pointString)
p = Point(p1.x, p1.y)
if orientationString != None:
po = Point.parsePoint(orientationString)
p = p1.add(po.rotate(270, Point(0, 0)))
return p
class LineGeneratorBase(object):
def __init__(self, card_width, card_height, card_margin, bleed_margin, pageWidth, pageHeight, page_margin, unitConverterFunc):
self.UnitConverterFunc = unitConverterFunc
self.card_width = card_width
self.card_height = card_height
self.card_margin = card_margin
self.bleed_margin = bleed_margin
self.PageWidth = pageWidth
self.PageHeight = pageHeight
self.page_margin = page_margin
self.ContainerWidth = -1
self.ContainerHeight = -1
self.GuideOffsetsWithFold = [
0,
self.bleed_margin, self.card_margin, self.card_height - 2*self.card_margin, self.card_margin, self.bleed_margin,
2*FOLD_GAP,
self.bleed_margin, self.card_margin, self.card_height - 2*self.card_margin, self.card_margin, self.bleed_margin
]
self.GuideOffsetsNoFold = [
0,
self.bleed_margin, self.card_margin,
self.card_width - 2*self.card_margin,
self.card_margin, self.bleed_margin
]
def CalcPageLeftMargin(self):
return (self.PageWidth - self.ContentWidth) / 2.0
def CalcPageBottomMargin(self):
return (self.PageHeight - self.ContentHeight) / 2.0
def DrawGuide(self, xmlParent, xpos, ypos):
posString = "{},{}".format(xpos, ypos)
attribs = {'position': posString, 'orientation': posString}
etree.SubElement(xmlParent, inkex.addNS('guide',"sodipodi"), attribs)
def ConvertPoint(self, p):
# convert point into svg approriate values, including catering for inkscapes "alternative" axis sytem ie 0, 0 is bottom left not top left
newX = self.UnitConverterFunc("{}mm".format(p.x))
newY = self.PageHeight - self.UnitConverterFunc("{}mm".format(p.y))
return Point(newX, newY)
def DrawLine(self, xmlParent, p1, p2):
cp1 = self.ConvertPoint(p1)
cp2 = self.ConvertPoint(p2)
pathStr = "M {},{} {}, {}".format(cp1.x, cp1.y, cp2.x, cp2.y)
style = {'stroke': '#000000', 'stroke-width': self.UnitConverterFunc('0.25mm'), 'fill': 'none'}
attribs = {'style': str(inkex.Style(style)), 'd': pathStr}
etree.SubElement(xmlParent, inkex.addNS('path','svg'), attribs )
def DrawVerticleGuides(self, xmlParent, positions, gap):
curPos = self.CalcPageLeftMargin()
lastPos = -1
while curPos + self.ContainerWidth <= self.PageWidth - self.page_margin:
for offset in positions:
curPos += offset
if curPos != lastPos: # don't double draw
self.DrawGuide(xmlParent, curPos, 0)
lastPos = curPos
curPos += gap
def DrawHorizontalGuides(self, xmlParent, positions, gap):
curPos = self.CalcPageBottomMargin()
lastPos = -1
while curPos + self.ContainerHeight <= self.PageHeight - self.page_margin:
for offset in positions:
curPos += offset
if curPos != lastPos: # don't double draw
self.DrawGuide(xmlParent, 0, curPos)
lastPos = curPos
curPos += gap
def GenerateFoldLines(self, xmlParent):
lines = self.GetFoldLinePositions()
for line in lines:
self.DrawLine(xmlParent, line[0], line[1])
def GenerateCropMarks(self, xmlParent):
lines = self.GetCropMarkLines()
for line in lines:
self.DrawLine(xmlParent, line[0], line[1])
@staticmethod
def CreateLineGenerator(layout, orientation, card_width, card_height, card_margin, bleed_margin, pageWidth, pageHeight, page_margin, unitConverterFunc):
if layout == "SIMPLE":
return SimpleGridLineGenerator(orientation, card_width, card_height, card_margin, bleed_margin, pageWidth, pageHeight, page_margin, unitConverterFunc)
if orientation == "HORIZONTAL":
return LineGeneratorForHorizontalCards(card_width, card_height, card_margin, bleed_margin, pageWidth, pageHeight, page_margin, unitConverterFunc)
return LineGeneratorForVerticalCards(card_width, card_height, card_margin, bleed_margin, pageWidth, pageHeight, page_margin, unitConverterFunc)
class SimpleGridLineGenerator(LineGeneratorBase):
def __init__(self, orientation, card_width, card_height, card_margin, bleed_margin, pageWidth, pageHeight, page_margin, unitConverterFunc):
if orientation == "HORIZONTAL":
super(SimpleGridLineGenerator, self).__init__(card_height, card_width, card_margin, bleed_margin, pageWidth, pageHeight, page_margin, unitConverterFunc)
else:
super(SimpleGridLineGenerator, self).__init__(card_width, card_height, card_margin, bleed_margin, pageWidth, pageHeight, page_margin, unitConverterFunc)
self.ContainerWidth = self.card_width + 2 * bleed_margin
self.ContainerHeight = self.card_height + 2 * bleed_margin
# num across
# num down
self.NumContainersAcross = int((self.PageWidth - 2*self.page_margin) // self.ContainerWidth) # round down division
self.NumContainersDown = int((self.PageHeight - 2*self.page_margin) // self.ContainerHeight) # round down division
# content sizes
self.ContentWidth = self.NumContainersAcross * self.ContainerWidth
self.ContentHeight = self.NumContainersDown * self.ContainerHeight
def GenerateGuides(self, xmlParent):
horizontalOffsets = self.GuideOffsetsNoFold = [
0,
self.bleed_margin, self.card_margin,
self.card_width - 2*self.card_margin,
self.card_margin, self.bleed_margin
]
verticalOffsets = self.GuideOffsetsNoFold = [
0,
self.bleed_margin, self.card_margin,
self.card_height - 2*self.card_margin,
self.card_margin, self.bleed_margin
]
self.DrawVerticleGuides(xmlParent, horizontalOffsets, 0)
self.DrawHorizontalGuides(xmlParent, verticalOffsets, 0)
def GetFoldLinePositions(self):
return [] # no fold lines in simple grid
def GetCropMarkLines(self):
lines = []
leftMargin = self.CalcPageLeftMargin()
bottomMargin = self.CalcPageBottomMargin()
#determine all horizontal crop marks, duplicates possible
# figure out the ypos
horizontal_ypos = []
for idx in range(self.NumContainersDown):
bottomY = self.bleed_margin
topY = bottomY + self.card_height
containerOffset = bottomMargin + idx*self.ContainerHeight
horizontal_ypos.append(containerOffset + bottomY)
horizontal_ypos.append(containerOffset + topY)
horizontal_ypos = RoundAndDeduplicatePoints(horizontal_ypos) # remove duplicate positions
horizontal_xpos = [leftMargin - CROP_GAP, self.PageWidth - leftMargin + CROP_GAP + CROP_LENGTH]
for xpos in horizontal_xpos:
for ypos in horizontal_ypos:
lines.append([
Point(xpos - CROP_LENGTH, ypos),
Point(xpos, ypos)
])
#determine all vertical crop marks, duplicates possible
# figure out the xpos
vertical_xpos = []
for idx in range(self.NumContainersAcross):
leftX = self.bleed_margin
rightX = leftX + self.card_width
containerOffset = leftMargin + idx*self.ContainerWidth
vertical_xpos.append(containerOffset + leftX)
vertical_xpos.append(containerOffset + rightX)
vertical_xpos = RoundAndDeduplicatePoints(vertical_xpos) # remove duplicate positions
vertical_ypos = [bottomMargin - CROP_GAP, self.PageHeight - bottomMargin + CROP_GAP + CROP_LENGTH]
for xpos in vertical_xpos:
for ypos in vertical_ypos:
lines.append([
Point(xpos, ypos),
Point(xpos, ypos - CROP_LENGTH)
])
return lines
class LineGeneratorForVerticalCards(LineGeneratorBase):
def __init__(self, card_width, card_height, card_margin, bleed_margin, pageWidth, pageHeight, page_margin, unitConverterFunc):
super(LineGeneratorForVerticalCards, self).__init__(card_width, card_height, card_margin, bleed_margin, pageWidth, pageHeight, page_margin, unitConverterFunc)
self.ContainerWidth = card_width + 2*bleed_margin
self.ContainerHeight = 2*(card_height + 2*bleed_margin + FOLD_GAP)
# num across
self.NumContainersAcross = int((self.PageWidth - 2*self.page_margin) // self.ContainerWidth) # round down division
# num down
contentHeight = lambda n: n * self.ContainerHeight + (n - 1)*(2*FOLD_GAP)
workingHeight = self.PageHeight - 2*self.page_margin
self.NumContainersDown = int(workingHeight // self.ContainerHeight) # round down division for nominal value
if contentHeight(self.NumContainersDown) > workingHeight:
self.NumContainersDown -= 1
# content sizes
self.ContentWidth = self.NumContainersAcross * self.ContainerWidth
self.ContentHeight = contentHeight(self.NumContainersDown)
def GenerateGuides(self, xmlParent):
self.DrawVerticleGuides(xmlParent, self.GuideOffsetsNoFold, 0)
self.DrawHorizontalGuides(xmlParent, self.GuideOffsetsWithFold, 2*FOLD_GAP)
def GetFoldLinePositions(self):
lines = []
leftMargin = self.CalcPageLeftMargin()
for idx in range(self.NumContainersDown):
foldY = self.CalcPageBottomMargin() + idx*(self.ContainerHeight + 2*FOLD_GAP) + self.ContainerHeight/2
lines.append([Point(leftMargin, foldY), Point(self.PageWidth - leftMargin, foldY)])
return lines
def GetCropMarkLines(self):
lines = []
leftMargin = self.CalcPageLeftMargin()
bottomMargin = self.CalcPageBottomMargin()
vertical_ypos = []
# determine all of the hornzontal facing crop marks, no duplicates possible
for idx in range(self.NumContainersDown):
bottomY = self.bleed_margin
topY = bottomY + self.card_height
containerOffset = bottomMargin + idx*(self.ContainerHeight + 2*FOLD_GAP)
vertical_ypos += [
containerOffset - CROP_GAP,
containerOffset + 2*self.bleed_margin + 2*self.card_margin + self.card_height + CROP_GAP + CROP_LENGTH
] # stash for later
for ypos in [containerOffset + bottomY, containerOffset + topY]:
for xpos in [leftMargin - CROP_GAP, self.PageWidth - leftMargin + CROP_GAP + CROP_LENGTH]:
lines.append([
Point(xpos, ypos),
Point(xpos - CROP_LENGTH, ypos)
])
#determine all vertical crop marks, duplicates possible
# figure out the xpos
vertical_xpos = []
for idx in range(self.NumContainersAcross):
leftX = self.bleed_margin
rightX = leftX + self.card_width
containerOffset = leftMargin + idx*self.ContainerWidth
vertical_xpos.append(containerOffset + leftX)
vertical_xpos.append(containerOffset + rightX)
vertical_xpos = list(set(vertical_xpos)) # remove duplicate positions
for xpos in vertical_xpos:
for ypos in vertical_ypos:
lines.append([
Point(xpos, ypos),
Point(xpos, ypos - CROP_LENGTH)
])
return lines
class LineGeneratorForHorizontalCards(LineGeneratorBase):
def __init__(self, card_width, card_height, card_margin, bleed_margin, pageWidth, pageHeight, page_margin, unitConverterFunc):
super(LineGeneratorForHorizontalCards, self).__init__(card_width, card_height, card_margin, bleed_margin, pageWidth, pageHeight, page_margin, unitConverterFunc)
self.ContainerWidth = 2*(card_height + 2 * bleed_margin + FOLD_GAP)
self.ContainerHeight = card_width + 2 * bleed_margin
# num across
contentWidth = lambda n: n * self.ContainerWidth + (n - 1)*(2*FOLD_GAP)
workingWidth = self.PageWidth - 2*self.page_margin
self.NumContainersAcross = int(workingWidth // self.ContainerWidth) # round down division for nominal value
if contentWidth(self.NumContainersAcross) > workingWidth:
self.NumContainersAcross -= 1
# num down
self.NumContainersDown = int((self.PageHeight - 2*self.page_margin) // self.ContainerHeight) # round down division
# content sizes
self.ContentWidth = contentWidth(self.NumContainersAcross)
self.ContentHeight = self.NumContainersDown * self.ContainerHeight
def GenerateGuides(self, xmlParent):
self.DrawVerticleGuides(xmlParent, self.GuideOffsetsWithFold, 2*FOLD_GAP)
self.DrawHorizontalGuides(xmlParent, self.GuideOffsetsNoFold, 0)
def GetFoldLinePositions(self):
lines = []
bottomMargin = self.CalcPageBottomMargin()
for idx in range(self.NumContainersAcross):
foldX = self.CalcPageLeftMargin() + idx*(self.ContainerWidth + 2*FOLD_GAP) + self.ContainerWidth/2
lines.append([Point(foldX, bottomMargin), Point(foldX, self.PageHeight - bottomMargin)])
return lines
def GetCropMarkLines(self):
lines = []
leftMargin = self.CalcPageLeftMargin()
bottomMargin = self.CalcPageBottomMargin()
horizontal_xpos = []
# determine all of the vertical facing crop marks, no duplicates possible
for idx in range(self.NumContainersAcross):
leftX = self.bleed_margin
rightX = leftX + self.card_height
containerOffset = leftMargin + idx*(self.ContainerWidth + 2*FOLD_GAP)
horizontal_xpos += [
containerOffset - CROP_GAP,
containerOffset + 2*self.bleed_margin + 2*self.card_margin + self.card_height + CROP_GAP + CROP_LENGTH
] # stash for later
for xpos in [containerOffset + leftX, containerOffset + rightX]:
for ypos in [bottomMargin - CROP_GAP, self.PageHeight - bottomMargin + CROP_GAP + CROP_LENGTH]:
lines.append([
Point(xpos, ypos),
Point(xpos, ypos - CROP_LENGTH)
])
#determine all horizontal crop marks, duplicates possible
# figure out the xpos
horizontal_ypos = []
for idx in range(self.NumContainersDown):
bottomY = self.bleed_margin
topY = bottomY + self.card_width
containerOffset = bottomMargin + idx*self.ContainerHeight
horizontal_ypos.append(containerOffset + bottomY)
horizontal_ypos.append(containerOffset + topY)
horizontal_ypos = RoundAndDeduplicatePoints(horizontal_ypos) # remove duplicate positions
for ypos in horizontal_ypos:
for xpos in horizontal_xpos:
lines.append([
Point(xpos, ypos),
Point(xpos - CROP_LENGTH, ypos)
])
return lines
class CardLayoutGuides(inkex.EffectExtension):
def add_arguments(self, pars):
pars.add_argument('-l', '--layout')
pars.add_argument('-w', '--card_width', type = float)
pars.add_argument('-d', '--card_height', type = float)
pars.add_argument('-o', '--orientation')
pars.add_argument('-c', '--card_margin', type = float)
pars.add_argument('-b', '--bleed_margin', type = float)
pars.add_argument('-p', '--page_margin', type = float)
def effect(self):
# find dimensions of page
pageWidth = inkex.units.convert_unit(self.svg.viewport_width, "mm", "px")
pageHeight = inkex.units.convert_unit(self.svg.viewport_height, "mm", "px")
opt = self.options
guideParent = self.document.xpath('//sodipodi:namedview',namespaces=inkex.NSS)[0]
### GUIDES
# remove all the existing guides
[node.delete() for node in self.document.xpath('//sodipodi:guide',namespaces=inkex.NSS)]
# create the generator object
gen = LineGeneratorBase.CreateLineGenerator(opt.layout, opt.orientation, opt.card_width, opt.card_height, opt.card_margin, opt.bleed_margin, pageWidth, pageHeight, opt.page_margin, self.svg.unittouu)
gen.GenerateGuides(guideParent)
### FOLD LINES
# remove any existing 'Crop marks' layer
[node.delete() for node in self.document.xpath("//svg:g[@inkscape:label='Crop Marks']",namespaces=inkex.NSS)]
svg = self.document.xpath('//svg:svg', namespaces=inkex.NSS)[0]
layer = etree.SubElement(svg, inkex.addNS('g',"svg"), {})
layer.set(inkex.addNS('label', 'inkscape'), "Crop Marks")
layer.set(inkex.addNS('groupmode', 'inkscape'), 'layer')
#layer.set(inkex.addNS('insensitive', 'sodipodi'), 'true')
gen.GenerateFoldLines(layer)
### CROP MARKS
gen.GenerateCropMarks(layer)
if __name__ == '__main__':
CardLayoutGuides().run()

View File

@ -0,0 +1,21 @@
[
{
"name": "Card Layout Guides",
"id": "fablabchemnitz.de.card_layout_guides",
"path": "card_layout_guides",
"dependent_extensions": null,
"original_name": "Cards",
"original_id": "phillips.effect.cardlayoutguides",
"license": "GNU GPL v2",
"license_url": "https://sourceforge.net/p/razorfoss/svn/HEAD/tree/trunk/Inkscape/LayoutGuides/CardLayoutGuides.py",
"comment": "",
"source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/card_layout_guides",
"fork_url": "https://sourceforge.net/p/razorfoss/svn/HEAD/tree/trunk/Inkscape/LayoutGuides/",
"documentation_url": "https://stadtfabrikanten.org/display/IFM/Card+Layout+Guides",
"inkscape_gallery_url": null,
"main_authors": [
"Luke Phillips:lukerazor@hotmail.com",
"github.com/eridur-de"
]
}
]

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Checkerboard</name>
<id>fablabchemnitz.de.checkerboard</id>
<param name="tab" type="notebook">
<page name="params" gui-text="Params">
<param name="size" type="string" gui-text="Cell size" gui-description="Enter value + unit, e.g. px, pt, mm, cm, in">50px</param>
<param name="rows" type="int" min="2" max="1000" gui-text="Rows">10</param>
<param name="cols" type="int" min="2" max="1000" gui-text="Columns">10</param>
<param name="layer" type="bool" gui-text="Create in current layer">true</param>
</page>
<page name="color1" gui-text="1st color">
<param name="color1" type="color" gui-text="Color 1">4286282751</param>
</page>
<page name="color2" gui-text="2nd color">
<param name="color2" type="color" gui-text="Color 2">8092671</param>
</page>
<page name="help" gui-text="Help">
<label>1. On the Params tab, choose the cell size (size of the constituent squares), number of rows and columns, and whether to add the checkerboard to the current layer (if unchecked, the checkerboard will be added to the root layer)</label>
<label>2. On the 1st color and 2nd color tabs, select the colors for the two sets of squares</label>
<label>3. Click Apply</label>
<label>More information and source code:</label>
<label appearance="url">https://github.com/jeffkayser/inkscape-checkerboard</label>
</page>
</param>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu name="FabLab Chemnitz">
<submenu name="Grids/Guides"/>
</submenu>
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">checkerboard.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,93 @@
#!/usr/bin/env python3
"""
Inkscape extension to create checkerboard patterns
Copyright (C) 2011 Jeff Kayser
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
import re
from inkex import Color
def rgbToHex(pickerColor):
longcolor = int(pickerColor)
if longcolor < 0:
longcolor = longcolor & 0xFFFFFFFF
return '#' + format(longcolor >> 8, '06X')
def draw_square(x, y, w, h, color, parent, id_=None):
"""Draw a w*h square at (x, y) having color color
"""
if len(color) == 4:
opacity=color[3]
else:
opacity=1.0
style = {'stroke': 'none', 'stroke-width': '1', 'fill': rgbToHex(color), 'fill-opacity': opacity}
attribs = {'style': str(inkex.Style(style)), 'height': str(h), 'width': str(w), 'x': str(x), 'y': str(y)}
if id_ is not None:
attribs.update({'id': id_})
obj = etree.SubElement(parent, inkex.addNS('rect', 'svg'), attribs)
def draw_grid(x, y, rows, cols, size, color1, color2, parent):
"""Draw a rows*cols checkboard grid at (x, y) with square size of size*size,
with squares having alternating colors color1 and color2
"""
# Group like-colors
group1 = etree.SubElement(parent, 'g', {'id': 'diagonal1'})
group2 = etree.SubElement(parent, 'g', {'id': 'diagonal2'})
for row in range(int(rows)):
for col in range(int(cols)):
alternate = (col + row) % 2 == 0
color = color1 if alternate else color2
group = group1 if alternate else group2
id_ = 'cell{0}x{1}'.format(col, row)
draw_square(x + col * size, y + row * size, size, size, color, group, id_)
class Checkerboard(inkex.EffectExtension):
def add_arguments(self, pars):
pars.add_argument("--tab")
pars.add_argument("--color1", type=Color, default=4286282751)
pars.add_argument("--color2", type=Color, default=8092671)
pars.add_argument("--size")
pars.add_argument("--rows", type=int)
pars.add_argument("--cols", type=int)
pars.add_argument("--layer", type=inkex.Boolean)
def effect(self):
if self.svg.get_current_layer() is not None:
group = etree.SubElement(self.svg.get_current_layer(), 'g', {'id': 'checkerboard'})
else:
parent = self.document.getroot()
group = etree.SubElement(parent, 'g', {'id': 'checkerboard'})
rows = self.options.rows
cols = self.options.cols
# Convert to pixels
size = self.svg.unittouu(self.options.size)
color1 = self.options.color1
color2 = self.options.color2
# Center checkerboard within visible viewport
x, y = self.svg.namedview.center[0] - cols * size / 2, self.svg.namedview.center[1] - rows * size / 2
draw_grid(x, y, rows, cols, size, color1, color2, group)
if __name__ == '__main__':
Checkerboard().run()

View File

@ -0,0 +1,21 @@
[
{
"name": "Checkerboard",
"id": "fablabchemnitz.de.checkerboard",
"path": "checkerboard",
"dependent_extensions": null,
"original_name": "Checkerboard",
"original_id": "org.jeffkayser.checkerboard",
"license": "GNU GPL v2",
"license_url": "https://github.com/jeffkayser/inkscape-checkerboard/blob/master/LICENSE",
"comment": "ported to Inkscape v1 by Mario Voigt",
"source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/checkerboard",
"fork_url": "https://github.com/jeffkayser/inkscape-checkerboard",
"documentation_url": "https://stadtfabrikanten.org/display/IFM/Checkerboard",
"inkscape_gallery_url": null,
"main_authors": [
"github.com/jeffkayser",
"github.com/eridur-de"
]
}
]

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Circle Tangents (Replaced by Snap Objects Feature)</name>
<id>fablabchemnitz.de.circle_tangents</id>
<param name="position" type="optiongroup" appearance="combo" gui-text="Outer or inner?">
<option value="inner">Inner</option>
<option value="outer">Outer</option>
</param>
<param name="selector" type="optiongroup" appearance="combo" gui-text="Which tangents?">
<option value="first">First</option>
<option value="second">Second</option>
<option value="both">Both</option>
</param>
<param name="use_style_from_first" type="bool" gui-text="Use style from first selected">true</param>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu name="FabLab Chemnitz">
<submenu name="Paths - Join/Order"/>
</submenu>
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">circle_tangents.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,230 @@
#!/usr/bin/env python3
'''
Copyright (C) 2012 Rhys Owen, rhysun@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 3 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, see <http://www.gnu.org/licenses/>.
A
|\
| \
| \
| \
| \
b| \h
| \
| \
|_ \
|_|_______\
C a B
'''
import inkex
from inkex.paths import Path
from inkex import Transform
from math import *
from lxml import etree
def poltocar(r, rad, negx=False, negy=False):
# converts polar coords to cartesian
x = r * cos(rad)
y = r * sin(rad)
if negx and not negy:
return [-x, y]
elif not negx and negy:
return [x, -y]
elif not negx and not negy:
return [-x, -y]
else:
return [x, y]
def deuclid(x1, y1, x2, y2):
# euclidean distance between two cartesian coords
squarex = (x1 - x2)**2
squarey = (y1 - y2)**2
d = sqrt(squarex + squarey)
return d
def getAngle(b, h):
angle = asin(b / h)
return angle
def aLength(b, h):
a = sqrt(h**2-b**2)
return a
def getPathData(obj):
if obj.get("d"):# If the circle has been converted to a path object
d = obj.get("d")
p = Path(d)
if obj.get("transform"):
trans = Transform(obj.get("transform"))
scalex = trans[0][0]
scaley = trans[1][1]
data = {'rx' : p[1][1][0]*scalex,
'ry' : p[1][1][1]*scaley,
'x' : (trans[0][0]*p[0][1][0])+(trans[0][1]*p[0][1][1])+trans[0][2]-(p[1][1][0]*scalex),
'y' : (trans[1][0]*p[0][1][0])+(trans[1][1]*p[0][1][1])+trans[1][2]}
else:
data = {'rx': p[1][1][0],
'ry': p[1][1][1],
'x' : p[0][1][0]-p[1][1][0],
'y' : p[0][1][1]}
elif obj.get("r"):# For a pure circle object
r = obj.get("r")
cx = obj.get("cx")
cy = obj.get("cy")
data = {'rx' : float(r),
'ry' : float(r),
'x' : float(cx),
'y' : float(cy)}
elif obj.get("rx"):# For ellipses
rx = obj.get("rx")
ry = obj.get("ry")
cx = obj.get("cx")
cy = obj.get("cy")
data = {'rx' : float(rx),
'ry' : float(ry),
'x' : float(cx),
'y' : float(cy)}
else:
stockErrorMsg("4")
return data
class CircleTangents(inkex.EffectExtension):
def add_arguments(self, pars):
pars.add_argument("--position", default="inner", help="Choose either inner or outer tangent lines")
pars.add_argument("--selector", default="both", help="Choose which tangents you want to get")
pars.add_argument("--use_style_from_first", type=inkex.Boolean, default=False, help="Use style from first selected")
def effect(self):
if len(self.options.ids) != 2:
inkex.errormsg("Please select exactly two circles and try again!")
return
c1object = self.svg.selected[self.options.ids[0]]
c2object = self.svg.selected[self.options.ids[1]]
if c1object.tag != inkex.addNS('circle','svg') or c2object.tag != inkex.addNS('circle','svg'):
self.msg("One or both objects are not svg:circle elements!")
return
c1 = getPathData(c1object)
c2 = getPathData(c2object)
# Create a third 'virtual' circle
if c1['rx'] <= c2['rx']:
c3x = c2['x']
c3y = c2['y']
if self.options.position == "outer":
c3r = c2['rx'] - c1['rx']
else:
c3r = c2['rx'] + c1['rx']
cyfA = [c1['x'], c1['y']]
cyfB = [c2['x'], c2['y']]
elif c1['rx'] > c2['rx']:
c3x = c1['x']
c3y = c1['y']
if self.options.position == "outer":
c3r = c1['rx'] - c2['rx']
else:
c3r = c1['rx'] + c2['rx']
cyfA = [c2['x'], c2['y']]
cyfB = [c1['x'], c1['y']]
# Test whether the circles are actually circles!
if c1['rx'] != c1['ry'] or c2['rx'] != c2['ry']:
inkex.errormsg("One or both objects may be elliptical. Ensure you have circles!")
return
# Hypotenus of the triangle - Euclidean distance between c1 x, y and c2 x, y.
h = deuclid(c1['x'], c1['y'], c2['x'], c2['y'])
b = c3r
B = None
try:
B = getAngle(b, h)
except ValueError as e:
if self.options.position == "inner":
inkex.errormsg("Error calculating angle. Maybe your circles are overlapping each other")
else:
inkex.errormsg("Error calculating angle.")
return
a = aLength(b, h)
# Angle of hypotenuse to x-axis
E = getAngle(max(c1['y'], c2['y']) - min(c1['y'], c2['y']), h)
# To test if the smallest circle is lower than the other
if cyfB[1] <= cyfA[1]:
negx = False
else:
negx = True
# To test if it's the smallest circle to the right of the other
if cyfB[0] <= cyfA[0]:
negy = False
else:
negy = True
angleTop = -B+E
angleBottom = B+E
if self.options.position == "outer":# External
perpTop = -(pi/2)
perpBottom = pi/2
else:# Internal
perpTop = pi/2
perpBottom = -(pi/2)
# Top coordinates of the top line
cyfC = poltocar(a, angleTop, negx, negy)
# Information for converting top 90grade coordinates
conversionTop = poltocar(min(c1['rx'], c2['rx']), perpTop+angleTop, negx, negy)#1.5707964 1.57079632679
# Bottom line coordinates
cyfD = poltocar(a, angleBottom, negx, negy)
# Information for converting the bottom 90 degree coordinates
conversionBottom = poltocar(min(c1['rx'], c2['rx']), perpBottom+angleBottom, negx, negy)
# Draw a line
llx1 = cyfA[0]
lly1 = cyfA[1]
if self.options.use_style_from_first is True:
llsteil = (c1object.get("style")) #note: if the selected objects do not contain a stroke width the tangents will be invisible!
else:
llsteil = "stroke:#000000; stroke-width:1px; fill:none;"
# Line 1
if self.options.selector == "first" or self.options.selector == "both":
ll1x2 = cyfC[0]
ll1y2 = cyfC[1]
parent = c1object.getparent()
attribsLine1 = {'style':llsteil,
inkex.addNS('label','inkscape'):"line1",
'd':'m '+str(llx1+conversionTop[0])+','+str(lly1+conversionTop[1])+' l '+str(ll1x2)+','+str(ll1y2)}
elfen1 = etree.SubElement(parent, inkex.addNS('path','svg'), attribsLine1 )
#Line 2
if self.options.selector == "second" or self.options.selector == "both":
ll2x2 = cyfD[0]
ll2y2 = cyfD[1]
parent = c1object.getparent()
attribsLine1 = {'style':llsteil,
inkex.addNS('label','inkscape'):"line2",
'd':'m '+str(llx1+conversionBottom[0])+','+str(lly1+conversionBottom[1])+' l '+str(ll2x2)+','+str(ll2y2)}
etree.SubElement(parent, inkex.addNS('path','svg'), attribsLine1 )
if __name__ == '__main__':
CircleTangents().run()

View File

@ -0,0 +1,21 @@
[
{
"name": "Circle Tangents (Replaced by Snap Objects Feature)",
"id": "fablabchemnitz.de.circle_tangents",
"path": "circle_tangents",
"dependent_extensions": null,
"original_name": "Tangent",
"original_id": "org.inkscape.tangent",
"license": "GNU GPL v3",
"license_url": "https://github.com/Rhysun/inkTan/blob/master/tangent.py",
"comment": "ported to Inkscape v1 manually by Mario Voigt",
"source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/circle_tangents",
"fork_url": "https://github.com/Rhysun/inkTan",
"documentation_url": "https://stadtfabrikanten.org/pages/viewpage.action?pageId=74645771",
"inkscape_gallery_url": null,
"main_authors": [
"github.com/Rhysun",
"github.com/eridur-de"
]
}
]

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Clones In Perspective</name>
<id>fablabchemnitz.de.clones_in_perspective</id>
<param max="256" name="num" type="int" gui-text="How many?">5</param>
<param max="0.9999" precision="4" name="ratio" type="float" gui-text="relative size">0.8</param>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu name="FabLab Chemnitz">
<submenu name="Shape/Pattern from existing Object(s)"/>
</submenu>
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">clones_in_perspective.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,63 @@
#!/usr/bin/env python3
import sys
import math
import inkex
from lxml import etree
class clonesInPerspective(inkex.EffectExtension):
def add_arguments(self, pars):
pars.add_argument('--num', type = int, default = 5, help = 'Drag out center of rotation before calling')
pars.add_argument('--ratio', type = float, default = 0.9, help = 'Ratio of size of nearest neighbor to first. Must be < 1')
def effect(self):
if len(self.svg.selected) != 1:
inkex.errormsg("Select exactly 1 thing. If necessary, group your items before.")
sys.exit(1)
xpath = list(self.svg.selected.items())[0][0]
sel = self.svg.selected[xpath]
id = sel.get('id')
try :
tx = sel.attrib[inkex.addNS('transform-center-x','inkscape')]
except KeyError:
tx = '0.'
try :
ty = sel.attrib[inkex.addNS('transform-center-y','inkscape')]
except KeyError:
ty = '0.'
if float(tx) == 0. and float(ty) == 0. :
inkex.errormsg("Center of rotation ist at the center of object. Please move the center first.")
sys.exit(1)
bbox = sel.bounding_box()
width = bbox.height
height = bbox.width
cx = float(bbox.left) + 0.5 * width #Find center of selected object
cy = float(bbox.top) + 0.5 * height #Find center of selected object
tx = float(tx)
ty = float(ty)
crat = 1.0
otx = tx
oty = ty
parent = sel.getparent()
j = parent.index(sel)
for i in range(self.options.num) :
crat *= self.options.ratio
tx *= self.options.ratio
ty *= self.options.ratio
att = {
"id" : self.svg.get_unique_id("clone" + id),
inkex.addNS('href','xlink') : "#" + id,
inkex.addNS('transform-center-x','inkscape') : str(tx),
inkex.addNS('transform-center-y','inkscape') : str(ty),
'transform' : ("matrix(%f,0,0,%f,%f,%f)" % (crat, crat,(1. - crat)*(cx + otx), (1. - crat)*(cy - oty))),
"width" : "100%",
"height" : "100%",
}
parent.insert(j, etree.Element('use', att))
if __name__ == '__main__':
clonesInPerspective().run()

View File

@ -0,0 +1,21 @@
[
{
"name": "Clones In Perspective",
"id": "fablabchemnitz.de.clones_in_perspective",
"path": "clones_in_perspective",
"dependent_extensions": null,
"original_name": "Clones in Perspective",
"original_id": "org.ekips.filter.clonesPerspective",
"license": "GNU GPL v2",
"license_url": "https://inkscape.org/de/~kurn/%E2%98%85clonesp",
"comment": "ported to Inkscape v1 manually by Mario Voigt",
"source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/clones_in_perspective",
"fork_url": "https://inkscape.org/de/~kurn/%E2%98%85clonesp",
"documentation_url": "https://stadtfabrikanten.org/display/IFM/Clones+In+Perspective",
"inkscape_gallery_url": null,
"main_authors": [
"inkscape.org/kurn",
"github.com/eridur-de"
]
}
]

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Close Paths</name>
<id>fablabchemnitz.de.close_paths</id>
<effect needs-live-preview="false">
<object-type>path</object-type>
<effects-menu>
<submenu name="FabLab Chemnitz">
<submenu name="Modify existing Path(s)"/>
</submenu>
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">close_paths.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,43 @@
#!/usr/bin/env python3
"""
Copyright (C) 2009 Nick Drobchenko, nick@cnc-club.ru
based on gcode.py (C) 2007 hugomatic...
based on addnodes.py (C) 2005,2007 Aaron Spike, aaron@ekips.org
based on dots.py (C) 2005 Aaron Spike, aaron@ekips.org
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
"""
#
# This extension close paths by adding 'z' before each 'm' if it is not the first 'm' and it is not prepended by 'z' already.
#
import inkex
import re
from inkex.paths import Path
class ClosePaths(inkex.EffectExtension):
def effect(self):
for id, node in self.svg.selected.items():
if node.tag == inkex.addNS('path','svg'):
d = node.get('d')
d = str(Path((Path(d).to_arrays())))
d = re.sub(r'(?i)(m[^mz]+)',r'\1 Z ',d)
d = re.sub(r'(?i)\s*z\s*z\s*',r' Z ',d)
node.set('d', d)
if __name__ == '__main__':
ClosePaths().run()

View File

@ -0,0 +1,21 @@
[
{
"name": "Close Paths",
"id": "fablabchemnitz.de.close_paths",
"path": "close_paths",
"dependent_extensions": null,
"original_name": "Close curves",
"original_id": "ru.cnc-club.filter.close_curves",
"license": "GNU GPL v2",
"license_url": "https://www.cnc-club.ru/forum/download/file.php?id=94&sid=63473c1c91035bea64a8986cd1ab292c",
"comment": "ported to Inkscape v1 by Mario Voigt; See https://www.cnc-club.ru/forum/viewtopic.php?f=15&t=37&p=92",
"source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/close_paths",
"fork_url": "https://www.cnc-club.ru/forum/download/file.php?id=94&sid=63473c1c91035bea64a8986cd1ab292c",
"documentation_url": "https://stadtfabrikanten.org/display/IFM/Close+Paths",
"inkscape_gallery_url": null,
"main_authors": [
"Nick Drobchenko:nick@cnc-club.ru",
"github.com/eridur-de"
]
}
]

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Convert To Polylines</name>
<id>fablabchemnitz.de.convert_to_polylines</id>
<effect>
<object-type>path</object-type>
<effects-menu>
<submenu name="FabLab Chemnitz">
<submenu name="Modify existing Path(s)"/>
</submenu>
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">convert_to_polylines.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,49 @@
#!/usr/bin/env python3
"""
Extension for InkScape 1.0
Converts curves to polylines - a quick and dirty helper for a lot of elements. Basically the same functionality can be done with default UI featureset but with a lot more mouse clicks
Author: Mario Voigt / FabLab Chemnitz
Mail: mario.voigt@stadtfabrikanten.org
Date: 05.09.2020
Last patch: 05.09.2020
License: GNU GPL v3
"""
import inkex
from inkex.paths import Path
class ConvertToPolylines(inkex.EffectExtension):
#convert a path (curve) to a polyline and remove dangling/duplicate/useless overlapping handles (points)
def convertPath(self, node):
if node.tag == inkex.addNS('path','svg'):
polypath = []
i = 0
for x, y in node.path.end_points:
if i == 0:
polypath.append(['M', [x,y]])
else:
polypath.append(['L', [x,y]])
if i == 1 and polypath[len(polypath)-2][1] == polypath[len(polypath)-1][1]:
polypath.pop(len(polypath)-1) #special handling for the seconds point after M command
elif polypath[len(polypath)-2] == polypath[len(polypath)-1]: #get the previous point
polypath.pop(len(polypath)-1)
i += 1
node.set('d', str(Path(polypath)))
children = node.getchildren()
if children is not None:
for child in children:
self.convertPath(child)
def effect(self):
if len(self.svg.selected) == 0:
self.convertPath(self.document.getroot())
else:
for id, item in self.svg.selected.items():
self.convertPath(item)
if __name__ == '__main__':
ConvertToPolylines().run()

View File

@ -0,0 +1,20 @@
[
{
"name": "Convert To Polylines",
"id": "fablabchemnitz.de.convert_to_polylines",
"path": "convert_to_polylines",
"dependent_extensions": null,
"original_name": "Convert To Polylines",
"original_id": "fablabchemnitz.de.convert_to_polylines",
"license": "GNU GPL v3",
"license_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/LICENSE",
"comment": "Written by Mario Voigt",
"source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/convert_to_polylines",
"fork_url": null,
"documentation_url": "https://stadtfabrikanten.org/display/IFM/Convert+To+Polylines",
"inkscape_gallery_url": "https://inkscape.org/~MarioVoigt/%E2%98%85convert-to-polylines",
"main_authors": [
"github.com/eridur-de"
]
}
]

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Convex Hull</name>
<id>fablabchemnitz.de.convex_hull</id>
<effect needs-live-preview="false">
<object-type>all</object-type>
<effects-menu>
<submenu name="FabLab Chemnitz">
<submenu name="Shape/Pattern from existing Path(s)"/>
</submenu>
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">convex_hull.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,140 @@
#!/usr/bin/env python3
import inkex
import sys
import numpy as n
from inkex import PathElement
from inkex.paths import CubicSuperPath, Path
from inkex.transforms import Transform
from lxml import etree
link = lambda a,b: n.concatenate((a,b[1:]))
edge = lambda a,b: n.concatenate(([a],[b]))
'''
Convex Hull routine by Markus Kohler, found at https://gist.github.com/kohlerm/0337f7c64df42f21f96405b3f8e895f2, which itself is an implementation of:
https://github.com/flengyel/REST/blob/master/quickhull.py
Compute the convex hull of a set of points in the plane
Adapted from en.literateprograms.org/Quickhull_(Python,arrays)
Content available under MIT/X11 License at
http://en.literateprograms.org/LiteratePrograms:Copyrights
Copyright (c) 2008 the authors listed in the page history
(see http://en.literateprograms.org/index.php?title=Quickhull_(Python,_arrays)&action=history)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
'''
def qhull(sample):
def dome(sample,base):
sys.setrecursionlimit(20000) #dangerous stuff
h, t = base
dists = n.dot(sample-h, n.dot(((0,-1),(1,0)),(t-h)))
outer = n.repeat(sample, dists>0, 0)
if len(outer):
pivot = sample[n.argmax(dists)]
return link(dome(outer, edge(h, pivot)),
dome(outer, edge(pivot, t)))
else:
return base
if len(sample) > 2:
axis = sample[:,0]
base = n.take(sample, [n.argmin(axis), n.argmax(axis)], 0)
return link(dome(sample, base),
dome(sample, base[::-1]))
else:
return sample
class ConvexHull(inkex.EffectExtension):
def __init__(self):
inkex.Effect.__init__(self)
self.paths = {}
self.paths_clone_transform = {}
def joinWithElement (self, element, path, makeGroup=False, cloneTransform=None):
if (not path) or (len(path) == 0 ):
return
g = self.document.getroot()
# Now make a <path> element which contains the twist & is a child
# of the new <g> element
style = { 'stroke': '#000000', 'fill': 'none', 'stroke-width': str(self.svg.unittouu("1px")) }
line_attribs = { 'style':str(inkex.Style(style)), 'd': path }
if (cloneTransform != None ) and (cloneTransform != '' ):
line_attribs['transform'] = cloneTransform
etree.SubElement(g, inkex.addNS('path', 'svg' ), line_attribs)
def getControlPoints(self, element, n_array = None):
if n_array == None:
n_array = []
if element.tag == inkex.addNS('path','svg'):
#element.apply_transform()
parent = element.getparent()
if parent is not None:
csp = Path(element.path.transform(parent.composed_transform())).to_arrays()
else:
csp = element.path.to_arrays()
p = CubicSuperPath(csp)
for subpath in p: # there may be several paths joined together (e.g. holes)
for csp in subpath: # groups of three to handle control points.
# just the points no control points (handles)
n_array.append(csp[1][0])
n_array.append(csp[1][1])
for child in element.getchildren():
n_array += self.getControlPoints(child, n_array)
return n_array
def effect(self):
if len(self.svg.selected) > 0:
global output_elements, points
n_array = []
elements = self.svg.selection.filter(PathElement).values()
if len(elements) > 0:
for element in elements:
if element.tag == inkex.addNS('path','svg'):
n_array += self.getControlPoints(element, None)
k = n.asarray(n_array)
length = int(len(k)/2)
c = k.reshape(length,2)
hull_pts = qhull(c)
pdata = ''
for vertex in hull_pts:
if pdata == '':
pdata = 'M%f,%f' % (vertex[0], vertex[1] )
else:
pdata += 'L %f,%f' % (vertex[0], vertex[1] )
pdata += ' Z'
path = 'polygon'
makeGroup = False
paths_clone_transform = None
self.joinWithElement(path, pdata, makeGroup, paths_clone_transform)
else:
inkex.errormsg('Selection seems not to contain svg:path elements. Maybe you have selected a group instead?')
return
else:
inkex.errormsg('Please select some paths first.')
return
if __name__ == '__main__':
ConvexHull().run()

View File

@ -0,0 +1,22 @@
[
{
"name": "Convex Hull",
"id": "fablabchemnitz.de.convex_hull",
"path": "convex_hull",
"dependent_extensions": null,
"original_name": "ConvexHull",
"original_id": "org.simarilius.filter.ConvexHull",
"license": "MIT License",
"license_url": " https://github.com/flengyel/REST/blob/master/quickhull.py",
"comment": "",
"source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/convex_hull",
"fork_url": "https://gist.github.com/kohlerm/0337f7c64df42f21f96405b3f8e895f2",
"documentation_url": "https://stadtfabrikanten.org/display/IFM/Convex+Hull",
"inkscape_gallery_url": null,
"main_authors": [
"github.com/flengyel",
"github.com/kohlerm",
"github.com/eridur-de"
]
}
]

View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Create Hexmap</name>
<id>fablabchemnitz.de.create_hexmap</id>
<param name="tab" type="notebook">
<page name="page_1" gui-text="Size">
<param name="units" type="optiongroup" appearance="combo" gui-text="Size units">
<option value="mm">mm</option>
<option value="cm">cm</option>
<option value="in">in</option>
<option value="pt">pt</option>
<option value="px">px</option>
</param>
<param name="cols" type="int" gui-text="Columns" min="1" max="1000000">10</param>
<param name="rows" type="int" gui-text="Rows" min="1" max="1000000">10</param>
<param name="hexsize" type="float" min="0" max="9999" gui-text="Hex Size (optional)">0</param>
<param name="strokewidth" type="float" min="0.0" max="9999.0" gui-text="Stroke Width">1.0</param>
<param name="verticesize" type="float" min="0.0" max="50.0" gui-text="Size of vertices (%)">10.0</param>
</page>
<page name="page_2" gui-text="Style">
<param name="bricks" type="bool" gui-text="Bricks">false</param>
<param name="squarebricks" type="bool" gui-text="Force Square Bricks">false</param>
<param name="rotate" type="bool" gui-text="Rotate">false</param>
<param name="halfhexes" type="bool" gui-text="Half hexes at top and bottom">false</param>
<param name="xshift" type="bool" gui-text="Shift grid to side and wrap">false</param>
<param name="firstcoldown" type="bool" gui-text="First column half-hex down">false</param>
</page>
<page name="page_3" gui-text="Coords">
<param name="coordseparator" type="string" gui-text="Coordinate Separator">.</param>
<param name="coordalphacol" type="bool" gui-text="Column Alpha Coordinates">false</param>
<param name="coordrevrow" type="bool" gui-text="Row Coordinates Reversed">false</param>
<param name="coordrowfirst" type="bool" gui-text="Row Coordinate First">false</param>
<param name="coordzeros" type="bool" gui-text="Zero-Padded Coordinates">true</param>
<param name="coordrows" type="int" min="1" max="100" gui-text="Coordinates Every ... Rows">1</param>
<param name="coordcolstart" type="int" gui-text="First Col Nr" min="0" max="1000">1</param>
<param name="coordrowstart" type="int" gui-text="First Row Nr" min="0" max="1000">1</param>
</page>
<page name="page_4" gui-text="Layers">
<param name="layer_grid" type="bool" gui-text="Grid">true</param>
<param name="layer_fill" type="bool" gui-text="Fill (#ffffff)">true</param>
<param name="layer_coordinates" type="bool" gui-text="Coordinates">true</param>
<param name="layer_centerdots" type="bool" gui-text="Centerdots">true</param>
<param name="layer_vertices" type="bool" gui-text="Vertices">false</param>
<param name="layer_circles" type="bool" gui-text="Circles">false</param>
<param name="layersingroup" type="bool" gui-text="Layers in Group">false</param>
</page>
<page name="page_5" gui-text="Debug">
<param name="generatelog" type="bool" gui-text="Generate log file">false</param>
<param name="logfilepath" type="path" gui-text="Log File (optional)" mode="file_new" filetypes="txt">debug.txt</param>
</page>
</param>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu name="FabLab Chemnitz">
<submenu name="Grids/Guides"/>
</submenu>
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">create_hexmap.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,420 @@
#!/usr/bin/env python3
import inkex
import sys
from inkex import NSS
import math
from lxml import etree
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return '%f,%f' % (self.x, self.y)
def y_mirror(self, h):
return Point(self.x, h - self.y);
def __sub__(self, other):
return Point(self.x - other.x, self.y - other.y)
def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)
def __mul__(self, k):
return Point(self.x * k, self.y * k)
def rotated(self, total_width):
return Point(self.y, total_width - self.x)
def nrdigits(f):
return int(math.floor(math.log10(f))) + 1
def alphacol(c):
d = c / 26
r = c % 26
return ('%c' % (r + 65)) * (int(d) + 1)
def calc_hex_height(hex_width):
return 0.25 * hex_width / math.tan(math.pi / 6) * 2
COORD_SIZE_PART_OF_HEX_HEIGHT = 0.1
COORD_YOFFSET_PART = 75
CENTERDOT_SIZE_FACTOR = 1.1690625
class CreateHexmap(inkex.EffectExtension):
def add_arguments(self, pars):
pars.add_argument('--tab')
pars.add_argument('--generatelog', type = inkex.Boolean, default = False)
pars.add_argument('--logfilepath', default = "debug.txt")
pars.add_argument("--units", default='mm', help="Units this dialog is using")
pars.add_argument('--cols', type = int, default = '10', help = 'Number of columns.')
pars.add_argument('--rows', type = int, default = '10', help = 'Number of columns.')
pars.add_argument('--hexsize', type = float, default = 0.0)
pars.add_argument('--strokewidth', type = float, default = 1.0)
pars.add_argument('--coordrows', type = int, default = '1')
pars.add_argument('--coordcolstart', type = int, default = '1')
pars.add_argument('--coordrowstart', type = int, default = '1')
pars.add_argument('--bricks', type = inkex.Boolean, default = False)
pars.add_argument('--squarebricks', type = inkex.Boolean, default = False)
pars.add_argument('--rotate', type = inkex.Boolean, default = False)
pars.add_argument('--coordseparator', default = '')
pars.add_argument('--layersingroup', type = inkex.Boolean, default = False, help = 'Put all layers in a layer group.')
pars.add_argument('--coordalphacol', type = inkex.Boolean, default = False, help = 'Reverse row coordinates.')
pars.add_argument('--coordrevrow', type = inkex.Boolean, default = False, help = 'Reverse row coordinates.')
pars.add_argument('--coordzeros', type = inkex.Boolean, default = True)
pars.add_argument('--coordrowfirst', type = inkex.Boolean, default = False, help = 'Reverse row coordinates.')
pars.add_argument('--xshift', type = inkex.Boolean, default = False, help = 'Shift grid half hex and wrap.')
pars.add_argument('--firstcoldown', type = inkex.Boolean, default = False, help = 'Make first column half-hex down.')
pars.add_argument('--halfhexes', type = inkex.Boolean, default = False)
pars.add_argument('--verticesize', type = float,default = 1.0)
pars.add_argument('--layer_grid', type = inkex.Boolean, default = True)
pars.add_argument('--layer_fill', type = inkex.Boolean, default = True)
pars.add_argument('--layer_coordinates', type = inkex.Boolean, default = True)
pars.add_argument('--layer_centerdots', type = inkex.Boolean, default = True)
pars.add_argument('--layer_vertices', type = inkex.Boolean, default = False)
pars.add_argument('--layer_circles', type = inkex.Boolean, default = False)
def createLayer(self, name):
layer = etree.Element(inkex.addNS('g', 'svg'))
layer.set(inkex.addNS('label', 'inkscape'), name)
layer.set(inkex.addNS('groupmode', 'inkscape'), 'layer')
return layer
def logwrite(self, msg):
if self.options.generatelog:
log = open(self.options.logfilepath, 'w')
log.write(msg)
log.close()
def svg_line(self, p1, p2):
line = etree.Element('line')
line.set('x1', str(p1.x + self.xoffset))
line.set('y1', str(p1.y + self.yoffset))
line.set('x2', str(p2.x + self.xoffset))
line.set('y2', str(p2.y + self.yoffset))
line.set('style', 'stroke:#000000; stroke-width:' + str(self.stroke_width) + ';stroke-linecap:round')
return line
def svg_circle(self, p, radius):
circle = etree.Element('circle')
circle.set('cx', str(p.x + self.xoffset))
circle.set('cy', str(p.y + self.yoffset))
circle.set('r', str(radius))
circle.set('fill', 'black')
return circle
def svg_polygon(self, points):
poly = etree.Element('polygon')
pointsdefa = []
for p in points:
offset_p = Point(p.x + self.xoffset, p.y + self.yoffset)
pointsdefa.append(str(offset_p))
pointsdef = ' '.join(pointsdefa)
poly.set('points', pointsdef)
poly.set('style', 'stroke:none;fill:#ffffff;fill-opacity:1;stroke-width:' + str(self.stroke_width) + ';stroke-linecap:round')
return poly
def svg_coord(self, p, col, row, cols, rows, anchor='middle'):
if self.coordrevrow:
row = rows - row
else:
row = row + 1
if self.coordrevcol:
col = cols - col
else:
col = col + 1
row = row + self.options.coordrowstart - 1
col = col + self.options.coordcolstart - 1
if ((row != 1 and row % self.coordrows != 0)
or row < 1 or col < 1):
return None
if self.coordrowfirst:
col,row = [row,col]
if self.coordalphacol:
acol = alphacol(col - 1)
if self.coordzeros:
zrow = str(row).zfill(self.rowdigits)
coord = acol + self.coordseparator + zrow
else:
coord = acol + self.coordseparator + str(row)
elif self.coordzeros:
zcol = str(col).zfill(self.coldigits)
zrow = str(row).zfill(self.rowdigits)
coord = zcol + self.coordseparator + zrow
else:
coord = str(col) + self.coordseparator + str(row)
self.logwrite(" coord-> '%s'\n" % (coord))
text = etree.Element('text')
text.set('x', str(p.x + self.xoffset))
text.set('y', str(p.y + self.yoffset))
style = ('text-align:center;text-anchor:%s;font-size:%fpt'
% (anchor, self.coordsize))
text.set('style', style)
text.text = coord
return text
def add_hexline(self, gridlayer, verticelayer, p1, p2):
if gridlayer is not None:
gridlayer.append(self.svg_line(p1, p2))
if verticelayer is not None:
verticelayer.append(self.svg_line(p1, (p2 - p1)
* self.verticesize + p1))
verticelayer.append(self.svg_line(p2, p2 - (p2 - p1)
* self.verticesize))
def effect(self):
strokewidth = self.options.strokewidth
cols = self.options.cols
rows = self.options.rows
halves = self.options.halfhexes
xshift = self.options.xshift
firstcoldown = self.options.firstcoldown
bricks = self.options.bricks
squarebricks = self.options.squarebricks
rotate = self.options.rotate
layersingroup = self.options.layersingroup
self.coordseparator = self.options.coordseparator
if self.coordseparator == None:
self.coordseparator = ''
self.coordrevrow = self.options.coordrevrow
self.coordrevcol = False
self.coordalphacol = self.options.coordalphacol
self.coordrows = self.options.coordrows
self.coordrowfirst = self.options.coordrowfirst
self.coordzeros = self.options.coordzeros
if rotate:
self.coordrowfirst = not self.coordrowfirst
self.coordrevcol = not self.coordrevrow
self.coordrevrow = False
self.verticesize = self.options.verticesize / 100.0
self.logwrite('verticesize: %f\n' % self.verticesize)
if self.verticesize < 0.01 or self.verticesize > 0.5:
self.logwrite('verticesize out of range\n')
self.verticesize = 0.15
self.coldigits = nrdigits(cols + self.options.coordcolstart)
self.rowdigits = nrdigits(rows + self.options.coordrowstart)
if self.coldigits < 2:
self.coldigits = 2
if self.rowdigits < 2:
self.rowdigits = 2
if self.coordrowfirst:
self.coldigits,self.rowdigits = [self.rowdigits,self.coldigits]
self.logwrite('cols: %d, rows: %d\n' % (cols, rows))
self.logwrite('xshift: %s, halves: %s\n' % (str(xshift), str(halves)))
svg = self.document.xpath('//svg:svg' , namespaces=NSS)[0]
self.stroke_width = self.svg.unittouu(str(self.options.strokewidth) + self.options.units)
width = float(self.svg.unittouu(svg.get('width'))) - self.stroke_width
height = float(self.svg.unittouu(svg.get('height'))) - self.stroke_width
# So I was a bit lazy and only added an offset to all the
# svg_* functions to compensate for the stroke width.
# There should be a better way.
self.xoffset = self.stroke_width * 0.5
self.yoffset = self.stroke_width * 0.5
if self.options.layer_grid:
hexgrid = self.createLayer('Hex Grid')
else:
hexgrid = None
if self.options.layer_fill:
hexfill = self.createLayer('Hex Fill')
else:
hexfill = None
if self.options.layer_coordinates:
hexcoords = self.createLayer('Hex Coordinates')
else:
hexcoords = None
if self.options.layer_centerdots:
hexdots = self.createLayer('Hex Centerdots')
else:
hexdots = None
if self.options.layer_vertices:
hexvertices = self.createLayer('Hex Vertices')
else:
hexvertices = None
if self.options.layer_circles:
hexcircles = self.createLayer('Hex Circles')
else:
hexcircles = None
if hexvertices is not None and hexgrid is not None:
hexgrid.set('style', 'display:none')
self.logwrite('w, h : %f, %f\n' % (width, height))
if xshift:
hex_cols = (cols * 3.0) * 0.25
else:
hex_cols = (cols * 3.0 + 1.0) * 0.25
if halves:
hex_rows = rows
else:
hex_rows = rows + 0.5
hex_width = width / hex_cols
if self.options.hexsize > 0:
hex_width = self.svg.unittouu(str(self.options.hexsize) + self.options.units)
hex_height = calc_hex_height(hex_width)
# square bricks workaround
if bricks and squarebricks:
hex_height = hex_width
hex_width = hex_width / 0.75
hexes_height = hex_height * hex_rows
hexes_width = hex_width * 0.75 * cols + hex_width * 0.25
self.coordsize = hex_height * COORD_SIZE_PART_OF_HEX_HEIGHT
if self.coordsize > 1.0:
self.coordsize = round(self.coordsize)
self.centerdotsize = self.stroke_width * CENTERDOT_SIZE_FACTOR
self.circlesize = hex_height / 2
self.logwrite('hex_width: %f, hex_height: %f\n' %(hex_width,
hex_height))
# FIXME try to remember what 0.005 is for
coord_yoffset = COORD_YOFFSET_PART * hex_height * 0.005
for col in range(cols + 1):
cx = (2.0 + col * 3.0) * 0.25 * hex_width
if xshift:
cx = cx - hex_width * 0.5
coldown = col % 2
if firstcoldown:
coldown = not coldown
for row in range(rows + 1):
cy = (0.5 + coldown * 0.5 + row) * hex_height
self.logwrite('col: %d, row: %d, c: %f %f\n' % (col, row,
cx, cy))
c = Point(cx, cy)
if rotate:
c = c.rotated(hexes_width)
if (hexcoords is not None
and (col < cols or xshift) and row < rows):
cc = c + Point(0, coord_yoffset)
anchor = 'middle'
if xshift and col == 0:
anchor = 'start'
elif xshift and col == cols:
anchor = 'end'
coord = self.svg_coord(cc, col, row, cols, rows, anchor)
if coord != None:
hexcoords.append(coord)
if (hexdots is not None
and (col < cols or xshift) and row < rows):
cd = self.svg_circle(c, self.centerdotsize)
cd.set('id', 'hexcenter_%d_%d'
% (col + self.options.coordcolstart,
row + self.options.coordrowstart))
hexdots.append(cd)
#FIXME make half-circles in half hexes
if (hexcircles is not None and (col < cols or xshift)
and row < rows):
el = self.svg_circle(c, self.circlesize)
el.set('id', 'hexcircle_%d_%d'
% (col + self.options.coordcolstart,
row + self.options.coordrowstart))
hexcircles.append(el)
x = [cx - hex_width * 0.5,
cx - hex_width * 0.25,
cx + hex_width * 0.25,
cx + hex_width * 0.5]
y = [cy - hex_height * 0.5,
cy,
cy + hex_height * 0.5]
if bricks and xshift:
sys.exit('No support for bricks with x shift.')
if xshift and col == 0:
x[0] = cx
x[1] = cx
elif xshift and col == cols:
x[2] = cx
x[3] = cx
if halves and coldown and row == rows-1:
y[2] = cy
# with bricks pattern, shift some coordinates a bit
# to make correct shape
if bricks:
brick_adjust = hex_width * 0.125
else:
brick_adjust = 0
p = [Point(x[2] + brick_adjust, y[0]),
Point(x[3] - brick_adjust, y[1]),
Point(x[2] + brick_adjust, y[2]),
Point(x[1] - brick_adjust, y[2]),
Point(x[0] + brick_adjust, y[1]),
Point(x[1] - brick_adjust, y[0])]
if rotate:
p = [point.rotated(hexes_width) for point in p]
if (hexfill is not None
and (col < cols or xshift) and row < rows):
if row < rows or (halves and coldown):
sp = self.svg_polygon(p)
if halves and coldown and row == rows - 1:
p2 = [x.y_mirror(hexes_height) for x in p]
sp = self.svg_polygon(p)
sp.set('id', 'hexfill_%d_%d'
% (col + self.options.coordcolstart,
row + self.options.coordrowstart))
hexfill.append(sp)
if ((col < cols and (not halves or row < rows
or not coldown))
or (xshift and col == cols
and not (halves and row == rows))):
self.add_hexline(hexgrid, hexvertices, p[5], p[0])
self.logwrite('line 0-5\n')
if row < rows:
if ((coldown or row > 0 or col < cols
or halves or xshift)
and not (xshift and col == 0)):
self.add_hexline(hexgrid, hexvertices, p[5], p[4])
self.logwrite('line 4-5\n')
if not coldown and row == 0 and col < cols:
self.add_hexline(hexgrid, hexvertices, p[0], p[1])
self.logwrite('line 0-1\n')
if not (halves and coldown and row == rows-1):
if (not (xshift and col == 0)
and not (not xshift and col == cols
and row == rows-1 and coldown)):
self.add_hexline(hexgrid, hexvertices, p[4], p[3])
self.logwrite('line 3-4\n')
if coldown and row == rows - 1 and col < cols:
self.add_hexline(hexgrid, hexvertices, p[1], p[2])
self.logwrite('line 1-2\n')
parent = svg
if layersingroup:
parent = self.createLayer('Hex Map')
self.append_if_new_name(svg, parent)
self.append_if_new_name(parent, hexfill)
self.append_if_new_name(parent, hexcircles)
self.append_if_new_name(parent, hexgrid)
self.append_if_new_name(parent, hexvertices)
self.append_if_new_name(parent, hexcoords)
self.append_if_new_name(parent, hexdots)
def append_if_new_name(self, svg, layer):
if layer is not None:
name = layer.get(inkex.addNS('label', 'inkscape'))
if not name in [c.get(inkex.addNS('label', 'inkscape'), 'name')
for c in svg.iterchildren()]:
svg.append(layer)
if __name__ == '__main__':
CreateHexmap().run()

View File

@ -0,0 +1,23 @@
[
{
"name": "Create Hexmap",
"id": "fablabchemnitz.de.create_hexmap",
"path": "create_hexmap",
"dependent_extensions": null,
"original_name": "Create Hexmap",
"original_id": "pelles.effect.hexmap",
"license": "GNU GPL v2",
"license_url": "https://github.com/lifelike/hexmapextension/blob/master/README.org",
"comment": "",
"source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/create_hexmap",
"fork_url": "https://github.com/lifelike/hexmapextension",
"documentation_url": "https://stadtfabrikanten.org/display/IFM/Create+Hexmap",
"inkscape_gallery_url": null,
"main_authors": [
"github.com/lifelike",
"github.com/pmjdebruijn",
"github.com/UnicodingUnicorn",
"github.com/eridur-de"
]
}
]

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Eggmazing</name>
<id>fablabchemnitz.de.eggmazing</id>
<param name="mazeSize" type="optiongroup" appearance="combo" gui-text="Maze dimensions (w x h):">
<option value="SMALL">Small (32 x 6)</option>
<option value="MEDIUM">Medium (64 x 12)</option>
<option value="LARGE">Large (96 x 18)</option>
<option value="XLARGE">Extra large (128 x 24)</option>
</param>
<effect needs-live-preview="true">
<object-type>all</object-type>
<effects-menu>
<submenu name="FabLab Chemnitz Shape Generators">
<submenu name="Puzzles/Mazes/Nests" />
</submenu>
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">eggmazing.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,663 @@
#!/usr/bin/env python3
# Draw a cylindrical maze suitable for plotting with the Eggbot
# The maze itself is generated using a depth first search (DFS)
# Written by Daniel C. Newman for the Eggbot Project
# Improvements and suggestions by W. Craig Trader
# 20 September 2010
# Update 26 April 2011 by Daniel C. Newman
#
# 1. Address Issue #40
# The extension now draws the maze by columns, going down
# one column of cells and then up the next column. By using
# this technique, the impact of slippage is largely limited
# the the West and East ends of the maze not meeting. Otherwise,
# the maze will still look quite well aligned both locally and
# globally. Only very gross slippage will impact the local
# appearance of the maze.
#
# Note that this new drawing technique is nearly as fast as
# the prior method. The prior method has been preserved and
# can be selected by setting self.hpp = True. ("hpp" intended
# to mean "high plotting precision".)
#
# 2. Changed the page dimensions to use a height of 800 rather
# than 1000 pixels.
#
# 3. When drawing the solution layer, draw the ending cell last.
# Previously, the starting and ending cells were first drawn,
# and then the solution path itself. That caused the pen to
# move to the beginning, the end, and then back to the beginning
# again to start the solution path. Alternatively, the solution
# path might have been drawn from the end to the start. However,
# just drawing the ending cell last was easier code-wise.
#
# 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 sys
import array
import math
import random
import inkex
from lxml import etree
# Initialize the pseudo random number generator
random.seed()
PLOT_WIDTH = 3200 # Eggbot plot width in pixels
PLOT_HEIGHT = 800 # Eggbot plot height in pixels
TARGET_WIDTH = 3200 # Desired plot width in pixels
TARGET_HEIGHT = 600 # Desired plot height in pixels
def draw_SVG_path(pts, c, t, parent):
"""
Add a SVG path element to the document
We could do this just as easily as a polyline
"""
if not pts: # Nothing to draw
return
if isinstance(pts, list):
assert len(pts) % 3 == 0, "len(pts) must be a multiple of three"
d = "{0} {1:d},{2:d}".format(pts[0], pts[1], pts[2])
for i in range(3, len(pts), 3):
d += " {0} {1:d},{2:d}".format(pts[i], pts[i + 1], pts[i + 2])
elif isinstance(pts, str):
d = pts
else:
return
style = {'stroke': c, 'stroke-width': str(t), 'fill': 'none'}
line_attribs = {'style': str(inkex.Style(style)), 'd': d}
etree.SubElement(parent, inkex.addNS('path', 'svg'), line_attribs)
def draw_SVG_rect(x, y, w, h, c, t, fill, parent):
"""
Add a SVG rect element to the document
"""
style = {'stroke': c, 'stroke-width': str(t), 'fill': fill}
rect_attribs = {'style': str(inkex.Style(style)),
'x': str(x), 'y': str(y),
'width': str(w), 'height': str(h)}
etree.SubElement(parent, inkex.addNS('rect', 'svg'), rect_attribs)
class Eggmazing(inkex.EffectExtension):
"""
Each cell in the maze is represented using 9 bits:
Visited -- When set, indicates that this cell has been visited during
construction of the maze
Border -- Four bits indicating which if any of this cell's walls are
part of the maze's boundary (i.e., are unremovable walls)
Walls -- Four bits indicating which if any of this cell's walls are
still standing
Visited Border Walls
x x x x x x x x x
W S E N W S E N
"""
_VISITED = 0x0100
_NORTH = 0x0001
_EAST = 0x0002
_SOUTH = 0x0004
_WEST = 0x0008
def __init__(self):
inkex.Effect.__init__(self)
self.arg_parser.add_argument("--tab", default="controls", help="The active tab when Apply was pressed")
self.arg_parser.add_argument("--mazeSize", default="MEDIUM", help="Difficulty of maze to build")
self.hpp = False
self.w = 0
self.h = 0
self.solved = 0
self.start_x = 0
self.start_y = 0
self.finish_x = 0
self.finish_y = 0
self.solution_x = None
self.solution_y = None
self.cells = None
# Drawing information
self.scale = 25.0
self.last_point = None
self.path = ''
def effect(self):
# These dimensions are chosen so as to maintain integral dimensions
# with a ratio of width to height of TARGET_WIDTH to TARGET_HEIGHT.
# Presently that's 3200 to 600 which leads to a ratio of 5 and 1/3.
if self.options.mazeSize == 'SMALL':
self.w = 32
self.h = 6
elif self.options.mazeSize == 'MEDIUM':
self.w = 64
self.h = 12
elif self.options.mazeSize == 'LARGE':
self.w = 96
self.h = 18
else:
self.w = 128
self.h = 24
# The large mazes tend to hit the recursion limit
limit = sys.getrecursionlimit()
if limit < (4 + self.w * self.h):
sys.setrecursionlimit(4 + self.w * self.h)
maze_size = self.w * self.h
self.finish_x = self.w - 1
self.finish_y = self.h - 1
self.solution_x = array.array('i', range(maze_size))
self.solution_y = array.array('i', range(maze_size))
self.cells = array.array('H', range(maze_size))
# Remove any old maze
for node in self.document.xpath('//svg:g[@inkscape:label="1 - Maze"]', namespaces=inkex.NSS):
parent = node.getparent()
parent.remove(node)
# Remove any old solution
for node in self.document.xpath('//svg:g[@inkscape:label="2 - Solution"]', namespaces=inkex.NSS):
parent = node.getparent()
parent.remove(node)
# Remove any empty, default "Layer 1"
for node in self.document.xpath('//svg:g[@id="layer1"]', namespaces=inkex.NSS):
if not node.getchildren():
parent = node.getparent()
parent.remove(node)
# Start a new maze
self.solved = 0
self.start_x = random.randint(0, self.w - 1)
self.finish_x = random.randint(0, self.w - 1)
# Initialize every cell with all four walls up
for i in range(maze_size):
self.cells[i] = Eggmazing._NORTH | Eggmazing._EAST | Eggmazing._SOUTH | Eggmazing._WEST
# Now set our borders -- borders being walls which cannot be removed.
# Since we are a maze on the surface of a cylinder we only have two
# edges and hence only two borders. We consider our two edges to run
# from WEST to EAST and to be at the NORTH and SOUTH.
z = (self.h - 1) * self.w
for x in range(self.w):
self.cells[x] |= Eggmazing._NORTH << 4
self.cells[x + z] |= Eggmazing._SOUTH << 4
# Build the maze
self.handle_cell(0, self.start_x, self.start_y)
# Now that the maze has been built, remove the appropriate walls
# associated with the start and finish points of the maze
# Note: we have to remove these after building the maze. If we
# remove them first, then the lack of a border at the start (or
# finish) cell will allow the handle_cell() routine to wander
# outside of the maze. I.e., handle_cell() doesn't do boundary
# checking on the cell cell coordinates it generates. Instead, it
# relies upon the presence of borders to prevent it wandering
# outside the confines of the maze.
self.remove_border(self.start_x, self.start_y, Eggmazing._NORTH)
self.remove_wall(self.start_x, self.start_y, Eggmazing._NORTH)
self.remove_border(self.finish_x, self.finish_y, Eggmazing._SOUTH)
self.remove_wall(self.finish_x, self.finish_y, Eggmazing._SOUTH)
# Now draw the maze
# The following scaling and translations scale the maze's
# (width, height) to (TARGET_WIDTH, TARGET_HEIGHT), and translates
# the maze so that it centered within a document of dimensions
# (width, height) = (PLOT_WIDTH, PLOT_HEIGHT)
# Note that each cell in the maze is drawn 2 x units wide by
# 2 y units high. A width and height of 2 was chosen for
# convenience and for allowing easy identification (as the integer 1)
# of the centerline along which to draw solution paths. It is the
# abstract units which are then mapped to the TARGET_WIDTH eggbot x
# pixels by TARGET_HEIGHT eggbot y pixels rectangle.
scale_x = float(TARGET_WIDTH) / float(2 * self.w)
scale_y = float(TARGET_HEIGHT) / float(2 * self.h)
translate_x = float(PLOT_WIDTH - TARGET_WIDTH) / 2.0
translate_y = float(PLOT_HEIGHT - TARGET_HEIGHT) / 2.0
# And the SVG transform is thus
t = 'translate({0:f},{1:f}) scale({2:f},{3:f})'.format(translate_x, translate_y, scale_x, scale_y)
# For scaling line thicknesses. We'll typically draw a line of
# thickness 1 but will need to make the SVG path have a thickness
# of 1 / scale so that after our transforms are applied, the
# resulting thickness is the 1 we wanted in the first place.
if scale_x > scale_y:
self.scale = scale_x
else:
self.scale = scale_y
self.last_point = None
self.path = ''
if not self.hpp:
# To draw the walls, we start at the left-most column of cells, draw down drawing
# the WEST and NORTH walls and then draw up drawing the EAST and SOUTH walls.
# By drawing in this back and forth fashion, we minimize the effect of slippage.
for x in range(0, self.w, 2):
self.draw_vertical(x)
else:
# The drawing style of the "high plotting precision" / "faster plotting" mode
# is such that it minimizes the number of pen up / pen down operations
# but at the expense of requiring higher drawing precision. It's style
# of drawing works best when there is very minimal slippage of the egg
# Draw the horizontal walls
self.draw_horizontal_hpp(0, Eggmazing._NORTH)
for y in range(self.h - 1):
self.draw_horizontal_hpp(y, Eggmazing._SOUTH)
self.draw_horizontal_hpp(self.h - 1, Eggmazing._SOUTH)
# Draw the vertical walls
# Since this is a maze on the surface of a cylinder, we don't need
# to draw the vertical walls at the outer edges (x = 0 & x = w - 1)
for x in range(self.w):
self.draw_vertical_hpp(x, Eggmazing._EAST)
# Maze in layer "1 - Maze"
attribs = {
inkex.addNS('label', 'inkscape'): '1 - Maze',
inkex.addNS('groupmode', 'inkscape'): 'layer',
'transform': t}
maze_layer = etree.SubElement(self.document.getroot(), 'g', attribs)
draw_SVG_path(self.path, "#000000", float(1 / self.scale), maze_layer)
# Now draw the solution in red in layer "2 - Solution"
attribs = {
inkex.addNS('label', 'inkscape'): '2 - Solution',
inkex.addNS('groupmode', 'inkscape'): 'layer',
'transform': t}
maze_layer = etree.SubElement(self.document.getroot(), 'g', attribs)
# Mark the starting cell
draw_SVG_rect(0.25 + 2 * self.start_x, 0.25 + 2 * self.start_y,
1.5, 1.5, "#ff0000", 0, "#ff0000", maze_layer)
# And now generate the solution path itself
# To minimize the number of plotted paths (and hence pen up / pen
# down operations), we generate as few SVG paths as possible.
# However, for aesthetic reasons we stop the path and start a new
# one when it runs off the edge of the document. We could keep on
# drawing as the eggbot will handle that just fine. However, it
# doesn't look as good in Inkscape. So, we end the path and start
# a new one which is wrapped to the other edge of the document.
pts = []
end_path = False
i = 0
while i < self.solved:
x1 = self.solution_x[i]
y1 = self.solution_y[i]
i += 1
x2 = self.solution_x[i]
y2 = self.solution_y[i]
if math.fabs(x1 - x2) > 1:
# We wrapped horizontally...
if x1 > x2:
x2 = x1 + 1
else:
x2 = x1 - 1
end_path = True
if i == 1:
pts.extend(['M', 2 * x1 + 1, 2 * y1 + 1])
pts.extend(['L', 2 * x2 + 1, 2 * y2 + 1])
if not end_path:
continue
x2 = self.solution_x[i]
y2 = self.solution_y[i]
pts.extend(['M', 2 * x2 + 1, 2 * y2 + 1])
end_path = False
# Put the solution path into the drawing
draw_SVG_path(pts, '#ff0000', float(8 / self.scale), maze_layer)
# Now mark the ending cell
draw_SVG_rect(0.25 + 2 * self.finish_x, 0.25 + 2 * self.finish_y,
1.5, 1.5, "#ff0000", 0, "#ff0000", maze_layer)
# Restore the recursion limit
sys.setrecursionlimit(limit)
# Set some document properties
node = self.document.getroot()
node.set('width', '3200')
node.set('height', '800')
self.svg.set('viewBox', '0 0 3200 800')
# The following end up being ignored by Inkscape....
node = self.svg.namedview
node.set('showborder', 'false')
node.set(inkex.addNS('cx', u'inkscape'), '1600')
node.set(inkex.addNS('cy', u'inkscape'), '500')
node.set(inkex.addNS('showpageshadow', u'inkscape'), 'false')
# Mark the cell at (x, y) as "visited"
def visit(self, x, y):
self.cells[y * self.w + x] |= Eggmazing._VISITED
# Return a non-zero value if the cell at (x, y) has been visited
def is_visited(self, x, y):
if self.cells[y * self.w + x] & Eggmazing._VISITED:
return -1
else:
return 0
# Return a non-zero value if the cell at (x, y) has a wall
# in the direction d
def is_wall(self, x, y, d):
if self.cells[y * self.w + x] & d:
return -1
else:
return 0
# Remove the wall in the direction d from the cell at (x, y)
def remove_wall(self, x, y, d):
self.cells[y * self.w + x] &= ~d
# Return a non-zero value if the cell at (x, y) has a border wall
# in the direction d
def is_border(self, x, y, d):
if self.cells[y * self.w + x] & (d << 4):
return -1
else:
return 0
# Remove the border in the direction d from the cell at (x, y)
def remove_border(self, x, y, d):
self.cells[y * self.w + x] &= ~(d << 4)
# This is the DFS algorithm which builds the maze. We start at depth 0
# at the starting cell (self.start_x, self.start_y). We then walk to a
# randomly selected neighboring cell which has not yet been visited (i.e.,
# previously walked into). Each step of the walk is a recursive descent
# in depth. The solution to the maze comes about when we walk into the
# finish cell at (self.finish_x, self.finish_y).
#
# Each recursive descent finishes when the currently visited cell has no
# unvisited neighboring cells.
#
# Since we don't revisit previously visited cells, each cell is visited
# no more than once. As it turns out, each cell is visited, but that's a
# little harder to show. Net, net, each cell is visited exactly once.
def handle_cell(self, depth, x, y):
# Mark the current cell as visited
self.visit(x, y)
# Save this cell's location in our solution trail / backtrace
if not self.solved:
self.solution_x[depth] = x
self.solution_y[depth] = y
if (x == self.finish_x) and (y == self.finish_y):
# Maze has been solved
self.solved = depth
# Shuffle the four compass directions: this is the primary source
# of "randomness" in the generated maze. We need to visit each
# neighboring cell which has not yet been visited. If we always
# did that in the same order, then our mazes would look very regular.
# So, we shuffle the list of directions we try in order to find an
# unvisited neighbor.
# HINT: TRY COMMENTING OUT THE shuffle() BELOW AND SEE FOR YOURSELF
directions = [Eggmazing._NORTH, Eggmazing._SOUTH, Eggmazing._EAST, Eggmazing._WEST]
random.shuffle(directions)
# Now from the cell at (x, y), look to each of the four
# directions for unvisited neighboring cells
for each_direction in directions:
# If there is a border in direction[i], then don't try
# looking for a neighboring cell in that direction. We
# Use this check and borders to prevent generating invalid
# cell coordinates.
if self.is_border(x, y, each_direction):
continue
# Determine the cell coordinates of a neighboring cell
# NOTE: we trust the use of maze borders to prevent us
# from generating invalid cell coordinates
if each_direction == Eggmazing._NORTH:
nx = x
ny = y - 1
opposite_direction = Eggmazing._SOUTH
elif each_direction == Eggmazing._SOUTH:
nx = x
ny = y + 1
opposite_direction = Eggmazing._NORTH
elif each_direction == Eggmazing._EAST:
nx = x + 1
ny = y
opposite_direction = Eggmazing._WEST
else:
nx = x - 1
ny = y
opposite_direction = Eggmazing._EAST
# Wrap in the horizontal dimension
if nx < 0:
nx += self.w
elif nx >= self.w:
nx -= self.w
# See if this neighboring cell has been visited
if self.is_visited(nx, ny):
# Neighbor has been visited already
continue
# The neighboring cell has not been visited: remove the wall in
# the current cell leading to the neighbor. And, from the
# neighbor remove its wall leading to the current cell.
self.remove_wall(x, y, each_direction)
self.remove_wall(nx, ny, opposite_direction)
# Now recur by "moving" to this unvisited neighboring cell
self.handle_cell(depth + 1, nx, ny)
def draw_line(self, x1, y1, x2, y2):
if self.last_point is not None:
if (self.last_point[0] == x1) and (self.last_point[1] == y1):
self.path += ' L {0:d},{1:d}'.format(x2, y2)
self.last_point = [x2, y2]
elif (self.last_point[0] == x2) and (self.last_point[1] == y2):
self.path += ' L {0:d},{1:d} L {2:d},{3:d}'.format(x1, y1, x2, y2)
# self.last_point unchanged
else:
self.path += ' M {0:d},{1:d} L {2:d},{3:d}'.format(x1, y1, x2, y2)
self.last_point = [x2, y2]
else:
self.path = 'M {0:d},{1:d} L {2:d},{3:d}'.format(x1, y1, x2, y2)
self.last_point = [x2, y2]
def draw_wall(self, x, y, d, dir_):
if dir_ > 0:
if d == Eggmazing._NORTH:
self.draw_line(2 * (x + 1), 2 * y, 2 * x, 2 * y)
elif d == Eggmazing._WEST:
self.draw_line(2 * x, 2 * y, 2 * x, 2 * (y + 1))
elif d == Eggmazing._SOUTH:
self.draw_line(2 * (x + 1), 2 * (y + 1), 2 * x, 2 * (y + 1))
else: # Eggmazing._EAST
self.draw_line(2 * (x + 1), 2 * y, 2 * (x + 1), 2 * (y + 1))
else:
if d == Eggmazing._NORTH:
self.draw_line(2 * x, 2 * y, 2 * (x + 1), 2 * y)
elif d == Eggmazing._WEST:
self.draw_line(2 * x, 2 * (y + 1), 2 * x, 2 * y)
elif d == Eggmazing._SOUTH:
self.draw_line(2 * x, 2 * (y + 1), 2 * (x + 1), 2 * (y + 1))
else: # Eggmazing._EAST
self.draw_line(2 * (x + 1), 2 * (y + 1), 2 * (x + 1), 2 * y)
# Draw the vertical walls of the maze along the column of cells at
# horizontal positions
def draw_vertical(self, x):
# Drawing moving downwards from north to south
if self.is_wall(x, 0, Eggmazing._NORTH):
self.draw_wall(x, 0, Eggmazing._NORTH, +1)
for y in range(self.h):
if self.is_wall(x, y, Eggmazing._WEST):
self.draw_wall(x, y, Eggmazing._WEST, +1)
if self.is_wall(x, y, Eggmazing._SOUTH):
self.draw_wall(x, y, Eggmazing._SOUTH, +1)
# Now, return drawing upwards moving from south to north
x += 1
if x >= self.w:
return
for y in range(self.h - 1, -1, -1):
if self.is_wall(x, y, Eggmazing._SOUTH):
self.draw_wall(x, y, Eggmazing._SOUTH, -1)
if self.is_wall(x, y, Eggmazing._WEST):
self.draw_wall(x, y, Eggmazing._WEST, -1)
if self.is_wall(x, 0, Eggmazing._NORTH):
self.draw_wall(x, 0, Eggmazing._NORTH, -1)
# Draw the horizontal walls of the maze along the row of
# cells at "height" y: "high plotting precision" version
def draw_horizontal_hpp(self, y, wall):
# Cater to Python 2.4 and earlier
# dy = 0 if wall == Eggmazing._NORTH else 1
if wall == Eggmazing._NORTH:
dy = 0
else:
dy = 1
tracing = False
segment = 0
for x in range(self.w):
if self.is_wall(x, y, wall):
if not tracing:
# Starting a new segment
segment = x
tracing = True
else:
if tracing:
# Reached the end of a segment
self.draw_line(2 * segment, 2 * (y + dy),
2 * x, 2 * (y + dy))
tracing = False
if tracing:
# Draw the last wall segment
self.draw_line(2 * segment, 2 * (y + dy),
2 * self.w, 2 * (y + dy))
# Draw the vertical walls of the maze along the column of cells at
# horizontal position x: "high plotting precision" version
def draw_vertical_hpp(self, x, wall):
dx = 0 if wall == Eggmazing._WEST else 1
# We alternate the direction in which we draw each vertical wall.
# First, from North to South and then from South to North. This
# reduces pen travel on the Eggbot
if x % 2 == 0: # North-South
y_start, y_finis, dy, offset = 0, self.h, 1, 0
else: # South-North
y_start, y_finis, dy, offset = self.h - 1, -1, -1, 2
tracing = False
segment = y_start
for y in range(y_start, y_finis, dy):
assert 0 <= y < self.h, "y ({0:d}) is out of range".format(y)
if self.is_wall(x, y, wall):
if not tracing:
# Starting a new segment
segment = y
tracing = True
else:
if tracing:
# Hit the end of a segment
self.draw_line(2 * (x + dx), 2 * segment + offset,
2 * (x + dx), 2 * y + offset)
tracing = False
if tracing:
# complete the last wall segment
self.draw_line(2 * (x + dx), 2 * segment + offset,
2 * (x + dx), 2 * y_finis + offset)
if __name__ == '__main__':
Eggmazing().run()

View File

@ -0,0 +1,22 @@
[
{
"name": "Eggmazing",
"id": "fablabchemnitz.de.eggmazing",
"path": "eggmazing",
"dependent_extensions": null,
"original_name": "Eggmazing",
"original_id": "command.eggbot.contributed.eggmazing",
"license": "GNU GPL v3",
"license_url": "https://github.com/evil-mad/EggBot/blob/master/LICENSE",
"comment": "",
"source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/eggmazing",
"fork_url": "https://github.com/evil-mad/EggBot/tree/master/inkscape_contributed",
"documentation_url": "https://stadtfabrikanten.org/display/IFM/Eggmazing",
"inkscape_gallery_url": null,
"main_authors": [
"github.com/evil-mad",
"github.com/oskay",
"github.com/eridur-de"
]
}
]

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Estucheria - Airplane Type Linear Case</name>
<id>fablabchemnitz.de.airplanetypelinearcase</id>
<image>airplanetypelinearcase.svg</image>
<param name="unit" gui-text="Unit:" type="optiongroup" appearance="combo">
<option translatable="no" value="mm">mm</option>
</param>
<param name="width" type="float" min="0.1" max="300.0" gui-text="A - width:">25.0</param>
<param name="depth" type="float" min="0.1" max="300.0" gui-text="L - length:">25.0</param>
<param name="height" type="float" min="0.1" max="300.0" gui-text="H - height:">25.0</param>
<param name="glue_tab" type="float" min="0.1" max="300.0" gui-text="P - Width glue tab">5.0</param>
<param name="close_tab" type="float" min="0.1" max="300.0" gui-text="C - Top side sealing flange:">5.0</param>
<param name="side_tabs" type="float" min="0.1" max="300.0" gui-text="T - Top side closure tabs:">5.0</param>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu name="FabLab Chemnitz Boxes/Papercraft">
<submenu name="Paper/Cardboard Boxes" />
</submenu>
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">airplanetypelinearcase.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,239 @@
#! /usr/bin/env python3
#
#
# Este script dibuja el perfil exterior de corte la caja en un solo
# path cerrado y añade despues los otros flejes necesarios con colores
# diferentes para identificarlos.
# rojo > para cortes y perfil exterior
# azul > para hendidos
# verde > para taladros
# amarillo > medios cortes
#
# TODO:
# agregar opción de dibujo en cm/in
# mover dibujo al centro del documento
#
# 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 3 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.
#
__version__ = "0.2"
import inkex
class AirplaneTypeLinearCase(inkex.EffectExtension):
def add_arguments(self, pars):
pars.add_argument("--width", type=float, default=25.0, help="Ancho de la caja")
pars.add_argument("--height", type=float, default=25.0, help="Alto de la caja")
pars.add_argument("--depth", type=float, default=25.0, help="Largo de la caja")
pars.add_argument("--glue_tab", type=float, default=5.0, help="Ancho pestaña de engomado")
pars.add_argument("--close_tab", type=float, default=5.0, help="Alto pestaña de cierre")
pars.add_argument("--side_tabs", type=float, default=5.0, help="Alto pestañas laterales de cierre")
pars.add_argument("--unit", default="mm", help="Tipo de unidades")
def effect(self):
centro_ancho_documento = self.svg.unittouu(self.document.getroot().get('width'))/2
centro_alto_documento = self.svg.unittouu(self.document.getroot().get('height'))/2
medida_pestana1=5
medida_pestana2=1
medida_pestana3=4
medida_pestana4=3
ancho_caja = self.svg.unittouu(str(self.options.width) + self.options.unit)
alto_caja = self.svg.unittouu(str(self.options.height) + self.options.unit)
largo_caja = self.svg.unittouu(str(self.options.depth) + self.options.unit)
ancho_pestana_cola = self.svg.unittouu(str(self.options.glue_tab) + self.options.unit)
alto_pestana_cierre = self.svg.unittouu(str(self.options.close_tab) + self.options.unit)
alto_pestana = self.svg.unittouu(str(self.options.side_tabs) + self.options.unit)
if self.options.unit=="cm":
medida_pestana1=0.5
medida_pestana2=0.1
medida_pestana3=0.4
medida_pestana3=0.3
if self.options.unit=='in':
medida_pestana1=0.196
medida_pestana2=0.039
medida_pestana3=0.157
medida_pestana3=0.118
medida1_pestanas_laterales=self.svg.unittouu(str(medida_pestana1) + self.options.unit)
medida2_pestanas_laterales=self.svg.unittouu(str(medida_pestana2) + self.options.unit)
medida3_pestanas_laterales=self.svg.unittouu(str(medida_pestana3) + self.options.unit)
medida4_pestanas_laterales=self.svg.unittouu(str(medida_pestana4) + self.options.unit)
id_caja = self.svg.get_unique_id('estuche-lineal')
group = self.svg.get_current_layer().add(inkex.Group(id=id_caja))
estilo_linea_cortes = {'stroke': '#FF0000', 'fill': 'none', 'stroke-width': str(self.svg.unittouu('1px'))}
estilo_linea_hendidos = {'stroke': '#0000FF', 'fill': 'none', 'stroke-width': str(self.svg.unittouu('1px'))}
estilo_linea_medioscortes = {'stroke': '#00FFFF', 'fill': 'none', 'stroke-width': str(self.svg.unittouu('1px'))}
# line.path --> M = coordenadas absolutas
# line.path --> l = dibuja una linea desde el punto actual a las coordenadas especificadas
# line.path --> c = dibuja una curva beizer desde el punto actual a las coordenadas especificadas
# line.path --> q = dibuja un arco desde el punto actual a las coordenadas especificadas usando un punto como referencia
# line.path --> Z = cierra path
#Perfil Exterior de la caja
line = group.add(inkex.PathElement(id=id_caja + '-perfil-exterior'))
line.path = [
['M', [0, 0]],
['l', [0, 0-largo_caja]],
['l', [0, 0]],
['q', [0,0-alto_pestana_cierre,alto_pestana_cierre, 0-alto_pestana_cierre]],
['l', [ancho_caja-(alto_pestana_cierre*2), 0]],
['q', [alto_pestana_cierre,0,alto_pestana_cierre,alto_pestana_cierre]],
['l', [0, 0]],
['l', [0, (largo_caja)]],
['l', [medida4_pestanas_laterales, 0-medida4_pestanas_laterales]],
['l', [0,0-(alto_pestana-medida4_pestanas_laterales)]],
['l', [(largo_caja-medida2_pestanas_laterales-medida3_pestanas_laterales-medida4_pestanas_laterales), 0]],
['l', [medida3_pestanas_laterales, (alto_pestana-medida2_pestanas_laterales-medida1_pestanas_laterales)]],
['l', [medida2_pestanas_laterales, medida2_pestanas_laterales]],
['l', [0, medida1_pestanas_laterales]],
['l', [0,0]],
['l', [ancho_caja, 0]],
['l', [0,0]],
['l', [0, 0]],
['l', [0, 0-medida1_pestanas_laterales]],
['l', [medida2_pestanas_laterales, 0-medida2_pestanas_laterales]],
['l', [medida3_pestanas_laterales, 0-(alto_pestana-medida2_pestanas_laterales-medida1_pestanas_laterales)]],
['l', [(largo_caja-medida2_pestanas_laterales-medida3_pestanas_laterales-medida4_pestanas_laterales), 0]],
['l', [0,alto_pestana-medida4_pestanas_laterales]],
['l', [medida4_pestanas_laterales, medida4_pestanas_laterales]],
['l', [0, alto_caja]],
['l', [0-medida4_pestanas_laterales, medida4_pestanas_laterales]],
['l', [0,(alto_pestana-medida4_pestanas_laterales)]],
['l', [0-(largo_caja-medida2_pestanas_laterales-medida3_pestanas_laterales-medida4_pestanas_laterales), 0]],
['l', [0-(medida3_pestanas_laterales), 0-(alto_pestana-medida2_pestanas_laterales-medida1_pestanas_laterales)]],
['l', [0-(medida2_pestanas_laterales), 0-(medida2_pestanas_laterales)]],
['l', [0, 0-medida1_pestanas_laterales]],
['l', [0, 0]],
['l', [0,0]],
['l', [0-ancho_caja, 0]],
['l', [0,0]],
['l', [0, 0]],
['l', [0, medida1_pestanas_laterales]],
['l', [0-medida2_pestanas_laterales, medida2_pestanas_laterales]],
['l', [0-medida3_pestanas_laterales, (alto_pestana-medida2_pestanas_laterales-medida1_pestanas_laterales)]],
['l', [0-(largo_caja-medida2_pestanas_laterales-medida3_pestanas_laterales-medida4_pestanas_laterales), 0]],
['l', [0,0-(alto_pestana-medida4_pestanas_laterales)]],
['l', [0-medida4_pestanas_laterales, 0-medida4_pestanas_laterales]],
['l', [0,0]],
['l', [0, largo_caja]],
['l', [0, 0]],
['q', [0,alto_pestana_cierre,0-alto_pestana_cierre, alto_pestana_cierre]],#
['l', [0-(ancho_caja-(alto_pestana_cierre*2)), 0]],
['q', [0-alto_pestana_cierre,0,0-alto_pestana_cierre,0-alto_pestana_cierre]],
['l', [0, 0]],
['l', [0, 0-largo_caja]],
['l', [0, 0-medida2_pestanas_laterales]],
['l', [0-ancho_pestana_cola, 0-(ancho_pestana_cola/2)]],
['l', [0, 0-(alto_caja-ancho_pestana_cola-(medida2_pestanas_laterales*2))]],
['l', [ancho_pestana_cola, 0-(ancho_pestana_cola/2)]],
['Z', []]
]
line.style = estilo_linea_cortes
#Hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-1'))
line.path = [
['M', [0,0]],
['l', [0,alto_caja]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-2'))
line.path = [
['M', [ancho_caja,0]],
['l', [0,alto_caja]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-3'))
line.path = [
['M', [ancho_caja+largo_caja,0]],
['l', [0,alto_caja]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-4'))
line.path = [
['M', [ancho_caja+ancho_caja+largo_caja,0]],
['l', [0,alto_caja]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-5'))
line.path = [
['M', [ancho_caja,0]],
['l', [largo_caja,0]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-6'))
line.path = [
['M', [0,0]],
['l', [ancho_caja,0]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-7'))
line.path = [
['M', [(ancho_caja*2)+largo_caja,0]],
['l', [largo_caja,0]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-8'))
line.path = [
['M', [0,alto_caja]],
['l', [ancho_caja,0]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-9'))
line.path = [
['M', [ancho_caja,alto_caja]],
['l', [largo_caja,0]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-10'))
line.path = [
['M', [(ancho_caja*2)+largo_caja,alto_caja]],
['l', [largo_caja,0]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-11'))
line.path = [
['M', [0,0-(largo_caja)]],
['l', [ancho_caja,0]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-12'))
line.path = [
['M', [0,alto_caja+largo_caja]],
['l', [ancho_caja,0]]
]
line.style = estilo_linea_hendidos
if __name__ == '__main__':
AirplaneTypeLinearCase().run()

View File

@ -0,0 +1,228 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="94mm"
height="64mm"
viewBox="0 0 94.000001 64.000001"
version="1.1"
id="svg8"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
sodipodi:docname="LinealAvionLayout.svg">
<defs
id="defs2">
<rect
id="rect889"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
x="-76.706352"
y="209.06828"
width="23.786987"
height="31.003265"
id="rect889-5" />
<rect
x="-76.706352"
y="209.06828"
width="23.786987"
height="31.003265"
id="rect902" />
<rect
x="-76.706352"
y="209.06828"
width="23.786987"
height="31.003265"
id="rect889-2" />
<rect
x="-76.706352"
y="209.06828"
width="23.786987"
height="31.003265"
id="rect902-7" />
<rect
x="-76.706352"
y="209.06828"
width="23.786987"
height="31.003265"
id="rect889-53" />
<rect
x="-76.706352"
y="209.06828"
width="23.786987"
height="31.003265"
id="rect902-3" />
<rect
x="-76.706352"
y="209.06828"
width="23.786987"
height="31.003265"
id="rect889-6" />
<rect
x="-76.706352"
y="209.06828"
width="23.786987"
height="31.003265"
id="rect902-4" />
<rect
id="rect889-2-4"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect979"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
</defs>
<sodipodi:namedview
height="211mm"
inkscape:window-maximized="1"
inkscape:window-y="-8"
inkscape:window-x="-8"
inkscape:window-height="1018"
inkscape:window-width="1920"
showgrid="false"
inkscape:document-rotation="0"
inkscape:current-layer="layer1"
inkscape:document-units="mm"
inkscape:cy="331.71525"
inkscape:cx="241.92977"
inkscape:zoom="0.98994949"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#ffffff"
id="base" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(0,-232.99994)"
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Capa 1">
<text
transform="matrix(0.41705182,0,0,0.41705182,49.95402,191.64277)"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect889);fill:#000000;fill-opacity:1;stroke:none;"
id="text887"
xml:space="preserve"><tspan
x="-68.892474"
y="219.23689"><tspan
style="font-size:11.2889px">A</tspan></tspan></text>
<text
xml:space="preserve"
id="text887-5"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect889-5);fill:#000000;fill-opacity:1;stroke:none;"
transform="matrix(0.41705182,0,0,0.41705182,71.467503,191.34641)"><tspan
x="-68.264088"
y="219.23689"><tspan
style="font-size:11.2889px">L</tspan></tspan></text>
<text
xml:space="preserve"
id="text887-2"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect889-2);fill:#000000;fill-opacity:1;stroke:none;"
transform="matrix(0.41705182,0,0,0.41705182,79.886969,176.2421)"><tspan
x="-68.892474"
y="219.23689"><tspan
style="font-size:11.2889px">H</tspan></tspan></text>
<text
xml:space="preserve"
id="text887-56"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect889-53);fill:#000000;fill-opacity:1;stroke:none;"
transform="matrix(0.27672844,0,0,0.27672844,103.87092,227.43766)"><tspan
x="-68.264088"
y="219.23689"><tspan
style="font-size:11.2889px">T</tspan></tspan></text>
<text
xml:space="preserve"
id="text887-9"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect889-6);fill:#000000;fill-opacity:1;stroke:none;"
transform="matrix(0.41705182,0,0,0.41705182,50.619024,205.89437)"><tspan
x="-67.10791"
y="214.78815"><tspan
style="font-size:6.35px">C</tspan></tspan></text>
<text
transform="matrix(0.41705182,0,0,0.41705182,31.923261,189.95252)"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect889-2-4);fill:#000000;fill-opacity:1;stroke:none;"
id="text887-2-3"
xml:space="preserve"><tspan
x="-68.583793"
y="219.23689"><tspan
style="font-size:11.2889px">P</tspan></tspan></text>
<g
transform="matrix(0.41893423,0,0,0.24778878,8.2024521,246.91584)"
id="estuche-lineal3266">
<path
id="estuche-lineal3266-perfil-exterior"
d="m 0,0 v -30 0 q 0,-20 20,-20 h 30 q 20,0 20,20 v 0 30 l 3,-3 v -17 h 22 l 4,14 1,1 v 5 0 h 70 v 0 0 -5 l 1,-1 4,-14 h 22 v 17 l 3,3 v 150 l -3,3 v 17 h -22 l -4,-14 -1,-1 v -5 0 0 h -70 v 0 0 5 l -1,1 -4,14 H 73 v -17 l -3,-3 v 0 30 0 q 0,20 -20,20 H 20 Q 0,200 0,180 v 0 -30 -1 l -15,-7.5 V 8.5 L 0,1 Z"
style="fill:none;stroke:#ff0000;stroke-width:0.264583" />
<path
id="estuche-lineal3266-perfil-hendidos-1"
d="M 0,0 V 150 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="estuche-lineal3266-perfil-hendidos-2"
d="M 70,0 V 150 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="estuche-lineal3266-perfil-hendidos-3"
d="M 100,0 V 150 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="estuche-lineal3266-perfil-hendidos-4"
d="M 170,0 V 150 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="estuche-lineal3266-perfil-hendidos-5"
d="m 70,0 h 30 z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="estuche-lineal3266-perfil-hendidos-6"
d="M 0,0 H 70 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="estuche-lineal3266-perfil-hendidos-7"
d="m 170,0 h 30 z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="estuche-lineal3266-perfil-hendidos-8"
d="M 0,150 H 70 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="estuche-lineal3266-perfil-hendidos-9"
d="m 70,150 h 30 z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="estuche-lineal3266-perfil-hendidos-10"
d="m 170,150 h 30 z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="estuche-lineal3266-perfil-hendidos-11"
d="M 0,-30 H 70 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="estuche-lineal3266-perfil-hendidos-12"
d="M 0,180 H 70 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.8 KiB

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Estucheria - Automatic Bottom Case</name>
<id>fablabchemnitz.de.automaticbottomcase</id>
<image>automaticbottomcase.svg</image>
<param name="unit" gui-text="Unit:" type="optiongroup" appearance="combo">
<option translatable="no" value="mm">mm</option>
</param>
<param name="width" type="float" min="0.1" max="300.0" gui-text="A - width:">25.0</param>
<param name="depth" type="float" min="0.1" max="300.0" gui-text="L - length:">25.0</param>
<param name="height" type="float" min="0.1" max="300.0" gui-text="H - height:">25.0</param>
<param name="glue_tab" type="float" min="0.1" max="300.0" gui-text="P - Width glue tab">5.0</param>
<param name="close_tab" type="float" min="0.1" max="300.0" gui-text="C - Top side sealing flange:">5.0</param>
<param name="side_tabs" type="float" min="0.1" max="300.0" gui-text="T - Top side closure tabs:">5.0</param>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu name="FabLab Chemnitz Boxes/Papercraft">
<submenu name="Paper/Cardboard Boxes" />
</submenu>
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">automaticbottomcase.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,233 @@
#! /usr/bin/env python3
#
#
# This script draws the outer profile of the box cut in a single
# closed path and then add the other necessary strips with colours
# different to identify them.
# red > for cuts and outer profile
# blue > for crevices
# green > for drills
# yellow > half-cut
#
# EVERYTHING:
# add cm/in drawing option
# move drawing to the center of the document
#
# 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 3 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.
#
__version__ = "0.2"
import inkex
class AutomaticBottomCase(inkex.EffectExtension):
def add_arguments(self, pars):
pars.add_argument("--width", type=float, default=25.0, help="Width of the box")
pars.add_argument("--height", type=float, default=25.0, help="Height of the box")
pars.add_argument("--depth", type=float, default=25.0, help="Length of the box")
pars.add_argument("--glue_tab", type=float, default=5.0, help="Tab width")
pars.add_argument("--close_tab", type=float, default=5.0, help="Height sealing flange")
pars.add_argument("--side_tabs", type=float, default=5.0, help="Height side sealing flanges")
pars.add_argument("--unit", default="mm", help="Type of units")
def effect(self):
width_center_document = self.svg.unittouu(self.document.getroot().get('width'))/2
height_center_document = self.svg.unittouu(self.document.getroot().get('height'))/2
eyelash_measure1=5
eyelash_measure2=1
eyelash_measure3=4
eyelash_measure4=3
box_width = self.svg.unittouu(str(self.options.width) + self.options.unit)
box_height = self.svg.unittouu(str(self.options.height) + self.options.unit)
box_length = self.svg.unittouu(str(self.options.depth) + self.options.unit)
glue_width = self.svg.unittouu(str(self.options.glue_tab) + self.options.unit)
top_flap_closure = self.svg.unittouu(str(self.options.close_tab) + self.options.unit)
top_flap = self.svg.unittouu(str(self.options.side_tabs) + self.options.unit)
if self.options.unit=="cm":
eyelash_measure1=0.5
eyelash_measure2=0.1
eyelash_measure3=0.4
eyelash_measure3=0.3
if self.options.unit=='in':
eyelash_measure1=0.196
eyelash_measure2=0.039
eyelash_measure3=0.157
eyelash_measure3=0.118
measure1_side_blind=self.svg.unittouu(str(eyelash_measure1) + self.options.unit)
measure2_side_blind=self.svg.unittouu(str(eyelash_measure2) + self.options.unit)
measure3_side_blind=self.svg.unittouu(str(eyelash_measure3) + self.options.unit)
measure4_side_blind=self.svg.unittouu(str(eyelash_measure4) + self.options.unit)
box_id = self.svg.get_unique_id('automaticbottomcase')
group = self.svg.get_current_layer().add(inkex.Group(id=box_id))
line_style_cuts = {'stroke': '#FF0000', 'fill': 'none', 'stroke-width': str(self.svg.unittouu('1px'))}
cleft_line_style = {'stroke': '#0000FF', 'fill': 'none', 'stroke-width': str(self.svg.unittouu('1px'))}
line_style_drills = {'stroke': '#00FF00', 'fill': 'none', 'stroke-width': str(self.svg.unittouu('1px'))}
media_line_style = {'stroke': '#00FFFF', 'fill': 'none', 'stroke-width': str(self.svg.unittouu('1px'))}
# line.path --> M = absolute coordinates
# line.path --> l = draws a line from the current point to the specified coordinates
# line.path --> c = draws a beizer curve from the current point to the specified coordinates
# line.path --> q = draws an arc from the current point to the specified coordinates using a point as reference
# line.path --> Z = close path
#Outside profile of the box
line = group.add(inkex.PathElement(id=box_id + '-profile-outside'))
line.path = [
['M', [0, 0]],
['l', [0, 0-box_length]],
['l', [0, 0]],
['q', [0,0-top_flap_closure,top_flap_closure, 0-top_flap_closure]],
['l', [box_width-(top_flap_closure*2), 0]],
['q', [top_flap_closure,0,top_flap_closure,top_flap_closure]],
['l', [0, 0]],
['l', [0, (box_length)]],
['l', [measure4_side_blind, 0-measure4_side_blind]],
['l', [0,0-(top_flap-measure4_side_blind)]],
['l', [(box_length-measure2_side_blind-measure3_side_blind-measure4_side_blind), 0]],
['l', [measure3_side_blind, (top_flap-measure2_side_blind-measure1_side_blind)]],
['l', [measure2_side_blind, measure2_side_blind]],
['l', [0, measure1_side_blind]],
['l', [0,0]],
['l', [box_width, 0]],
['l', [0,0]],
['l', [0, 0]],
['l', [0, 0-measure1_side_blind]],
['l', [measure2_side_blind, 0-measure2_side_blind]],
['l', [measure3_side_blind, 0-(top_flap-measure2_side_blind-measure1_side_blind)]],
['l', [(box_length-measure2_side_blind-measure3_side_blind-measure4_side_blind), 0]],
['l', [0,top_flap-measure4_side_blind]],
['l', [measure4_side_blind, measure4_side_blind]],
['l', [0, box_height]],
['l', [0-((box_length/2)),box_length/2]],
['l', [0-((box_length/2)-(measure2_side_blind)*2),0]],
['l', [0-(measure2_side_blind),0-((box_length/2)-measure2_side_blind)]],
['l', [0-measure2_side_blind,0-measure2_side_blind]],
['l', [0-measure2_side_blind,measure2_side_blind]],
['l', [0,box_length*0.7]],
['l', [0-(((box_length*0.5)-measure2_side_blind)/2),0]],
['l', [0-(((box_length*0.5)-measure2_side_blind)/2),0-((box_length*0.7)-(box_length*0.5))]],
['l', [0-((box_width/2)-((box_length*0.5)-measure2_side_blind)),0]],
['l', [measure3_side_blind,measure3_side_blind]],
['l', [0-((box_length*0.7)-(box_length*0.5))-measure3_side_blind,((box_length*0.7)-(box_length*0.5))-measure3_side_blind]],
['l', [0-(((box_width/2)-((box_length*0.5)))+((box_length*0.7)-(box_length*0.5))),0]],
['l', [0-measure4_side_blind/2,0-((box_length*0.7))-measure2_side_blind]],
['l', [0-(box_length/2),box_length/2]],
['l', [0-((box_length/2)-(measure2_side_blind)*2),0]],
['l', [0-measure2_side_blind,0-((box_length/2)-measure2_side_blind)]],
['l', [0-measure2_side_blind,0-measure2_side_blind]],
['l', [0-measure2_side_blind,measure2_side_blind]],
['l', [0,box_length*0.7]],
['l', [0-(((box_length*0.5)-measure2_side_blind)/2),0]],
['l', [0-(((box_length*0.5)-measure2_side_blind)/2),0-((box_length*0.7)-(box_length*0.5))]],
['l', [0-((box_width/2)-((box_length*0.5)-measure2_side_blind)),0]],
['l', [measure3_side_blind,measure3_side_blind]],
['l', [0-((box_length*0.7)-(box_length*0.5))-measure3_side_blind,((box_length*0.7)-(box_length*0.5))-measure3_side_blind]],
['l', [0-(((box_width/2)-((box_length*0.5)))+((box_length*0.7)-(box_length*0.5))),0]],
['l', [0-measure4_side_blind/2,0-((box_length*0.7))-measure2_side_blind]],
['l', [0, 0-measure2_side_blind]],
['l', [0-glue_width, 0-(glue_width/2)]],
['l', [0, 0-(box_height-glue_width-(measure2_side_blind*2))]],
['l', [glue_width, 0-(glue_width/2)]],
['Z', []]
]
line.style = line_style_cuts
#Hendidos
line = group.add(inkex.PathElement(id=box_id + '-profile-splits-1'))
line.path = [
['M', [0,0]],
['l', [0,box_height]]
]
line.style = cleft_line_style
line = group.add(inkex.PathElement(id=box_id + '-profile-splits-2'))
line.path = [
['M', [box_width,0]],
['l', [0,box_height]]
]
line.style = cleft_line_style
line = group.add(inkex.PathElement(id=box_id + '-profile-splits-3'))
line.path = [
['M', [box_width+box_length,0]],
['l', [0,box_height]]
]
line.style = cleft_line_style
line = group.add(inkex.PathElement(id=box_id + '-profile-splits-4'))
line.path = [
['M', [box_width+box_width+box_length,0]],
['l', [0,box_height]]
]
line.style = cleft_line_style
line = group.add(inkex.PathElement(id=box_id + '-profile-splits-5'))
line.path = [
['M', [0,0]],
['l', [box_width,0]]
]
line.style = cleft_line_style
line = group.add(inkex.PathElement(id=box_id + '-profile-splits-6'))
line.path = [
['M', [0,box_height]],
['l', [((box_length+box_width)*2),0]]
]
line.style = cleft_line_style
line = group.add(inkex.PathElement(id=box_id + '-profile-splits-7'))
line.path = [
['M', [box_width,0]],
['l', [box_length,0]]
]
line.style = cleft_line_style
line = group.add(inkex.PathElement(id=box_id + '-profile-splits-8'))
line.path = [
['M', [box_length+box_width*2,0]],
['l', [box_length,0]]
]
line.style = cleft_line_style
line = group.add(inkex.PathElement(id=box_id + '-profile-splits-9'))
line.path = [
['M', [0,0-(box_length)]],
['l', [box_width,0]]
]
line.style = cleft_line_style
line = group.add(inkex.PathElement(id=box_id + '-profile-drill-1'))
line.path = [
['M', [box_width-measure2_side_blind,box_height+measure2_side_blind]],
['l', [0-((box_length*0.5)),(box_length*0.5)]]
]
line.style = line_style_drills
line = group.add(inkex.PathElement(id=box_id + '-profile-drill-2'))
line.path = [
['M', [((box_width*2)+box_length)-measure2_side_blind,box_height+measure2_side_blind]],
['l', [0-((box_length*0.5)),(box_length*0.5)]]
]
line.style = line_style_drills
if __name__ == '__main__':
AutomaticBottomCase().run()

View File

@ -0,0 +1,236 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="94mm"
height="64mm"
viewBox="0 0 94.000001 64.000001"
version="1.1"
id="svg8"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
sodipodi:docname="FondoAutomaticoLayout.svg">
<defs
id="defs2">
<rect
x="-76.706352"
y="209.06828"
width="23.786987"
height="31.003265"
id="rect889" />
<rect
id="rect889-5"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect902"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect889-2"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect902-7"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect889-53"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect902-3"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect889-6"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect902-4"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
x="-76.706352"
y="209.06828"
width="23.786987"
height="31.003265"
id="rect889-2-4" />
<rect
x="-76.706352"
y="209.06828"
width="23.786987"
height="31.003265"
id="rect979" />
<rect
id="rect889-2-4-1"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect2863"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.8"
inkscape:cx="135.62322"
inkscape:cy="128.43274"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
inkscape:window-width="1920"
inkscape:window-height="1018"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
height="211mm" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Capa 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-232.99994)">
<text
xml:space="preserve"
id="text887"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect889);fill:#000000;fill-opacity:1;stroke:none;"
transform="matrix(0.41705182,0,0,0.41705182,46.319357,195.18017)"><tspan
x="-68.892474"
y="219.23689"><tspan
style="font-size:11.2889px">A</tspan></tspan></text>
<text
transform="matrix(0.41705182,0,0,0.41705182,68.201319,194.96547)"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect889-5);fill:#000000;fill-opacity:1;stroke:none;"
id="text887-5"
xml:space="preserve"><tspan
x="-68.264088"
y="219.23689"><tspan
style="font-size:11.2889px">L</tspan></tspan></text>
<text
transform="matrix(0.41705182,0,0,0.41705182,77.672498,179.97242)"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect889-2);fill:#000000;fill-opacity:1;stroke:none;"
id="text887-2"
xml:space="preserve"><tspan
x="-68.892474"
y="219.23689"><tspan
style="font-size:11.2889px">H</tspan></tspan></text>
<text
transform="matrix(0.41705182,0,0,0.41705182,112.60673,158.33517)"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect889-53);fill:#000000;fill-opacity:1;stroke:none;"
id="text887-56"
xml:space="preserve"><tspan
x="-68.264088"
y="219.23689"><tspan
style="font-size:11.2889px">T</tspan></tspan></text>
<text
xml:space="preserve"
id="text887-2-3"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect889-2-4);fill:#000000;fill-opacity:1;stroke:none;"
transform="matrix(0.28752215,0,0,0.28752215,21.573379,221.63586)"><tspan
x="-68.583793"
y="219.23689"><tspan
style="font-size:11.2889px">P</tspan></tspan></text>
<g
transform="matrix(0.25345327,0,0,0.23462671,5.0055809,251.88848)"
id="estuche-fondoautomatico8066">
<path
id="estuche-fondoautomatico8066-perfil-exterior"
d="m 0,0 v -50.25 h 0.25 q 0,-20 20,-20 h 79.5 q 20,0 20,20 H 120 V -0.5 l 3,-3 v -27 h 41.5 l 4,24 1,1 v 5 0.25 h 120 V 0 h 0.25 v -5 l 1,-1 4,-24 h 41.75 v 27 l 3,3 v 150 l -24.75,25 H 292 l -1,-24 -1,-1 -1,1 v 35 h -12 l -12,-10 h -36 l 4,4 -14,6 h -45 l -4,-35.5 -25,25 h -23 l -1,-24 -1,-1 -1,1 v 35 h -12 l -12,-10 H 59 l 4,4 -14,6 H 4 L 0,151.25 v -1 L -15,142.5 V 10 L 0,2.25 Z"
style="fill:none;stroke:#ff0000;stroke-width:0.264583" />
<path
id="estuche-fondoautomatico8066-perfil-hendidos-1"
d="M 0,0 V 150 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="estuche-fondoautomatico8066-perfil-hendidos-2"
d="M 120,0 V 150 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="estuche-fondoautomatico8066-perfil-hendidos-3"
d="M 170,0 V 150 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="estuche-fondoautomatico8066-perfil-hendidos-4"
d="M 290,0 V 150 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="estuche-fondoautomatico8066-perfil-hendidos-5"
d="M 0,0 H 120 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="estuche-fondoautomatico8066-perfil-hendidos-6"
d="M 0,150.25 H 339.5 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="estuche-fondoautomatico8066-perfil-hendidos-7"
d="m 120.25,0.25 h 49.5 z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="estuche-fondoautomatico8066-perfil-hendidos-8"
d="M 290.5,0.25 H 340 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="estuche-fondoautomatico8066-perfil-hendidos-9"
d="m 0.25,-50.25 h 119.5 z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="estuche-fondoautomatico8066-perfil-taladro-1"
d="M 119.25,151.25 94.5,176 Z"
style="fill:none;stroke:#00ff00;stroke-width:0.264583" />
<path
id="estuche-fondoautomatico8066-perfil-taladro-2"
d="M 289.5,151.25 264.75,176 Z"
style="fill:none;stroke:#00ff00;stroke-width:0.264583" />
</g>
<text
transform="matrix(0.28752215,0,0,0.28752215,38.815037,175.78393)"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect889-2-4-1);fill:#000000;fill-opacity:1;stroke:none;"
id="text887-2-3-2"
xml:space="preserve"><tspan
x="-68.892474"
y="219.23689"><tspan
style="font-size:11.2889px">C</tspan></tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Estucheria - 4 Point Base Box</name>
<id>fablabchemnitz.de.box4p</id>
<image>box4p.svg</image>
<param name="unit" gui-text="Unit:" type="optiongroup" appearance="combo">
<option translatable="no" value="mm">mm</option>
</param>
<param name="width" type="float" min="0.1" max="300.0" gui-text="A - width:">25.0</param>
<param name="depth" type="float" min="0.1" max="300.0" gui-text="L - length:">25.0</param>
<param name="height" type="float" min="0.1" max="300.0" gui-text="H - height:">25.0</param>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu name="FabLab Chemnitz Boxes/Papercraft">
<submenu name="Paper/Cardboard Boxes" />
</submenu>
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">box4p.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,162 @@
#! /usr/bin/env python3
#
# Este script dibuja el perfil exterior de corte la caja en un solo
# path cerrado y añade despues los otros flejes necesarios con colores
# diferentes para identificarlos.
# rojo > para cortes y perfil exterior
# azul > para hendidos
# verde > para taladros
# amarillo > medios cortes
#
# TODO:
# agregar opción de dibujo en cm/in
# mover dibujo al centro del documento
#
# 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 3 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.
#
__version__ = "0.2"
import inkex
class Box4P(inkex.EffectExtension):
def add_arguments(self, pars):
pars.add_argument("--width", type=float, default=25.0, help="Ancho de la caja")
pars.add_argument("--height", type=float, default=25.0, help="Alto de la caja")
pars.add_argument("--depth", type=float, default=25.0, help="Largo de la caja")
pars.add_argument("--unit", default="mm", help="Tipo de unidades")
def effect(self):
centro_ancho_documento = self.svg.unittouu(self.document.getroot().get('width'))/2
centro_alto_documento = self.svg.unittouu(self.document.getroot().get('height'))/2
medida_pestana1=2
ancho_caja = self.svg.unittouu(str(self.options.width) + self.options.unit)
alto_caja = self.svg.unittouu(str(self.options.height) + self.options.unit)
largo_caja = self.svg.unittouu(str(self.options.depth) + self.options.unit)
if self.options.unit=="cm":
medida_pestana1=0.2
if self.options.unit=='in':
medida_pestana1=0.0787
medida1_pestanas_laterales=self.svg.unittouu(str(medida_pestana1) + self.options.unit)
id_caja = self.svg.get_unique_id('bandeja-4P')
group = self.svg.get_current_layer().add(inkex.Group(id=id_caja))
estilo_linea_cortes = {'stroke': '#FF0000', 'fill': 'none', 'stroke-width': str(self.svg.unittouu('1px'))}
estilo_linea_hendidos = {'stroke': '#0000FF', 'fill': 'none', 'stroke-width': str(self.svg.unittouu('1px'))}
estilo_linea_taladros = {'stroke': '#00FF00', 'fill': 'none', 'stroke-width': str(self.svg.unittouu('1px'))}
estilo_linea_medioscortes = {'stroke': '#00FFFF', 'fill': 'none', 'stroke-width': str(self.svg.unittouu('1px'))}
# line.path --> M = coordenadas absolutas
# line.path --> l = dibuja una linea desde el punto actual a las coordenadas especificadas
# line.path --> c = dibuja una curva beizer desde el punto actual a las coordenadas especificadas
# line.path --> q = dibuja un arco desde el punto actual a las coordenadas especificadas usando un punto como referencia
# line.path --> Z = cierra path
#Perfil Exterior de la caja
line = group.add(inkex.PathElement(id=id_caja + '-perfil-exterior'))
line.path = [
['M', [0, 0]],
['l', [alto_caja,0]],
['l', [ancho_caja,0]],
['l', [alto_caja,0]],
['l', [0,alto_caja-medida1_pestanas_laterales]],
['l', [0-(alto_caja-medida1_pestanas_laterales),0]],
['l', [0-medida1_pestanas_laterales,medida1_pestanas_laterales]],
['l', [alto_caja,0]],
['l', [0,largo_caja]],
['l', [0-alto_caja,0]],
['l', [medida1_pestanas_laterales,medida1_pestanas_laterales]],
['l', [alto_caja-medida1_pestanas_laterales,0]],
['l', [0,alto_caja-medida1_pestanas_laterales]],
['l', [0-(alto_caja),0]],
['l', [0-(ancho_caja),0]],
['l', [0-(alto_caja),0]],
['l', [0,0-(alto_caja-medida1_pestanas_laterales)]],
['l', [alto_caja-medida1_pestanas_laterales,0]],
['l', [medida1_pestanas_laterales,0-medida1_pestanas_laterales]],
['l', [0-(alto_caja),0]],
['l', [0,0-largo_caja]],
['l', [alto_caja,0]],
['l', [0-medida1_pestanas_laterales,0-medida1_pestanas_laterales]],
['l', [0-(alto_caja-medida1_pestanas_laterales),0]],
['Z', []]
]
line.style = estilo_linea_cortes
#Hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-1'))
line.path = [
['M', [alto_caja,0]],
['l', [0,largo_caja+(alto_caja*2)]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-2'))
line.path = [
['M', [ancho_caja+alto_caja,0]],
['l', [0,largo_caja+(alto_caja*2)]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-3'))
line.path = [
['M', [alto_caja,alto_caja]],
['l', [ancho_caja,0]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-4'))
line.path = [
['M', [alto_caja,alto_caja+largo_caja]],
['l', [ancho_caja,0]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-5'))
line.path = [
['M', [alto_caja,alto_caja]],
['l', [0-alto_caja,alto_caja]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-6'))
line.path = [
['M', [alto_caja+ancho_caja,alto_caja]],
['l', [alto_caja,alto_caja]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-7'))
line.path = [
['M', [alto_caja+ancho_caja,alto_caja+largo_caja]],
['l', [alto_caja,0-alto_caja]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-8'))
line.path = [
['M', [alto_caja,alto_caja+largo_caja]],
['l', [0-alto_caja,0-alto_caja]]
]
line.style = estilo_linea_hendidos
if __name__ == '__main__':
Box4P().run()

View File

@ -0,0 +1,212 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="94mm"
height="64mm"
viewBox="0 0 94.000001 64.000001"
version="1.1"
id="svg8"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
sodipodi:docname="Fondo4PLayout.svg">
<defs
id="defs2">
<rect
x="-76.706352"
y="209.06828"
width="23.786987"
height="31.003265"
id="rect889" />
<rect
id="rect889-5"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect902"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect889-2"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect902-7"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect889-53"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect902-3"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect889-6"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect902-4"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
x="-76.706352"
y="209.06828"
width="23.786987"
height="31.003265"
id="rect889-2-4" />
<rect
x="-76.706352"
y="209.06828"
width="23.786987"
height="31.003265"
id="rect979" />
<rect
id="rect889-53-2"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect2158"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect889-6-3"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect2161"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.4"
inkscape:cx="30.346562"
inkscape:cy="510.32236"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
inkscape:window-width="1920"
inkscape:window-height="1018"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
height="211mm" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Capa 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-232.99994)">
<text
xml:space="preserve"
id="text887"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect889);fill:#000000;fill-opacity:1;stroke:none;"
transform="matrix(0.41705182,0,0,0.41705182,37.180884,174.77023)"><tspan
x="-68.892474"
y="219.23689"><tspan
style="font-size:11.2889px">A</tspan></tspan></text>
<text
transform="matrix(0.41705182,0,0,0.41705182,73.057456,197.94617)"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect889-5);fill:#000000;fill-opacity:1;stroke:none;"
id="text887-5"
xml:space="preserve"><tspan
x="-68.264088"
y="219.23689"><tspan
style="font-size:11.2889px">L</tspan></tspan></text>
<text
transform="matrix(0.41705182,0,0,0.41705182,116.8177,198.07432)"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect889-2);fill:#000000;fill-opacity:1;stroke:none;"
id="text887-2"
xml:space="preserve"><tspan
x="-68.892474"
y="219.23689"><tspan
style="font-size:11.2889px">H</tspan></tspan></text>
<g
transform="matrix(0,0.16755463,-0.19590377,0,92.695792,234.23686)"
id="bandeja-4P4625">
<path
id="bandeja-4P4625-perfil-exterior"
d="m 0,0 h 30 305 30 v 28 h -28 l -2,2 h 30 v 404 h -30 l 2,2 h 28 v 28 H 335 30 0 v -28 h 28 l 2,-2 H 0 V 30 H 30 L 28,28 H 0 Z"
style="fill:none;stroke:#ff0000;stroke-width:0.264583" />
<path
id="bandeja-4P4625-perfil-hendidos-1"
d="M 30,0 V 464 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="bandeja-4P4625-perfil-hendidos-2"
d="M 335,0 V 464 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="bandeja-4P4625-perfil-hendidos-3"
d="M 30,30 H 335 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="bandeja-4P4625-perfil-hendidos-4"
d="M 30,434 H 335 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="bandeja-4P4625-perfil-hendidos-5"
d="M 30,30 0,60 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="bandeja-4P4625-perfil-hendidos-6"
d="m 335,30 30,30 z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="bandeja-4P4625-perfil-hendidos-7"
d="m 335,434 30,-30 z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="bandeja-4P4625-perfil-hendidos-8"
d="M 30,434 0,404 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Estucheria - Double Railing Case</name>
<id>fablabchemnitz.de.doublerailingcase</id>
<image>doublerailingcase.svg</image>
<param name="unit" gui-text="Unit:" type="optiongroup" appearance="combo">
<option translatable="no" value="mm">mm</option>
</param>
<param name="width" type="float" min="0.1" max="300.0" gui-text="A - width:">25.0</param>
<param name="depth" type="float" min="0.1" max="300.0" gui-text="L - length:">25.0</param>
<param name="height" type="float" min="0.1" max="300.0" gui-text="H - height:">25.0</param>
<param name="glue_tab" type="float" min="0.1" max="300.0" gui-text="P - Width glue tab">5.0</param>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu name="FabLab Chemnitz Boxes/Papercraft">
<submenu name="Paper/Cardboard Boxes" />
</submenu>
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">doublerailingcase.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,225 @@
#! /usr/bin/env python3
#
#
# This script draws the outer profile of the box cut in a single
# closed path and then add the other necessary strips with colours
# different to identify them.
# red > for cuts and outer profile
# blue > for crevices
# green > for drills
# yellow > half-cut
#
# TODO:
# add cm/in drawing option
# move drawing to the center of the document
#
# 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 3 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.
#
__version__ = "0.2"
import inkex
class DoubeRailingCase(inkex.EffectExtension):
def add_arguments(self, pars):
pars.add_argument("--width", type=float, default=25.0, help="Width of the box")
pars.add_argument("--height", type=float, default=25.0, help="Height of the box")
pars.add_argument("--depth", type=float, default=25.0, help="Length of the box")
pars.add_argument("--glue_tab", type=float, default=5.0, help="Tab width")
pars.add_argument("--unit", default="mm", help="Type of units")
def effect(self):
center_width_document = self.svg.unittouu(self.document.getroot().get('width'))/2
center_height_document = self.svg.unittouu(self.document.getroot().get('height'))/2
box_width = self.svg.unittouu(str(self.options.width) + self.options.unit)
box_height = self.svg.unittouu(str(self.options.height) + self.options.unit)
box_length = self.svg.unittouu(str(self.options.depth) + self.options.unit)
eyelash_width = self.svg.unittouu(str(self.options.glue_tab) + self.options.unit)
eyelash_measure1=2
eyelash_measure2=1
eyelash_measure3=5
eyelash_measure4=3
id_box = self.svg.get_unique_id('double-railing-case')
group = self.svg.get_current_layer().add(inkex.Group(id=id_box))
line_style_cuts = {'stroke': '#FF0000', 'fill': 'none', 'stroke-width': str(self.svg.unittouu('1px'))}
cleft_line_style = {'stroke': '#0000FF', 'fill': 'none', 'stroke-width': str(self.svg.unittouu('1px'))}
line_style_drills = {'stroke': '#00FF00', 'fill': 'none', 'stroke-width': str(self.svg.unittouu('1px'))}
media_line_style = {'stroke': '#00FFFF', 'fill': 'none', 'stroke-width': str(self.svg.unittouu('1px'))}
# line.path --> M = absolute coordinates
# line.path --> l = draws a line from the current point to the specified coordinates
# line.path --> c = draws a beizer curve from the current point to the specified coordinates
# line.path --> q = draws an arc from the current point to the specified coordinates using a point as reference
# line.path --> Z = close path
#Outside profile of the box
line = group.add(inkex.PathElement(id=id_box + '-profile-outside'))
line.path = [
['M', [0, 0]],
['l', [box_width,0]],
['l', [0,0]],
['l', [0,eyelash_width]],
['l', [eyelash_width,0]],
['l', [0-(eyelash_width-eyelash_measure1),box_height-eyelash_measure4]],
['l', [0-eyelash_measure1,eyelash_measure2]],
['l', [0,eyelash_measure1]],
['l', [box_height-eyelash_measure3,eyelash_measure3]],
['l', [eyelash_measure3,box_height-eyelash_measure3]],
['l', [eyelash_measure1,0]],
['l', [eyelash_measure2,eyelash_measure1]],
['l', [box_height-eyelash_measure4,eyelash_width-eyelash_measure1]],
['l', [0,box_length-(eyelash_width*2)]],
['l', [0-(box_height-eyelash_measure4),eyelash_width-eyelash_measure1]],
['l', [0-eyelash_measure2,eyelash_measure1]],
['l', [0-eyelash_measure1,0]],
['l', [0-eyelash_measure3,box_height-eyelash_measure3]],
['l', [0-(box_height-eyelash_measure3),eyelash_measure3]],
['l', [0,eyelash_measure1]],
['l', [eyelash_measure1,eyelash_measure2]],
['l', [eyelash_width-eyelash_measure1,box_height-eyelash_measure4]],
['l', [0-eyelash_width,0]],
['l', [0,0]],
['l', [0,eyelash_width]],
['l', [0-box_width,0]],
['l', [0,0-eyelash_width]],
['l', [0,0]],
['l', [0-eyelash_width,0]],
['l', [eyelash_width-eyelash_measure1,0-(box_height-eyelash_measure4)]],
['l', [eyelash_measure1,0-eyelash_measure2]],
['l', [0,0-eyelash_measure1]],
['l', [0-(box_height-eyelash_measure3),0-eyelash_measure3]],
['l', [0-eyelash_measure3,0-(box_height-eyelash_measure3)]],
['l', [0-eyelash_measure1,0]],
['l', [0-eyelash_measure2,0-eyelash_measure1]],
['l', [0-(box_height-eyelash_measure4),0-(eyelash_width-eyelash_measure1)]],
['l', [0,0-(box_length-(eyelash_width*2))]],
['l', [box_height-eyelash_measure4,0-(eyelash_width-eyelash_measure1),]],
['l', [eyelash_measure2,0-eyelash_measure1]],
['l', [eyelash_measure1,0]],
['l', [eyelash_measure3,0-(box_height-eyelash_measure3)]],
['l', [(box_height-eyelash_measure3),0-eyelash_measure3]],
['l', [0,0-eyelash_measure1]],
['l', [0-eyelash_measure1,0-eyelash_measure2]],
['l', [0-(eyelash_width-eyelash_measure1),0-(box_height-eyelash_measure4)]],
['l', [eyelash_width,0]],
['Z', []]
]
line.style = line_style_cuts
#profile splits
line = group.add(inkex.PathElement(id=id_box + '-profile-splits-1'))
line.path = [
['M', [0,eyelash_width]],
['l', [box_width,0]]
]
line.style = cleft_line_style
line = group.add(inkex.PathElement(id=id_box + '-profile-splits-2'))
line.path = [
['M', [0,(box_height+eyelash_width)]],
['l', [box_width,0]]
]
line.style = cleft_line_style
line = group.add(inkex.PathElement(id=id_box + '-profile-splits-3'))
line.path = [
['M', [0-box_height,((box_height*2)+eyelash_width)]],
['l', [box_width+(box_height*2),0]]
]
line.style = cleft_line_style
line = group.add(inkex.PathElement(id=id_box + '-profile-splits-4'))
line.path = [
['M', [0-box_height,(((box_height*2)+eyelash_width)+box_length)]],
['l', [box_width+(box_height*2),0]]
]
line.style = cleft_line_style
line = group.add(inkex.PathElement(id=id_box + '-profile-splits-5'))
line.path = [
['M', [0,(box_height*3)+box_length+eyelash_width]],
['l', [box_width,0]]
]
line.style = cleft_line_style
line = group.add(inkex.PathElement(id=id_box + '-profile-splits-6'))
line.path = [
['M', [0,(box_height*4)+box_length+eyelash_width]],
['l', [box_width,0]]
]
line.style = cleft_line_style
line = group.add(inkex.PathElement(id=id_box + '-profile-splits-7'))
line.path = [
['M', [0,eyelash_width]],
['l', [0,box_length+(box_height*4)]]
]
line.style = cleft_line_style
line = group.add(inkex.PathElement(id=id_box + '-profile-splits-8'))
line.path = [
['M', [box_width,eyelash_width]],
['l', [0,box_length+(box_height*4)]]
]
line.style = cleft_line_style
line = group.add(inkex.PathElement(id=id_box + '-profile-splits-9'))
line.path = [
['M', [0-box_height,eyelash_width+(box_height*2)]],
['l', [0,box_length]]
]
line.style = cleft_line_style
line = group.add(inkex.PathElement(id=id_box + '-profile-splits-10'))
line.path = [
['M', [box_width+box_height,eyelash_width+(box_height*2)]],
['l', [0,box_length]]
]
line.style = cleft_line_style
line = group.add(inkex.PathElement(id=id_box + '-profile-splits-11'))
line.path = [
['M', [0,eyelash_width+(box_height*2)]],
['l', [0-(box_height-eyelash_measure3),0-(box_height-eyelash_measure3)]]
]
line.style = cleft_line_style
line = group.add(inkex.PathElement(id=id_box + '-profile-splits-12'))
line.path = [
['M', [box_width,eyelash_width+(box_height*2)]],
['l', [box_height-eyelash_measure3,0-(box_height-eyelash_measure3)]]
]
line.style = cleft_line_style
line = group.add(inkex.PathElement(id=id_box + '-profile-splits-13'))
line.path = [
['M', [box_width,eyelash_width+(box_height*2)+box_length]],
['l', [box_height-eyelash_measure3,box_height-eyelash_measure3]]
]
line.style = cleft_line_style
line = group.add(inkex.PathElement(id=id_box + '-profile-splits-14'))
line.path = [
['M', [0,eyelash_width+(box_height*2)+box_length]],
['l', [0-(box_height-eyelash_measure3),box_height-eyelash_measure3]]
]
line.style = cleft_line_style
if __name__ == '__main__':
DoubeRailingCase().run()

View File

@ -0,0 +1,220 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="94mm"
height="64mm"
viewBox="0 0 94.000001 64.000001"
version="1.1"
id="svg8"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
sodipodi:docname="DobleBarandaLayout.svg">
<defs
id="defs2">
<rect
id="rect889"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
x="-76.706352"
y="209.06828"
width="23.786987"
height="31.003265"
id="rect889-5" />
<rect
x="-76.706352"
y="209.06828"
width="23.786987"
height="31.003265"
id="rect902" />
<rect
x="-76.706352"
y="209.06828"
width="23.786987"
height="31.003265"
id="rect889-2" />
<rect
x="-76.706352"
y="209.06828"
width="23.786987"
height="31.003265"
id="rect902-7" />
<rect
x="-76.706352"
y="209.06828"
width="23.786987"
height="31.003265"
id="rect889-53" />
<rect
x="-76.706352"
y="209.06828"
width="23.786987"
height="31.003265"
id="rect902-3" />
<rect
x="-76.706352"
y="209.06828"
width="23.786987"
height="31.003265"
id="rect889-6" />
<rect
x="-76.706352"
y="209.06828"
width="23.786987"
height="31.003265"
id="rect902-4" />
<rect
id="rect889-2-4"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect979"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
</defs>
<sodipodi:namedview
height="211mm"
inkscape:window-maximized="1"
inkscape:window-y="-8"
inkscape:window-x="-8"
inkscape:window-height="1018"
inkscape:window-width="1920"
showgrid="false"
inkscape:document-rotation="0"
inkscape:current-layer="layer1"
inkscape:document-units="mm"
inkscape:cy="108.71689"
inkscape:cx="162.05938"
inkscape:zoom="1.4"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#ffffff"
id="base" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(0,-232.99994)"
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Capa 1">
<text
transform="matrix(0.41705182,0,0,0.41705182,47.465771,176.04713)"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect889);fill:#000000;fill-opacity:1;stroke:none;"
id="text887"
xml:space="preserve"><tspan
x="-68.892474"
y="219.23689"><tspan
style="font-size:11.2889px">A</tspan></tspan></text>
<text
xml:space="preserve"
id="text887-5"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect889-5);fill:#000000;fill-opacity:1;stroke:none;"
transform="matrix(0.41705182,0,0,0.41705182,74.101888,192.29305)"><tspan
x="-68.264088"
y="219.23689"><tspan
style="font-size:11.2889px">L</tspan></tspan></text>
<text
xml:space="preserve"
id="text887-2"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect889-2);fill:#000000;fill-opacity:1;stroke:none;"
transform="matrix(0.41705182,0,0,0.41705182,101.82825,197.6344)"><tspan
x="-68.892474"
y="219.23689"><tspan
style="font-size:11.2889px">H</tspan></tspan></text>
<text
transform="matrix(0.27672844,0,0,0.27672844,108.8139,222.99361)"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect889-2-4);fill:#000000;fill-opacity:1;stroke:none;"
id="text887-2-3"
xml:space="preserve"><tspan
x="-68.583793"
y="219.23689"><tspan
style="font-size:11.2889px">P</tspan></tspan></text>
<g
transform="matrix(0,-0.18377696,0.20645318,0,1.7584784,284.73758)"
id="estuche-doble-baranda9828">
<path
id="estuche-doble-baranda9828-perfil-exterior"
d="m 0,0 h 210 v 15 h 15 l -13,27 -2,1 v 2 l 25,5 5,25 h 2 l 1,2 27,13 v 260 l -27,13 -1,2 h -2 l -5,25 -25,5 v 2 l 2,1 13,27 h -15 v 15 H 0 v -15 h -15 l 13,-27 2,-1 v -2 l -25,-5 -5,-25 h -2 l -1,-2 -27,-13 V 90 l 27,-13 1,-2 h 2 L -25,50 0,45 V 43 L -2,42 -15,15 H 0 Z"
style="fill:none;stroke:#ff0000;stroke-width:0.264583" />
<path
id="estuche-doble-baranda9828-perfil-hendidos-1"
d="M 0,15 H 210 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="estuche-doble-baranda9828-perfil-hendidos-2"
d="M 0,45 H 210 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="estuche-doble-baranda9828-perfil-hendidos-3"
d="M -30,75 H 240 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="estuche-doble-baranda9828-perfil-hendidos-4"
d="M -30,365 H 240 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="estuche-doble-baranda9828-perfil-hendidos-5"
d="M 0,395 H 210 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="estuche-doble-baranda9828-perfil-hendidos-6"
d="M 0,425 H 210 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="estuche-doble-baranda9828-perfil-hendidos-7"
d="M 0,15 V 425 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="estuche-doble-baranda9828-perfil-hendidos-8"
d="M 210,15 V 425 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="estuche-doble-baranda9828-perfil-hendidos-9"
d="M -30,75 V 365 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="estuche-doble-baranda9828-perfil-hendidos-10"
d="M 240,75 V 365 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="estuche-doble-baranda9828-perfil-hendidos-11"
d="M 0,75 -25,50 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="estuche-doble-baranda9828-perfil-hendidos-12"
d="M 210,75 235,50 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="estuche-doble-baranda9828-perfil-hendidos-13"
d="m 210,365 25,25 z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="estuche-doble-baranda9828-perfil-hendidos-14"
d="m 0,365 -25,25 z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Estucheria - Girdle</name>
<id>fablabchemnitz.de.girdle</id>
<image>girdle.svg</image>
<param name="unit" gui-text="Unit:" type="optiongroup" appearance="combo">
<option translatable="no" value="mm">mm</option>
</param>
<param name="width" type="float" min="0.1" max="300.0" gui-text="A - width:">25.0</param>
<param name="depth" type="float" min="0.1" max="300.0" gui-text="L - length:">25.0</param>
<param name="height" type="float" min="0.1" max="300.0" gui-text="H - height:">25.0</param>
<param name="glue_tab" type="float" min="0.1" max="300.0" gui-text="P - Width glue tab">5.0</param>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu name="FabLab Chemnitz Boxes/Papercraft">
<submenu name="Paper/Cardboard Boxes" />
</submenu>
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">girdle.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,137 @@
#! /usr/bin/env python3
#
# Este script dibuja el perfil exterior de corte la caja en un solo
# path cerrado y añade despues los otros flejes necesarios con colores
# diferentes para identificarlos.
# rojo > para cortes y perfil exterior
# azul > para hendidos
# verde > para taladros
# amarillo > medios cortes
#
# TODO:
# agregar opción de dibujo en cm/in
# mover dibujo al centro del documento
#
# 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 3 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.
#
__version__ = "0.2"
import inkex
class Girdle(inkex.EffectExtension):
def add_arguments(self, pars):
pars.add_argument("--width", type=float, default=25.0, help="Ancho de la caja")
pars.add_argument("--height", type=float, default=25.0, help="Alto de la caja")
pars.add_argument("--depth", type=float, default=25.0, help="Largo de la caja")
pars.add_argument("--glue_tab", type=float, default=5.0, help="Ancho pestaña de engomado")
pars.add_argument("--unit", default="mm", help="Tipo de unidades")
def effect(self):
centro_ancho_documento = self.svg.unittouu(self.document.getroot().get('width'))/2
centro_alto_documento = self.svg.unittouu(self.document.getroot().get('height'))/2
medida_pestana1=5
medida_pestana2=1
medida_pestana3=4
medida_pestana4=3
ancho_caja = self.svg.unittouu(str(self.options.width) + self.options.unit)
alto_caja = self.svg.unittouu(str(self.options.height) + self.options.unit)
largo_caja = self.svg.unittouu(str(self.options.depth) + self.options.unit)
ancho_pestana_cola = self.svg.unittouu(str(self.options.glue_tab) + self.options.unit)
if self.options.unit=="cm":
medida_pestana1=0.5
medida_pestana2=0.1
medida_pestana3=0.4
medida_pestana3=0.3
if self.options.unit=='in':
medida_pestana1=0.196
medida_pestana2=0.039
medida_pestana3=0.157
medida_pestana3=0.118
medida1_pestanas_laterales=self.svg.unittouu(str(medida_pestana1) + self.options.unit)
medida2_pestanas_laterales=self.svg.unittouu(str(medida_pestana2) + self.options.unit)
medida3_pestanas_laterales=self.svg.unittouu(str(medida_pestana3) + self.options.unit)
medida4_pestanas_laterales=self.svg.unittouu(str(medida_pestana4) + self.options.unit)
id_caja = self.svg.get_unique_id('estuche-faja')
group = self.svg.get_current_layer().add(inkex.Group(id=id_caja))
estilo_linea_cortes = {'stroke': '#FF0000', 'fill': 'none', 'stroke-width': str(self.svg.unittouu('1px'))}
estilo_linea_hendidos = {'stroke': '#0000FF', 'fill': 'none', 'stroke-width': str(self.svg.unittouu('1px'))}
estilo_linea_taladros = {'stroke': '#00FF00', 'fill': 'none', 'stroke-width': str(self.svg.unittouu('1px'))}
estilo_linea_medioscortes = {'stroke': '#00FFFF', 'fill': 'none', 'stroke-width': str(self.svg.unittouu('1px'))}
# line.path --> M = coordenadas absolutas
# line.path --> l = dibuja una linea desde el punto actual a las coordenadas especificadas
# line.path --> c = dibuja una curva beizer desde el punto actual a las coordenadas especificadas
# line.path --> q = dibuja un arco desde el punto actual a las coordenadas especificadas usando un punto como referencia
# line.path --> Z = cierra path
#Perfil Exterior de la caja
line = group.add(inkex.PathElement(id=id_caja + '-perfil-exterior'))
line.path = [
['M', [0, 0]],
['l', [ancho_caja,0]],
['l', [largo_caja,0]],
['l', [ancho_caja,0]],
['l', [largo_caja,0]],
['l', [0,alto_caja]],
['l', [0-(largo_caja),0]],
['l', [0-(ancho_caja),0]],
['l', [0-largo_caja,0]],
['l', [0-ancho_caja,0]],
['l', [0-ancho_pestana_cola,0-ancho_pestana_cola]],
['l', [0,0-(alto_caja-(ancho_pestana_cola*2))]],
['Z', []]
]
line.style = estilo_linea_cortes
#Hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-1'))
line.path = [
['M', [0,0]],
['l', [0,alto_caja]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-2'))
line.path = [
['M', [ancho_caja,0]],
['l', [0,alto_caja]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-3'))
line.path = [
['M', [ancho_caja+largo_caja,0]],
['l', [0,alto_caja]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-4'))
line.path = [
['M', [ancho_caja+ancho_caja+largo_caja,0]],
['l', [0,alto_caja]]
]
line.style = estilo_linea_hendidos
if __name__ == '__main__':
Girdle().run()

View File

@ -0,0 +1,180 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="94mm"
height="64mm"
viewBox="0 0 94.000001 64.000001"
version="1.1"
id="svg8"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
sodipodi:docname="FajaLayout.svg">
<defs
id="defs2">
<rect
id="rect889"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
x="-76.706352"
y="209.06828"
width="23.786987"
height="31.003265"
id="rect889-5" />
<rect
x="-76.706352"
y="209.06828"
width="23.786987"
height="31.003265"
id="rect902" />
<rect
x="-76.706352"
y="209.06828"
width="23.786987"
height="31.003265"
id="rect889-2" />
<rect
x="-76.706352"
y="209.06828"
width="23.786987"
height="31.003265"
id="rect902-7" />
<rect
x="-76.706352"
y="209.06828"
width="23.786987"
height="31.003265"
id="rect889-53" />
<rect
x="-76.706352"
y="209.06828"
width="23.786987"
height="31.003265"
id="rect902-3" />
<rect
x="-76.706352"
y="209.06828"
width="23.786987"
height="31.003265"
id="rect889-6" />
<rect
x="-76.706352"
y="209.06828"
width="23.786987"
height="31.003265"
id="rect902-4" />
<rect
id="rect889-2-4"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect979"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
</defs>
<sodipodi:namedview
height="211mm"
inkscape:window-maximized="1"
inkscape:window-y="-8"
inkscape:window-x="-8"
inkscape:window-height="1018"
inkscape:window-width="1920"
showgrid="false"
inkscape:document-rotation="0"
inkscape:current-layer="layer1"
inkscape:document-units="mm"
inkscape:cy="59.842544"
inkscape:cx="-219.62908"
inkscape:zoom="0.98994949"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#ffffff"
id="base" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(0,-232.99994)"
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Capa 1">
<text
transform="matrix(0.41705182,0,0,0.41705182,50.709976,203.54903)"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect889);fill:#000000;fill-opacity:1;stroke:none;"
id="text887"
xml:space="preserve"><tspan
x="-68.892474"
y="219.23689"><tspan
style="font-size:11.2889px">A</tspan></tspan></text>
<text
xml:space="preserve"
id="text887-5"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect889-5);fill:#000000;fill-opacity:1;stroke:none;"
transform="matrix(0.41705182,0,0,0.41705182,70.900538,203.47943)"><tspan
x="-68.264088"
y="219.23689"><tspan
style="font-size:11.2889px">L</tspan></tspan></text>
<text
xml:space="preserve"
id="text887-2"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect889-2);fill:#000000;fill-opacity:1;stroke:none;"
transform="matrix(0.41705182,0,0,0.41705182,78.753039,174.35222)"><tspan
x="-68.892474"
y="219.23689"><tspan
style="font-size:11.2889px">H</tspan></tspan></text>
<text
transform="matrix(0.41705182,0,0,0.41705182,31.50749,197.62541)"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect889-2-4);fill:#000000;fill-opacity:1;stroke:none;"
id="text887-2-3"
xml:space="preserve"><tspan
x="-68.583793"
y="219.23689"><tspan
style="font-size:11.2889px">P</tspan></tspan></text>
<g
transform="matrix(0.32727879,0,0,0.40392018,6.8339186,235.01699)"
id="estuche-faja5487">
<path
id="estuche-faja5487-perfil-exterior"
d="m 0,0 h 100 30 99.75 29.75 V 150 H 229.75 130 100 0 L -15,135 V 15 Z"
style="fill:none;stroke:#ff0000;stroke-width:0.264583" />
<path
id="estuche-faja5487-perfil-hendidos-1"
d="M 0,0 V 150 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="estuche-faja5487-perfil-hendidos-2"
d="M 100,0 V 150 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="estuche-faja5487-perfil-hendidos-3"
d="M 129.75,0 V 150 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
<path
id="estuche-faja5487-perfil-hendidos-4"
d="M 229.5,0 V 150 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.264583" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Estucheria - Linear Case</name>
<id>fablabchemnitz.de.linearcase</id>
<image>linearcase.svg</image>
<param name="unit" gui-text="Unit:" type="optiongroup" appearance="combo">
<option translatable="no" value="mm">mm</option>
</param>
<param name="width" type="float" min="0.1" max="300.0" gui-text="A - width:">25.0</param>
<param name="depth" type="float" min="0.1" max="300.0" gui-text="L - length:">25.0</param>
<param name="height" type="float" min="0.1" max="300.0" gui-text="H - height:">25.0</param>
<param name="glue_tab" type="float" min="0.1" max="300.0" gui-text="P - Width glue tab">5.0</param>
<param name="close_tab" type="float" min="0.1" max="300.0" gui-text="C - Top side sealing flange:">5.0</param>
<param name="side_tabs" type="float" min="0.1" max="300.0" gui-text="T - Top side closure tabs:">5.0</param>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu name="FabLab Chemnitz Boxes/Papercraft">
<submenu name="Paper/Cardboard Boxes" />
</submenu>
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">linearcase.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,239 @@
#! /usr/bin/env python3
#
#
# Este script dibuja el perfil exterior de corte la caja en un solo
# path cerrado y añade despues los otros flejes necesarios con colores
# diferentes para identificarlos.
# rojo > para cortes y perfil exterior
# azul > para hendidos
# verde > para taladros
# amarillo > medios cortes
#
# TODO:
# agregar opción de dibujo en cm/in
# mover dibujo al centro del documento
#
# 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 3 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.
#
__version__ = "0.2"
import inkex
class LinearCase(inkex.EffectExtension):
def add_arguments(self, pars):
pars.add_argument("--width", type=float, default=25.0, help="Ancho de la caja")
pars.add_argument("--height", type=float, default=25.0, help="Alto de la caja")
pars.add_argument("--depth", type=float, default=25.0, help="Largo de la caja")
pars.add_argument("--glue_tab", type=float, default=5.0, help="Ancho pestaña de engomado")
pars.add_argument("--close_tab", type=float, default=5.0, help="Alto pestaña de cierre")
pars.add_argument("--side_tabs", type=float, default=5.0, help="Alto pestañas laterales de cierre")
pars.add_argument("--unit", default="mm", help="Tipo de unidades")
def effect(self):
centro_ancho_documento = self.svg.unittouu(self.document.getroot().get('width'))/2
centro_alto_documento = self.svg.unittouu(self.document.getroot().get('height'))/2
ancho_caja = self.svg.unittouu(str(self.options.width) + self.options.unit)
alto_caja = self.svg.unittouu(str(self.options.height) + self.options.unit)
largo_caja = self.svg.unittouu(str(self.options.depth) + self.options.unit)
ancho_pestana_cola = self.svg.unittouu(str(self.options.glue_tab) + self.options.unit)
alto_pestana_cierre = self.svg.unittouu(str(self.options.close_tab) + self.options.unit)
alto_pestana = self.svg.unittouu(str(self.options.side_tabs) + self.options.unit)
if self.options.unit=="mm":
medida_pestana1=5
medida_pestana2=1
medida_pestana3=4
medida_pestana4=3
if self.options.unit=="cm":
medida_pestana1=0.5
medida_pestana2=0.1
medida_pestana3=0.4
medida_pestana4=0.3
if self.options.unit=='in':
medida_pestana1=0.196
medida_pestana2=0.039
medida_pestana3=0.157
medida_pestana4=0.118
medida1_pestanas_laterales=self.svg.unittouu(str(medida_pestana1) + self.options.unit)
medida2_pestanas_laterales=self.svg.unittouu(str(medida_pestana2) + self.options.unit)
medida3_pestanas_laterales=self.svg.unittouu(str(medida_pestana3) + self.options.unit)
medida4_pestanas_laterales=self.svg.unittouu(str(medida_pestana4) + self.options.unit)
id_caja = self.svg.get_unique_id('estuche-lineal')
group = self.svg.get_current_layer().add(inkex.Group(id=id_caja))
estilo_linea_cortes = {'stroke': '#FF0000', 'fill': 'none', 'stroke-width': str(self.svg.unittouu('1px'))}
estilo_linea_hendidos = {'stroke': '#0000FF', 'fill': 'none', 'stroke-width': str(self.svg.unittouu('1px'))}
estilo_linea_medioscortes = {'stroke': '#00FFFF', 'fill': 'none', 'stroke-width': str(self.svg.unittouu('1px'))}
# line.path --> M = coordenadas absolutas
# line.path --> l = dibuja una linea desde el punto actual a las coordenadas especificadas
# line.path --> c = dibuja una curva beizer desde el punto actual a las coordenadas especificadas
# line.path --> q = dibuja un arco desde el punto actual a las coordenadas especificadas usando un punto como referencia
# line.path --> Z = cierra path
#Perfil Exterior de la caja
line = group.add(inkex.PathElement(id=id_caja + '-perfil-exterior'))
line.path = [
['M', [0, 0]],
['l', [ancho_caja, 0]],
['l', [0,0]],
['l', [0, 0]],
['l', [0, 0-medida1_pestanas_laterales]],
['l', [medida2_pestanas_laterales, 0-medida2_pestanas_laterales]],
['l', [medida3_pestanas_laterales, 0-(alto_pestana-medida2_pestanas_laterales-medida1_pestanas_laterales)]],
['l', [(largo_caja-medida2_pestanas_laterales-medida3_pestanas_laterales-medida4_pestanas_laterales), 0]],
['l', [0,alto_pestana-medida4_pestanas_laterales]],
['l', [medida4_pestanas_laterales, medida4_pestanas_laterales]],
['l', [0, 0-largo_caja]],
['l', [0, 0]],
['q', [0,0-alto_pestana_cierre,alto_pestana_cierre, 0-alto_pestana_cierre]],
['l', [ancho_caja-(alto_pestana_cierre*2), 0]],
['q', [alto_pestana_cierre,0,alto_pestana_cierre,alto_pestana_cierre]],
['l', [0, 0]],
['l', [0, (largo_caja)]],
['l', [medida4_pestanas_laterales, 0-medida4_pestanas_laterales]],
['l', [0,0-(alto_pestana-medida4_pestanas_laterales)]],
['l', [(largo_caja-medida2_pestanas_laterales-medida3_pestanas_laterales-medida4_pestanas_laterales), 0]],
['l', [medida3_pestanas_laterales, (alto_pestana-medida2_pestanas_laterales-medida1_pestanas_laterales)]],
['l', [medida2_pestanas_laterales, medida2_pestanas_laterales]],
['l', [0, medida1_pestanas_laterales]],
['l', [0,0]],
['l', [0, alto_caja]],
['l', [0-medida4_pestanas_laterales, medida4_pestanas_laterales]],
['l', [0,(alto_pestana-medida4_pestanas_laterales)]],
['l', [0-(largo_caja-medida2_pestanas_laterales-medida3_pestanas_laterales-medida4_pestanas_laterales), 0]],
['l', [0-(medida3_pestanas_laterales), 0-(alto_pestana-medida2_pestanas_laterales-medida1_pestanas_laterales)]],
['l', [0-(medida2_pestanas_laterales), 0-(medida2_pestanas_laterales)]],
['l', [0, 0-medida1_pestanas_laterales]],
['l', [0, 0]],
['l', [0,0]],
['l', [0-ancho_caja, 0]],
['l', [0,0]],
['l', [0, 0]],
['l', [0, medida1_pestanas_laterales]],
['l', [0-medida2_pestanas_laterales, medida2_pestanas_laterales]],
['l', [0-medida3_pestanas_laterales, (alto_pestana-medida2_pestanas_laterales-medida1_pestanas_laterales)]],
['l', [0-(largo_caja-medida2_pestanas_laterales-medida3_pestanas_laterales-medida4_pestanas_laterales), 0]],
['l', [0,0-(alto_pestana-medida4_pestanas_laterales)]],
['l', [0-medida4_pestanas_laterales, 0-medida4_pestanas_laterales]],
['l', [0,0]],
['l', [0, largo_caja]],
['l', [0, 0]],
['q', [0,alto_pestana_cierre,0-alto_pestana_cierre, alto_pestana_cierre]],#
['l', [0-(ancho_caja-(alto_pestana_cierre*2)), 0]],
['q', [0-alto_pestana_cierre,0,0-alto_pestana_cierre,0-alto_pestana_cierre]],
['l', [0, 0]],
['l', [0, 0-largo_caja]],
['l', [0, 0-medida2_pestanas_laterales]],
['l', [0-ancho_pestana_cola, 0-(ancho_pestana_cola/2)]],
['l', [0, 0-(alto_caja-ancho_pestana_cola-(medida2_pestanas_laterales*2))]],
['l', [ancho_pestana_cola, 0-(ancho_pestana_cola/2)]],
['Z', []]
]
line.style = estilo_linea_cortes
#Hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-1'))
line.path = [
['M', [0,0]],
['l', [0,alto_caja]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-2'))
line.path = [
['M', [ancho_caja,0]],
['l', [0,alto_caja]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-3'))
line.path = [
['M', [ancho_caja+largo_caja,0]],
['l', [0,alto_caja]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-4'))
line.path = [
['M', [ancho_caja+ancho_caja+largo_caja,0]],
['l', [0,alto_caja]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-5'))
line.path = [
['M', [ancho_caja,0]],
['l', [largo_caja,0]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-6'))
line.path = [
['M', [ancho_caja+largo_caja,0]],
['l', [ancho_caja,0]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-7'))
line.path = [
['M', [(ancho_caja*2)+largo_caja,0]],
['l', [largo_caja,0]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-8'))
line.path = [
['M', [0,alto_caja]],
['l', [ancho_caja,0]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-9'))
line.path = [
['M', [ancho_caja,alto_caja]],
['l', [largo_caja,0]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-10'))
line.path = [
['M', [(ancho_caja*2)+largo_caja,alto_caja]],
['l', [largo_caja,0]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-11'))
line.path = [
['M', [ancho_caja+largo_caja,0-(largo_caja)]],
['l', [ancho_caja,0]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-12'))
line.path = [
['M', [0,alto_caja+largo_caja]],
['l', [ancho_caja,0]]
]
line.style = estilo_linea_hendidos
if __name__ == '__main__':
LinearCase().run()

View File

@ -0,0 +1,217 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="94mm"
height="64mm"
viewBox="0 0 94.000001 64.000001"
version="1.1"
id="svg8"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
sodipodi:docname="LinealLayout.svg">
<defs
id="defs2">
<rect
x="-76.706352"
y="209.06828"
width="23.786987"
height="31.003265"
id="rect889" />
<rect
id="rect889-5"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect902"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect889-2"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect902-7"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect889-53"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect902-3"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect889-6"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect902-4"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
x="-76.706352"
y="209.06828"
width="23.786987"
height="31.003265"
id="rect889-2-4" />
<rect
x="-76.706352"
y="209.06828"
width="23.786987"
height="31.003265"
id="rect979" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.7"
inkscape:cx="242.19298"
inkscape:cy="182.95136"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
inkscape:window-width="1920"
inkscape:window-height="1018"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
height="211mm" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Capa 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-232.99994)">
<g
transform="translate(0.11175705,0.20100678)"
id="g2216">
<path
style="fill:none;stroke:#ff0000;stroke-width:0.110345"
d="M 8.3109493,250.29248 H 37.504577 v -2.08526 l 0.417052,-0.41705 1.251155,-6.25578 h 9.592192 v 7.50693 l 1.251155,1.25116 v -12.51156 c 0,-2.08525 1.04263,-3.12788 3.127889,-3.12788 h 22.93785 c 2.08526,0 3.127889,1.04263 3.127889,3.12788 v 12.51156 l 1.251156,-1.25116 v -7.50693 h 9.592192 l 1.251155,6.25578 0.417052,0.41705 v 2.08526 29.01291 l -1.251156,1.25115 v 7.50693 h -9.592192 l -1.251155,-6.25577 -0.417052,-0.41706 v -2.08525 H 50.016131 v 2.08525 l -0.417051,0.41706 -1.251156,6.25577 h -9.592191 v -7.50693 l -1.251156,-1.25115 v 12.51155 c 0,2.08526 -1.04263,3.12789 -3.127889,3.12789 h -22.93785 c -2.0852592,0 -3.1278887,-1.04263 -3.1278887,-3.12789 v -12.51155 l -6.2557774,-2.91937 v -23.17418 z"
id="estuche-lineal2174-perfil-exterior"
sodipodi:nodetypes="ccccccccsssscccccccccccccccccccccsssscccc" />
<path
style="fill:none;stroke:#0000ff;stroke-width:0.075174"
d="m 8.3109493,250.27078 v 29.03461 z"
id="estuche-lineal2174-perfil-hendidos-1" />
<path
style="fill:none;stroke:#0000ff;stroke-width:0.075174"
d="m 37.504577,250.27078 v 29.03461 z"
id="estuche-lineal2174-perfil-hendidos-2" />
<path
style="fill:none;stroke:#0000ff;stroke-width:0.075174"
d="m 50.016131,250.27078 v 29.03461 z"
id="estuche-lineal2174-perfil-hendidos-3" />
<path
style="fill:none;stroke:#0000ff;stroke-width:0.075174"
d="m 79.209759,250.27078 v 29.03461 z"
id="estuche-lineal2174-perfil-hendidos-4" />
<path
style="fill:none;stroke:#0000ff;stroke-width:0.110345"
d="M 37.504577,250.29269 H 91.721314 Z"
id="estuche-lineal2174-perfil-hendidos-5" />
<path
style="fill:none;stroke:#0000ff;stroke-width:0.110345"
d="M 8.3109493,279.30539 H 50.016131 Z"
id="estuche-lineal2174-perfil-hendidos-6" />
<path
style="fill:none;stroke:#0000ff;stroke-width:0.110345"
d="M 79.209759,279.30539 H 91.721314 Z"
id="estuche-lineal2174-perfil-hendidos-7" />
<path
style="fill:none;stroke:#0000ff;stroke-width:0.110345"
d="M 8.3109493,291.81694 H 37.504577 Z"
id="estuche-lineal2174-perfil-hendidos-8" />
<path
style="fill:none;stroke:#0000ff;stroke-width:0.110345"
d="M 50.016131,237.56039 H 79.209759 Z"
id="estuche-lineal2174-perfil-hendidos-9" />
<text
transform="matrix(0.41705182,0,0,0.41705182,49.653275,186.90606)"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect889);fill:#000000;fill-opacity:1;stroke:none;"
id="text887"
xml:space="preserve"><tspan
x="-68.892474"
y="219.23689"><tspan
style="font-size:11.2889px">A</tspan></tspan></text>
<text
xml:space="preserve"
id="text887-5"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect889-5);fill:#000000;fill-opacity:1;stroke:none;"
transform="matrix(0.41705182,0,0,0.41705182,70.788782,187.02545)"><tspan
x="-68.264088"
y="219.23689"><tspan
style="font-size:11.2889px">L</tspan></tspan></text>
<text
xml:space="preserve"
id="text887-2"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect889-2);fill:#000000;fill-opacity:1;stroke:none;"
transform="matrix(0.41705182,0,0,0.41705182,79.397236,174.15122)"><tspan
x="-68.892474"
y="219.23689"><tspan
style="font-size:11.2889px">H</tspan></tspan></text>
<text
xml:space="preserve"
id="text887-56"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect889-53);fill:#000000;fill-opacity:1;stroke:none;"
transform="matrix(0.41705182,0,0,0.41705182,112.58601,193.71337)"><tspan
x="-68.264088"
y="219.23689"><tspan
style="font-size:11.2889px">T</tspan></tspan></text>
<text
xml:space="preserve"
id="text887-9"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect889-6);fill:#000000;fill-opacity:1;stroke:none;"
transform="matrix(0.41705182,0,0,0.41705182,49.940303,204.74843)"><tspan
x="-67.10791"
y="214.78815"><tspan
style="font-size:6.35px">C</tspan></tspan></text>
<text
transform="matrix(0.41705182,0,0,0.41705182,31.773707,184.57322)"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect889-2-4);fill:#000000;fill-opacity:1;stroke:none;"
id="text887-2-3"
xml:space="preserve"><tspan
x="-68.583793"
y="219.23689"><tspan
style="font-size:11.2889px">P</tspan></tspan></text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

@ -0,0 +1,21 @@
[
{
"name": "Estucheria - <various>",
"id": "fablabchemnitz.de.estucheria.<various>",
"path": "estucheria",
"dependent_extensions": null,
"original_name": "<various>",
"original_id": "org.inkscape.estucheria.<various>",
"license": "GNU GPL v3",
"license_url": "https://gitlab.com/aljurado/packaging-inkscape-extensions/-/blob/master/LICENSE",
"comment": "",
"source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/estucheria",
"fork_url": "https://gitlab.com/aljurado/packaging-inkscape-extensions",
"documentation_url": "https://stadtfabrikanten.org/display/IFM/Estucheria",
"inkscape_gallery_url": null,
"main_authors": [
"gitlab.com/aljurado",
"github.com/eridur-de"
]
}
]

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Estucheria - Swiss Bottom Case</name>
<id>fablabchemnitz.de.swissbottomcase</id>
<image>swissbottomcase.svg</image>
<param name="unit" gui-text="Unit:" type="optiongroup" appearance="combo">
<option translatable="no" value="mm">mm</option>
</param>
<param name="width" type="float" min="0.1" max="300.0" gui-text="A - width:">25.0</param>
<param name="depth" type="float" min="0.1" max="300.0" gui-text="L - length:">25.0</param>
<param name="height" type="float" min="0.1" max="300.0" gui-text="H - height:">25.0</param>
<param name="glue_tab" type="float" min="0.1" max="300.0" gui-text="P - Width glue tab">5.0</param>
<param name="close_tab" type="float" min="0.1" max="300.0" gui-text="C - Top side sealing flange:">5.0</param>
<param name="side_tabs" type="float" min="0.1" max="300.0" gui-text="T - Top side closure tabs:">5.0</param>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu name="FabLab Chemnitz Boxes/Papercraft">
<submenu name="Paper/Cardboard Boxes" />
</submenu>
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">swissbottomcase.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,218 @@
#! /usr/bin/env python3
#
# Este script dibuja el perfil exterior de corte la caja en un solo
# path cerrado y añade despues los otros flejes necesarios con colores
# diferentes para identificarlos.
# rojo > para cortes y perfil exterior
# azul > para hendidos
# verde > para taladros
# amarillo > medios cortes
#
# TODO:
# agregar opción de dibujo en cm/in
# mover dibujo al centro del documento.
#
# 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 3 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.
#
__version__ = "0.2"
import inkex
class SwissBottomCase(inkex.EffectExtension):
def add_arguments(self, pars):
pars.add_argument("--width", type=float, default=25.0, help="Ancho de la caja")
pars.add_argument("--height", type=float, default=25.0, help="Alto de la caja")
pars.add_argument("--depth", type=float, default=25.0, help="Largo de la caja")
pars.add_argument("--glue_tab", type=float, default=5.0, help="Ancho pestaña de engomado")
pars.add_argument("--close_tab", type=float, default=5.0, help="Alto pestaña de cierre")
pars.add_argument("--side_tabs", type=float, default=5.0, help="Alto pestañas laterales de cierre")
pars.add_argument("--unit", default="mm", help="Tipo de unidades")
def effect(self):
centro_ancho_documento = self.svg.unittouu(self.document.getroot().get('width'))/2
centro_alto_documento = self.svg.unittouu(self.document.getroot().get('height'))/2
medida_pestana1=5
medida_pestana2=1
medida_pestana3=4
medida_pestana4=3
medida_pestana5=10
ancho_caja = self.svg.unittouu(str(self.options.width) + self.options.unit)
alto_caja = self.svg.unittouu(str(self.options.height) + self.options.unit)
largo_caja = self.svg.unittouu(str(self.options.depth) + self.options.unit)
ancho_pestana_cola = self.svg.unittouu(str(self.options.glue_tab) + self.options.unit)
alto_pestana_cierre = self.svg.unittouu(str(self.options.close_tab) + self.options.unit)
alto_pestana = self.svg.unittouu(str(self.options.side_tabs) + self.options.unit)
if self.options.unit=="cm":
medida_pestana1=0.5
medida_pestana2=0.1
medida_pestana3=0.4
medida_pestana3=0.3
medida_pestana5=1.0
if self.options.unit=="in":
medida_pestana1=0.196
medida_pestana2=0.039
medida_pestana3=0.157
medida_pestana3=0.118
medida_pestana5=0.393
medida1_pestanas_laterales=self.svg.unittouu(str(medida_pestana1) + self.options.unit)
medida2_pestanas_laterales=self.svg.unittouu(str(medida_pestana2) + self.options.unit)
medida3_pestanas_laterales=self.svg.unittouu(str(medida_pestana3) + self.options.unit)
medida4_pestanas_laterales=self.svg.unittouu(str(medida_pestana4) + self.options.unit)
medida1_pestanas_cierre=self.svg.unittouu(str(medida_pestana5) + self.options.unit)
id_caja = self.svg.get_unique_id('estuche-fondosuizo')
group = self.svg.get_current_layer().add(inkex.Group(id=id_caja))
estilo_linea_cortes = {'stroke': '#FF0000', 'fill': 'none', 'stroke-width': str(self.svg.unittouu('1px'))}
estilo_linea_hendidos = {'stroke': '#0000FF', 'fill': 'none', 'stroke-width': str(self.svg.unittouu('1px'))}
estilo_linea_taladros = {'stroke': '#00FF00', 'fill': 'none', 'stroke-width': str(self.svg.unittouu('1px'))}
estilo_linea_medioscortes = {'stroke': '#00FFFF', 'fill': 'none', 'stroke-width': str(self.svg.unittouu('1px'))}
# line.path --> M = coordenadas absolutas
# line.path --> l = dibuja una linea desde el punto actual a las coordenadas especificadas
# line.path --> c = dibuja una curva beizer desde el punto actual a las coordenadas especificadas
# line.path --> q = dibuja un arco desde el punto actual a las coordenadas especificadas usando un punto como referencia
# line.path --> Z = cierra path
#Perfil Exterior de la caja
line = group.add(inkex.PathElement(id=id_caja + '-perfil-exterior'))
line.path = [
['M', [0, 0]],
['l', [ancho_caja, 0]],
['l', [0,0]],
['l', [0, 0]],
['l', [0, 0-medida1_pestanas_laterales]],
['l', [medida2_pestanas_laterales, 0-medida2_pestanas_laterales]],
['l', [medida3_pestanas_laterales, 0-(alto_pestana-medida2_pestanas_laterales-medida1_pestanas_laterales)]],
['l', [(largo_caja-medida2_pestanas_laterales-medida3_pestanas_laterales-medida4_pestanas_laterales), 0]],
['l', [0,alto_pestana-medida4_pestanas_laterales]],
['l', [medida4_pestanas_laterales, medida4_pestanas_laterales]],
['l', [0, 0-largo_caja]],
['l', [0, 0]],
['q', [0,0-alto_pestana_cierre,alto_pestana_cierre, 0-alto_pestana_cierre]],
['l', [ancho_caja-(alto_pestana_cierre*2), 0]],
['q', [alto_pestana_cierre,0,alto_pestana_cierre,alto_pestana_cierre]],
['l', [0, 0]],
['l', [0, (largo_caja)]],
['l', [medida4_pestanas_laterales, 0-medida4_pestanas_laterales]],
['l', [0,0-(alto_pestana-medida4_pestanas_laterales)]],
['l', [(largo_caja-medida2_pestanas_laterales-medida3_pestanas_laterales-medida4_pestanas_laterales), 0]],
['l', [medida3_pestanas_laterales, (alto_pestana-medida2_pestanas_laterales-medida1_pestanas_laterales)]],
['l', [medida2_pestanas_laterales, medida2_pestanas_laterales]],
['l', [0, medida1_pestanas_laterales]],
['l', [0,0]],
['l', [0, alto_caja]],
['l', [0,(largo_caja*0.5)+medida1_pestanas_cierre]],
['l', [0-(largo_caja)*0.5,0]],
['l', [0,0-medida1_pestanas_cierre]],
['l', [0-(largo_caja)*0.5,0-((largo_caja)*0.5)]],
['l', [0,(largo_caja*0.5)+medida1_pestanas_cierre]],
['l', [0-(ancho_caja)*0.25,0]],
['l', [0, 0-medida1_pestanas_cierre]],
['l', [0-(ancho_caja)*0.5,0]],
['l', [0, medida1_pestanas_cierre]],
['l', [0-(ancho_caja)*0.25,0]],
['l', [0,0-((largo_caja*0.5)+medida1_pestanas_cierre)]],
['l', [0-(largo_caja)*0.5,((largo_caja*0.5))]],
['l', [0,medida1_pestanas_cierre]],
['l', [0-(largo_caja)*0.5,0]],
['l', [0,0-((largo_caja*0.5)+medida1_pestanas_cierre)]],
['l', [0-ancho_caja*0.25,(largo_caja*0.5)]],
['l', [0,medida1_pestanas_cierre]],
['l', [0-ancho_caja*0.5,0]],
['l', [0,0-medida1_pestanas_cierre]],
['l', [0-ancho_caja*0.25,0-(largo_caja*0.5)]],
['l', [0, 0]],
['l', [0, 0]],
['l', [0, 0-medida2_pestanas_laterales]],
['l', [0-ancho_pestana_cola, 0-(ancho_pestana_cola/2)]],
['l', [0, 0-(alto_caja-ancho_pestana_cola-(medida2_pestanas_laterales*2))]],
['l', [ancho_pestana_cola, 0-(ancho_pestana_cola/2)]],
['Z', []]
]
line.style = estilo_linea_cortes
#Hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-1'))
line.path = [
['M', [0,0]],
['l', [0,alto_caja]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-2'))
line.path = [
['M', [ancho_caja,0]],
['l', [0,alto_caja]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-3'))
line.path = [
['M', [ancho_caja+largo_caja,0]],
['l', [0,alto_caja]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-4'))
line.path = [
['M', [ancho_caja+ancho_caja+largo_caja,0]],
['l', [0,alto_caja]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-5'))
line.path = [
['M', [ancho_caja,0]],
['l', [largo_caja,0]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-6'))
line.path = [
['M', [largo_caja+ancho_caja,0]],
['l', [ancho_caja,0]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-7'))
line.path = [
['M', [(ancho_caja*2)+largo_caja,0]],
['l', [largo_caja,0]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-8'))
line.path = [
['M', [ancho_caja+largo_caja,0-largo_caja]],
['l', [ancho_caja,0]]
]
line.style = estilo_linea_hendidos
line = group.add(inkex.PathElement(id=id_caja + '-perfil-hendidos-9'))
line.path = [
['M', [0,alto_caja]],
['l', [(ancho_caja+largo_caja)*2,0]]
]
line.style = estilo_linea_hendidos
if __name__ == '__main__':
SwissBottomCase().run()

View File

@ -0,0 +1,232 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
sodipodi:docname="FondoSuizoLayout.svg"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
id="svg8"
version="1.1"
viewBox="0 0 94.000001 64.000001"
height="64mm"
width="94mm">
<defs
id="defs2">
<rect
x="-76.706352"
y="209.06828"
width="23.786987"
height="31.003265"
id="rect889" />
<rect
id="rect889-5"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect902"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect889-2"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect902-7"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect889-53"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect902-3"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect889-6"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect902-4"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
x="-76.706352"
y="209.06828"
width="23.786987"
height="31.003265"
id="rect889-2-4" />
<rect
x="-76.706352"
y="209.06828"
width="23.786987"
height="31.003265"
id="rect979" />
<rect
id="rect889-53-2"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect2158"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect889-6-3"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
<rect
id="rect2161"
height="31.003265"
width="23.786987"
y="209.06828"
x="-76.706352" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.4"
inkscape:cx="49.930469"
inkscape:cy="118.23988"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
inkscape:window-width="1920"
inkscape:window-height="1018"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
height="211mm" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Capa 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-232.99994)">
<text
xml:space="preserve"
id="text887"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect889);fill:#000000;fill-opacity:1;stroke:none;"
transform="matrix(0.41705182,0,0,0.41705182,43.528428,194.28861)"><tspan
x="-68.892474"
y="219.23689"><tspan
style="font-size:11.2889px">A</tspan></tspan></text>
<text
transform="matrix(0.41705182,0,0,0.41705182,66.364823,194.408)"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect889-5);fill:#000000;fill-opacity:1;stroke:none;"
id="text887-5"
xml:space="preserve"><tspan
x="-68.264088"
y="219.23689"><tspan
style="font-size:11.2889px">L</tspan></tspan></text>
<text
transform="matrix(0.41705182,0,0,0.41705182,78.375063,181.34478)"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect889-2);fill:#000000;fill-opacity:1;stroke:none;"
id="text887-2"
xml:space="preserve"><tspan
x="-68.892474"
y="219.23689"><tspan
style="font-size:11.2889px">H</tspan></tspan></text>
<text
xml:space="preserve"
id="text887-2-3"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect889-2-4);fill:#000000;fill-opacity:1;stroke:none;"
transform="matrix(0.30011567,0,0,0.30011567,23.016641,217.4035)"><tspan
x="-68.583793"
y="219.23689"><tspan
style="font-size:11.2889px">P</tspan></tspan></text>
<text
transform="matrix(0.41705181,0,0,0.41705181,104.8736,154.35938)"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect889-53-2);fill:#000000;fill-opacity:1;stroke:none;"
id="text887-56"
xml:space="preserve"><tspan
x="-68.264088"
y="219.23689"><tspan
style="font-size:11.2889px">T</tspan></tspan></text>
<path
id="estuche-fondosuizo2763-perfil-exterior"
d="M 5.5892335,251.95808 H 31.266128 v -1.14916 l 0.256769,-0.22983 0.770307,-12.41096 h 16.176444 v 13.10045 l 0.770307,0.6895 v -16.08827 q 0,-1.72375 1.925767,-1.72375 h 21.825361 q 1.925767,0 1.925767,1.72375 v 16.08827 l 0.770307,-0.6895 v -13.10045 h 16.176444 l 0.770307,12.41096 0.256768,0.22983 v 1.14916 34.47487 8.04414 h -8.986913 v -2.29833 l -8.986913,-5.74581 v 10.13561 h -6.419224 v -2.29832 H 55.659179 v 2.29832 h -6.419224 v -10.13561 l -8.986913,5.74581 v 2.29833 h -8.986914 v -8.04414 l -6.419223,8.04414 v 2.29832 H 12.008457 v -2.29832 l -6.4192235,-8.04414 -3.8515343,-3.44749 v -29.41855 z"
style="fill:none;stroke:#ff0000;stroke-width:0.0642745" />
<path
id="estuche-fondosuizo2763-perfil-hendidos-1"
d="m 5.5892335,251.95808 v 34.47487 z"
style="fill:none;stroke:#0000ff;stroke-width:0.0642745" />
<path
id="estuche-fondosuizo2763-perfil-hendidos-2"
d="m 31.266128,251.95808 v 34.47487 z"
style="fill:none;stroke:#0000ff;stroke-width:0.0642745" />
<path
id="estuche-fondosuizo2763-perfil-hendidos-3"
d="m 49.239955,251.95808 v 34.47487 z"
style="fill:none;stroke:#0000ff;stroke-width:0.0642745" />
<path
id="estuche-fondosuizo2763-perfil-hendidos-4"
d="m 74.91685,251.95808 v 34.47487 z"
style="fill:none;stroke:#0000ff;stroke-width:0.0642745" />
<path
id="estuche-fondosuizo2763-perfil-hendidos-5"
d="M 31.266128,251.95808 H 92.890676 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.0642745" />
<path
id="estuche-fondosuizo2763-perfil-hendidos-6"
d="M 5.5892335,286.43295 H 92.890676 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.0642745" />
<path
id="estuche-fondosuizo2763-perfil-hendidos-7"
d="M 74.91685,286.43295 H 92.890676 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.0642745" />
<path
id="estuche-fondosuizo2763-perfil-hendidos-8"
d="M 49.239955,236.92815 H 74.91685 Z"
style="fill:none;stroke:#0000ff;stroke-width:0.0642745" />
<text
transform="matrix(0.41705181,0,0,0.41705181,89.096941,146.87361)"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:center;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect889-6-3);fill:#000000;fill-opacity:1;stroke:none;"
id="text887-9"
xml:space="preserve"><tspan
x="-67.10791"
y="214.78815"><tspan
style="font-size:6.35px">C</tspan></tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Fibonacci Pattern</name>
<id>fablabchemnitz.de.fibonacci_pattern</id>
<param name="FirstRadius" type="int" min="1" max="500" gui-text="First Radius:">2</param>
<param name="LastRadius" type="int" min="1" max="500" gui-text="Last Radius:">5</param>
<param name="NumberOfNodes" type="int" min="200" max="2000" gui-text="Number of Nodes:">400</param>
<param name="SpreadFactor" type="int" min="1" max="100" gui-text="Spread Factor:">20</param>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu name="FabLab Chemnitz">
<submenu name="Shape/Pattern from Generator"/>
</submenu>
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">fibonacci_pattern.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,86 @@
#!/usr/bin/env python3
'''
Copyright (C) 2015-2015 Carlos Mostek carlosmostek@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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
'''
import math
import inkex
from lxml import etree
# This is basically the draw method from the help guides for inkscape
def draw_SVG_ellipse(rx, ry, cx, cy, parent, start_end=(0,2*math.pi),transform='' ):
style = { 'stroke' : '#000000',
'stroke-width' : '1',
'fill' : 'none' }
ell_attribs = {'style':str(inkex.Style(style)),
inkex.addNS('cx','sodipodi') :str(cx),
inkex.addNS('cy','sodipodi') :str(cy),
inkex.addNS('rx','sodipodi') :str(rx),
inkex.addNS('ry','sodipodi') :str(ry),
inkex.addNS('start','sodipodi') :str(start_end[0]),
inkex.addNS('end','sodipodi') :str(start_end[1]),
inkex.addNS('open','sodipodi') :'true', #all ellipse sectors we will draw are open
inkex.addNS('type','sodipodi') :'arc',
'transform' :transform
}
ell = etree.SubElement(parent, inkex.addNS('path','svg'), ell_attribs )
# This is the workhorse, it draws the circle based on which node number
def drawKthCircle(k,firstRadius,lastRadius,numNodes,spreadFactor,parent):
# Use golden circle phi
phi = (math.sqrt(5) - 1)/2
# Calculate the node radius
growth = lastRadius - firstRadius
nodeRadius = firstRadius + growth*float(k - 1)/float(numNodes)
# Calculate X and Y from theta = 2 pi phi k and radius = sqrt(k)
r = spreadFactor * math.sqrt(k)
theta = 2*math.pi*phi*k
# use simple trig to get cx and cy
x = r * math.cos(theta)
y = r * math.sin(theta)
# Add the px to the size
nodeRadiusTxt = "%spx"%nodeRadius
# Draw the node
draw_SVG_ellipse(nodeRadiusTxt,nodeRadiusTxt,x,y,parent)
class FibonacciPattern(inkex.EffectExtension):
def add_arguments(self, pars):
pars.add_argument("-f", "--FirstRadius", type=int, default="5", help="The radius of the first layer of circles in pixels.")
pars.add_argument("-l", "--LastRadius", type=int, default="10", help="The radius of the last layer of circles in pixels.")
pars.add_argument("-n", "--NumberOfNodes", type=int, default="5", help="The number of layers in the fibonacci spiral")
pars.add_argument("-s", "--SpreadFactor",type=int, default="10", help="This will create a larger spread between the nodes from the center.")
def effect(self):
# Foreach Node
for k in range(1,self.options.NumberOfNodes):
# Draw the circle
drawKthCircle(k,
self.options.FirstRadius,
self.options.LastRadius,
self.options.NumberOfNodes,
self.options.SpreadFactor,
self.svg.get_current_layer())
if __name__ == '__main__':
FibonacciPattern().run()

View File

@ -0,0 +1,21 @@
[
{
"name": "Fibonacci Pattern",
"id": "fablabchemnitz.de.fibonacci_pattern",
"path": "fibonacci_pattern",
"dependent_extensions": null,
"original_name": "Beauty Full Day Fibonacci Pattern",
"original_id": "com.beautyfullday.inkscape.fibonaccipattern",
"license": "GNU GPL v2",
"license_url": "https://github.com/mostekcm/beautyfullday_fibonaccipattern/blob/master/render_fibonaccipattern.py",
"comment": "ported to Inkscape v1 by Mario Voigt",
"source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/fibonacci_pattern",
"fork_url": "https://github.com/mostekcm/beautyfullday_fibonaccipattern",
"documentation_url": "https://stadtfabrikanten.org/display/IFM/Fibonacci+Pattern",
"inkscape_gallery_url": null,
"main_authors": [
"github.com/mostekcm",
"github.com/eridur-de"
]
}
]

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Flash Burst</name>
<id>fablabchemnitz.de.flash_burst</id>
<param name="r1" type="int" min="1" max="20" gui-text="Radius - inner (min)">10</param>
<param name="r2" type="int" min="1" max="20" gui-text="Radius - inner (max)">10</param>
<separator />
<param name="R1" type="int" min="200" max="500" gui-text="Radius - outer (min)">350</param>
<param name="R2" type="int" min="200" max="500" gui-text="Radius - outer (max)">350</param>
<separator />
<param name="a1" type="int" min="1" max="45" gui-text="Angle (min)">5</param>
<param name="a2" type="int" min="1" max="45" gui-text="Angle (max)">10</param>
<separator />
<param name="ad1" type="int" min="1" max="10" gui-text="Angle delta (min)">3</param>
<param name="ad2" type="int" min="1" max="10" gui-text="Angle delta (max)">5</param>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu name="FabLab Chemnitz Shape Generators">
<submenu name="Streaks And Blobs" />
</submenu>
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">flash_burst.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,105 @@
#!/usr/bin/env python3
#
# Copyright (C) 2021 Stefan Benediktsson
#
# GNU Affero General Public License v3.0
# Permissions of this strongest copyleft license are
# conditioned on making available complete source code
# of licensed works and modifications, which include
# larger works using a licensed work, under the same
# license. Copyright and license notices must be preserved.
# Contributors provide an express grant of patent rights.
# When a modified version is used to provide a service
# over a network, the complete source code of the modified
# version must be made available.
"""
Generate a FlashBurst as SVG.
"""
import random
from math import acos, cos, radians, sin, sqrt, tan
import inkex
def points_to_svgd(p):
"""
p: list of 2 tuples (x, y coordinates)
"""
f = p[0]
p = p[1:]
svgd = 'M{:.3f},{:.3f}'.format(f[0], f[1])
for x in p:
svgd += 'L{:.3f},{:.3f}'.format(x[0], x[1])
svgd +='Z'
return svgd
def checkSize(a, b):
if a >= b:
return b, a
else:
return a, b
class FlashBurst(inkex.GenerateExtension):
container_label = 'Rendered FlashBurst'
def add_arguments(self, pars):
pars.add_argument("--r1", type=int, default=20, help="Inner Radius min")
pars.add_argument("--r2", type=int, default=20, help="Inner Radius max")
pars.add_argument("--R1", type=int, default=350, help="Outer Radius min")
pars.add_argument("--R2", type=int, default=350, help="Outer Radius min")
pars.add_argument("--a1", type=int, default=5, help="Angle min")
pars.add_argument("--a2", type=int, default=7, help="Angle max")
pars.add_argument("--ad1", type=int, default=3, help="Angle delta min")
pars.add_argument("--ad2", type=int, default=2, help="Angle delta max")
def generate(self):
r1 = self.options.r1
r2 = self.options.r2
R1 = self.options.R1
R2 = self.options.R2
a1 = self.options.a1
a2 = self.options.a2
ad1 = self.options.ad1
ad2 = self.options.ad2
# generate points: list of (x, y) pairs
r1, r2 = checkSize(r1, r2)
R1, R2 = checkSize(R1, R2)
a1, a2 = checkSize(a1, a2)
ad1, ad2 = checkSize(ad1, ad2)
a = 0
oX = 0
oY = 0
style = {'stroke': '#000000', 'fill': '#000000', 'stroke-width': str(self.svg.unittouu('1px'))}
while a < 360:
a = a+random.randint(a1,a2)
dI = random.randint(r1,r2)
dO = random.randint(R1,R2)
ad = random.randint(ad1,ad2)
x0 = cos(radians(a)) * dI
y0 = sin(radians(a)) * dI
x10 = cos(radians(a-ad/2)) * dO
y10 = sin(radians(a-ad/2)) * dO
x11 = cos(radians(a+ad/2)) * dO
y11 = sin(radians(a+ad/2)) * dO
points = []
points.append((x0, y0))
points.append((x10,y10))
points.append((x11,y11))
path = points_to_svgd(points)
yield inkex.PathElement(style=str(inkex.Style(style)), d=str(path))
if __name__ == '__main__':
FlashBurst().run()

View File

@ -0,0 +1,21 @@
[
{
"name": "Flash Burst",
"id": "fablabchemnitz.de.flash_burst",
"path": "flash_burst",
"dependent_extensions": null,
"original_name": "FlashBurst",
"original_id": "hipix.FlashBurst",
"license": "GNU AGPL v3",
"license_url": "https://github.com/curiousmaster/hipix_inkscape_extensions/blob/main/LICENSE",
"comment": "",
"source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/flash_burst",
"fork_url": "https://github.com/curiousmaster/hipix_inkscape_extensions",
"documentation_url": "https://stadtfabrikanten.org/display/IFM/Flash+Burst",
"inkscape_gallery_url": null,
"main_authors": [
"github.com/curiousmaster",
"github.com/eridur-de"
]
}
]

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Flevobézier</name>
<id>fablabchemnitz.de.flevobezier</id>
<effect>
<object-type>path</object-type>
<effects-menu>
<submenu name="FabLab Chemnitz">
<submenu name="Modify existing Path(s)"/>
</submenu>
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">flevobezier.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,241 @@
#!/usr/bin/env python3
# Flevobezier: an Inkscape extension fitting Bezier curves
# Parcly Taxel / Jeremy Tan, 2019
# https://gitlab.com/parclytaxel
from __future__ import division
from math import *
from inkex.bezier import bezierpointatt
import inkex
import gettext
from inkex.paths import Path
import sys
def pout(t): sys.exit((gettext.gettext(t)))
class FlevoBezier(inkex.EffectExtension):
def effect(self):
if len(self.svg.selected) == 0: pout("Please select at least one path.")
for obj in self.svg.selected: # The objects are the paths, which may be compound
curr = self.svg.selected[obj]
raw = Path(curr.get("d")).to_arrays()
subpaths, prev = [], 0
for i in range(len(raw)): # Breaks compound paths into simple paths
if raw[i][0] == 'M' and i != 0:
subpaths.append(raw[prev:i])
prev = i
subpaths.append(raw[prev:])
output = ""
for simpath in subpaths:
closed = False
if simpath[-1][0] == 'Z':
closed = True
if simpath[-2][0] == 'L': simpath[-1][1] = simpath[0][1]
else: simpath.pop()
#nodes = [node(simpath[i][1][-2:]) for i in range(len(simpath))]
nodes = []
for i in range(len(simpath)):
if simpath[i][0] == 'V': # vertical and horizontal lines only have one point in args, but 2 are required
#inkex.utils.debug(simpath[i][0])
simpath[i][0]='L' #overwrite V with regular L command
add=simpath[i-1][1][0] #read the X value from previous segment
simpath[i][1].append(simpath[i][1][0]) #add the second (missing) argument by taking argument from previous segment
simpath[i][1][0]=add #replace with recent X after Y was appended
if simpath[i][0] == 'H': # vertical and horizontal lines only have one point in args, but 2 are required
#inkex.utils.debug(simpath[i][0])
simpath[i][0]='L' #overwrite H with regular L command
simpath[i][1].append(simpath[i-1][1][1]) #add the second (missing) argument by taking argument from previous segment
#inkex.utils.debug(simpath[i])
nodes.append(node(simpath[i][1][-2:]))
output += flevobezier(nodes, closed)
curr.set("d", output)
# The main algorithm! Yay!
def flevobezier(points, z):
if len(points) < 2: pout("A curve isn't a point, silly!")
res = []
prevtrail, trail, lead, window = 0, 0, 1, points[:2] # Start with first two points
maybeover = False # Over by error followed by over by angle -> backup
curcurve = [window[0], slide(window[0], window[1], 1 / 3), slide(window[0], window[1], 2 / 3), window[1]] # Current working curve, always a 4-list
while lead + 1 < len(points):
lead += 1
window = points[trail:lead + 1] # Extend the window one more node
v = window[-3] - window[-2]
w = window[-1] - window[-2]
try:
dist(v) / dist(w)
except ZeroDivisionError as e:
pout("Division by zero. Check if your path contains duplicate handles.")
if dotp(v, w) / dist(v) / dist(w) >= 0.5: # 60 degrees or less, over by angle
if maybeover: # backup
newcurve = stress(points[prevtrail:lead])[0]
res[-3:] = newcurve[1:] # replace the last three nodes in res with those of newcurve
trail = lead - 1
maybeover = False
else:
if not res: res += curcurve[:1]
res += curcurve[1:]
prevtrail = trail
trail = lead - 1
window = points[trail:lead + 1]
curcurve = [window[0], slide(window[0], window[1], 1 / 3), slide(window[0], window[1], 2 / 3), window[1]]
else: # then see what to do based on how long the window is
over = False
if len(window) == 3: # Quadratic curve on three nodes stepped to a cubic
t = chords(window)[1]
qcurve = [window[0], (window[1] - (1 - t) * (1 - t) * window[0] - t * t * window[2]) / (2 * t * (1 - t)), window[2]]
newcurve = [qcurve[0], slide(qcurve[0], qcurve[1], 2 / 3), slide(qcurve[1], qcurve[2], 1 / 3), qcurve[2]]
elif len(window) == 4: # Cubic curve on four nodes
newcurve = cubicfrom4(window)
else: # Stress
product = stress(window)
shortseg = min([dist(window[i], window[i + 1]) for i in range(len(window) - 1)])
# Stop condition: maximum error > 1 / 3 * minimum segment length
if max(product[1]) > 0.33 * shortseg: over = True
else: newcurve = product[0]
if over: # Over by error bound
maybeover = True
if not res: res += curcurve[:1]
res += curcurve[1:]
prevtrail = trail
trail = lead - 1
window = points[trail:lead + 1]
curcurve = [window[0], slide(window[0], window[1], 1 / 3), slide(window[0], window[1], 2 / 3), window[1]]
else: curcurve, maybeover = newcurve, False
if maybeover: # When it has reached the end...
newcurve = stress(points[prevtrail:lead + 1])[0]
res[-3:] = newcurve[1:]
else:
if not res: res += curcurve[:1]
res += curcurve[1:] # If it has reached the end, accept curcurve
# Smoothing
ouro = res.pop() # Removes the final (redundant) node of closed paths. In the end, does not affect open paths.
for t in range(0, len(res), 3):
if t != 0 or z: # If not at beginning or if path is closed
v = res[t - 1] - res[t] # Previous handle
w = res[t + 1] - res[t] # Next handle
try:
angle = dotp(v, w) / dist(v) / dist(w)
if angle <= -0.94: # ~ cos(160 degrees)
# Rotate opposing handles and make a straight line.
theta = (pi - acos(angle)) / 2 # Angle to rotate by
sign = 1 if (dirc(v) > dirc(w)) ^ (abs(dirc(v) - dirc(w)) >= pi) else -1 # Direction to rotate (WTF?)
res[t - 1] = res[t] + spin(v, sign * theta)
res[t + 1] = res[t] + spin(w, -sign * theta)
except ZeroDivisionError:
pout("Path has only one point left. Cannot continue")
res.append(ouro)
# Formatting and final output
out = "M " + str(res[0])
for c in range(1, len(res), 3): out += " ".join([" C", str(res[c]), str(res[c + 1]), str(res[c + 2])])
if z: out += " Z "
return out
'''Helper functions and classes below'''
# Node object as a helper to simplify code. Calling it point would be SO cliched.
class node:
def __init__(self, x = None, y = None):
if y != None: self.x, self.y = float(x), float(y)
elif type(x) == list or type(x) == tuple: self.x, self.y = float(x[0]), float(x[1])
else: self.x, self.y = 0.0, 0.0
def __str__(self): return str(self.x) + " " + str(self.y)
def __repr__(self): return str(self)
def __add__(self, pode): return node(self.x + pode.x, self.y + pode.y) # Vector addition
def __sub__(self, pode): return node(self.x - pode.x, self.y - pode.y) # and subtraction
def __neg__(self): return node(-self.x, -self.y)
def __mul__(self, scal): # Multiplication by a scalar
if type(scal) == int or type(scal) == float: return node(self.x * scal, self.y * scal)
else: return node(self.x * scal.x - self.y * scal.y, self.y * scal.x + self.x * scal.y) # Fallback does complex multiplication
def __rmul__(self, scal): return self * scal
def __truediv__(self, scal): # Division by a scalar
if type(scal) == int or type(scal) == float: return node(self.x / scal, self.y / scal)
else:
n = scal.x * scal.x + scal.y * scal.y
return node(self.x * scal.x + self.y * scal.y, self.y * scal.x - self.x * scal.y) / n # Fallback does complex division
# Operations on nodes
def dist(n0, n1 = None): return hypot(n1.y - n0.y, n1.x - n0.x) if n1 else hypot(n0.y, n0.x) # For these two functions
def dirc(n0, n1 = None): return atan2(n1.y - n0.y, n1.x - n0.x) if n1 else atan2(n0.y, n0.x) # n0 is the origin if n1 is present
def slide(n0, n1, t): return n0 + t * (n1 - n0)
def dotp(n0, n1): return n0.x * n1.x + n0.y * n1.y
# Operation on vectors: rotation. Positive theta means counterclockwise rotation.
def spin(v, theta): return node(v.x * cos(theta) - v.y * sin(theta), v.x * sin(theta) + v.y * cos(theta))
# Wrapper function for node curves to mesh with bezierpointatt
def curveat(curve, t): return node(bezierpointatt(((node.x, node.y) for node in curve), t))
# This function takes in a list of nodes and returns
# a list of numbers between 0 and 1 corresponding to the relative positions
# of said nodes (assuming consecutive nodes are linked by straight lines).
# The first item is always 0.0 and the last one 1.0.
def chords(nodes):
lengths = [dist(nodes[i + 1], nodes[i]) for i in range(len(nodes) - 1)]
ratios = [0.0] + [sum(lengths[:i + 1]) / sum(lengths) for i in range(len(lengths))]
ratios[-1] = 1.0 # Just in case...
return ratios
# Takes a list of four nodes and generates a curve passing through all based on chords().
# If lm and mu (the two params for the middle nodes) are not given they are calculated.
def cubicfrom4(nodes, p = None, q = None):
if p == None or q == None:
store = chords(nodes)
lm, mu = store[1], store[2] # First one is short for lambda
else: lm, mu = p, q
a = 3 * (1 - lm) * (1 - lm) * lm
b = 3 * (1 - lm) * lm * lm
c = 3 * (1 - mu) * (1 - mu) * mu
d = 3 * (1 - mu) * mu * mu
x = nodes[1] - (1 - lm) ** 3 * nodes[0] - lm ** 3 * nodes[3]
y = nodes[2] - (1 - mu) ** 3 * nodes[0] - mu ** 3 * nodes[3]
det = a * d - b * c
if not det: pout("Singular matrix!")
l, m = (d * x - b * y) / det, (a * y - c * x) / det
return [nodes[0], l, m, nodes[3]]
# Stress theory: takes a list of five or more nodes and stresses a curve to fit
def stress(string):
# Make an initial guess considering the end nodes together with the 2nd/2nd last, 3rd/3rd last, ... nodes.
# This is much faster than considering all sets of two interior nodes.
callipers, seeds = chords(string), []
middle = len(string) // 2
for i in range(1, middle):
seeds.append(cubicfrom4([string[0], string[i], string[-i - 1], string[-1]], callipers[i], callipers[-i - 1]))
a, b = node(), node()
for i in range(len(seeds)):
a += seeds[i][1]
b += seeds[i][2]
curve = [string[0], a / len(seeds), b / len(seeds), string[-1]]
# Refine by projection and handle shifting
for i in range(5):
for j in range(middle - 1, 0, -1):
delta1, delta2 = project(curve, string[j]), project(curve, string[-j - 1])
curve[1] += 2.5 * delta1
curve[2] += 2.5 * delta2
errors = [dist(project(curve, k)) for k in string]
return curve, errors
# Projection of node onto cubic curve based on public domain code by Mike "Pomax" Kamermans
# https://pomax.github.io/bezierinfo/#projections
def project(curve, node):
samples = 200
lookup = [dist(curveat(curve, i / samples), node) for i in range(samples + 1)]
mindist = min(lookup)
t = lookup.index(mindist) / samples
width = 1 / samples # Width of search interval
while width > 1.0e-5:
left = dist(curveat(curve, max(t - width, 0)), node)
right = dist(curveat(curve, min(t + width, 1)), node)
if t == 0.0: left = mindist + 1
if t == 1.0: right = mindist + 1
if left < mindist or right < mindist:
mindist = min(left, right)
t = max(t - width, 0.0) if left < right else min(t + width, 1.0)
else: width /= 2
projection = curveat(curve, t)
return node - projection
if __name__ == '__main__':
FlevoBezier().run()

View File

@ -0,0 +1,21 @@
[
{
"name": "Flevobézier",
"id": "fablabchemnitz.de.flevobezier",
"path": "flevobezier",
"dependent_extensions": null,
"original_name": "Flevobézier",
"original_id": "org.parclytaxel.inkscape.flevobezier",
"license": "MIT License",
"license_url": "https://gitlab.com/parclytaxel/Kinross/-/blob/master/licence",
"comment": "",
"source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/flevobezier",
"fork_url": "https://gitlab.com/parclytaxel/Kinross",
"documentation_url": "https://stadtfabrikanten.org/display/IFM/Flevobezier",
"inkscape_gallery_url": null,
"main_authors": [
"gitlab.com/parclytaxel",
"github.com/eridur-de"
]
}
]

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Flip</name>
<id>fablabchemnitz.de.flip</id>
<effect>
<object-type>path</object-type>
<effects-menu>
<submenu name="FabLab Chemnitz">
<submenu name="Transformations"/>
</submenu>
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">flip.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,38 @@
import math
from inkex import EffectExtension, PathElement, transforms as T
# https://en.wikipedia.org/wiki/Rotations_and_reflections_in_two_dimensions
def reflection_matrix(theta):
theta2 = 2 * theta
return [
[math.cos(theta2), math.sin(theta2), 0],
[math.sin(theta2), -math.cos(theta2), 0],
]
def svg_matrix_order(mat):
((a, c, e), (b, d, f)) = mat
return a, b, c, d, e, f
class Flip(EffectExtension):
"""Extension to flip a path about the line from the start to end node"""
def effect(self):
for node in self.svg.selection.filter(PathElement).values():
points = list(node.path.end_points)
if len(points) < 2 or points[0] == points[-1]:
continue
start = points[0]
end = points[-1]
v = end - start
theta = math.atan2(v.y, v.x)
# transforms go in reverse order
mat = T.Transform()
mat.add_translate(start)
mat.add_matrix(*svg_matrix_order(reflection_matrix(theta)))
mat.add_translate(-start)
node.path = node.path.transform(mat)
if __name__ == '__main__':
Flip().run()

Some files were not shown because too many files have changed in this diff Show More