commit abe89898125501f5d9b51fb3485cca6d97adf901 Author: Mario Voigt Date: Thu Sep 1 10:55:46 2022 +0200 Initial commit. First part of extensions. More are coming back again soon. diff --git a/extensions/fablabchemnitz/000_about_fablabchemnitz.svg b/extensions/fablabchemnitz/000_about_fablabchemnitz.svg new file mode 100644 index 0000000..df967f8 --- /dev/null +++ b/extensions/fablabchemnitz/000_about_fablabchemnitz.svg @@ -0,0 +1,110 @@ + + + + + + + diff --git a/extensions/fablabchemnitz/000_update-json.sh b/extensions/fablabchemnitz/000_update-json.sh new file mode 100755 index 0000000..1a524f7 --- /dev/null +++ b/extensions/fablabchemnitz/000_update-json.sh @@ -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 diff --git a/extensions/fablabchemnitz/000_validate.sh b/extensions/fablabchemnitz/000_validate.sh new file mode 100755 index 0000000..f689cae --- /dev/null +++ b/extensions/fablabchemnitz/000_validate.sh @@ -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 diff --git a/extensions/fablabchemnitz/affine_spirals/affine_spirals.inx b/extensions/fablabchemnitz/affine_spirals/affine_spirals.inx new file mode 100644 index 0000000..83251f2 --- /dev/null +++ b/extensions/fablabchemnitz/affine_spirals/affine_spirals.inx @@ -0,0 +1,24 @@ + + + Affine Spirals + fablabchemnitz.de.affine_spirals + + + 3 + 2 + 0.8 + true + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/affine_spirals/affine_spirals.py b/extensions/fablabchemnitz/affine_spirals/affine_spirals.py new file mode 100644 index 0000000..710b87a --- /dev/null +++ b/extensions/fablabchemnitz/affine_spirals/affine_spirals.py @@ -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() \ No newline at end of file diff --git a/extensions/fablabchemnitz/affine_spirals/meta.json b/extensions/fablabchemnitz/affine_spirals/meta.json new file mode 100644 index 0000000..bbd39d9 --- /dev/null +++ b/extensions/fablabchemnitz/affine_spirals/meta.json @@ -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" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/apollonian_gasket/__pycache__/apollon.cpython-310.pyc b/extensions/fablabchemnitz/apollonian_gasket/__pycache__/apollon.cpython-310.pyc new file mode 100644 index 0000000..310f3e7 Binary files /dev/null and b/extensions/fablabchemnitz/apollonian_gasket/__pycache__/apollon.cpython-310.pyc differ diff --git a/extensions/fablabchemnitz/apollonian_gasket/__pycache__/apolloniangasket_func.cpython-310.pyc b/extensions/fablabchemnitz/apollonian_gasket/__pycache__/apolloniangasket_func.cpython-310.pyc new file mode 100644 index 0000000..a6d0fba Binary files /dev/null and b/extensions/fablabchemnitz/apollonian_gasket/__pycache__/apolloniangasket_func.cpython-310.pyc differ diff --git a/extensions/fablabchemnitz/apollonian_gasket/apollon.py b/extensions/fablabchemnitz/apollonian_gasket/apollon.py new file mode 100644 index 0000000..565485a --- /dev/null +++ b/extensions/fablabchemnitz/apollonian_gasket/apollon.py @@ -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 . + +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) + diff --git a/extensions/fablabchemnitz/apollonian_gasket/apollonian_gasket.inx b/extensions/fablabchemnitz/apollonian_gasket/apollonian_gasket.inx new file mode 100644 index 0000000..fd51415 --- /dev/null +++ b/extensions/fablabchemnitz/apollonian_gasket/apollonian_gasket.inx @@ -0,0 +1,34 @@ + + + Apollonian Gasket + fablabchemnitz.de.apollonian_gasket + + + 3 + 2.0 + 3.0 + 3.0 + true + true + + + + + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/apollonian_gasket/apollonian_gasket.py b/extensions/fablabchemnitz/apollonian_gasket/apollonian_gasket.py new file mode 100644 index 0000000..c859d2d --- /dev/null +++ b/extensions/fablabchemnitz/apollonian_gasket/apollonian_gasket.py @@ -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() \ No newline at end of file diff --git a/extensions/fablabchemnitz/apollonian_gasket/apolloniangasket_func.py b/extensions/fablabchemnitz/apollonian_gasket/apolloniangasket_func.py new file mode 100644 index 0000000..64315b6 --- /dev/null +++ b/extensions/fablabchemnitz/apollonian_gasket/apolloniangasket_func.py @@ -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 . + +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('\n' % (width, width, corner.real, corner.imag, vbwidth, vbwidth)) + + # Keep stroke width relative + svg.append('\n' % lw) + + # Iterate through circle list, circles with radius radmin: + fill = colors.color_for(abs(c.r)) + svg.append(( '\n' % (c.m.real, c.m.imag, abs(c.r), fill))) + + svg.append('\n') + svg.append('\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 \ No newline at end of file diff --git a/extensions/fablabchemnitz/apollonian_gasket/meta.json b/extensions/fablabchemnitz/apollonian_gasket/meta.json new file mode 100644 index 0000000..c5f25a2 --- /dev/null +++ b/extensions/fablabchemnitz/apollonian_gasket/meta.json @@ -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" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/archimedes_spiral/archimedes_spiral.inx b/extensions/fablabchemnitz/archimedes_spiral/archimedes_spiral.inx new file mode 100644 index 0000000..5c9a594 --- /dev/null +++ b/extensions/fablabchemnitz/archimedes_spiral/archimedes_spiral.inx @@ -0,0 +1,55 @@ + + + Archimedes Spiral + fablabchemnitz.de.archimedes_spiral + + + + 50 + 3 + 50 + + + + + 0 + 5 + + + + + + + + + + + + + + + + + + + + + + + + + ../000_about_fablabchemnitz.svg + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/archimedes_spiral/archimedes_spiral.py b/extensions/fablabchemnitz/archimedes_spiral/archimedes_spiral.py new file mode 100644 index 0000000..5c55d24 --- /dev/null +++ b/extensions/fablabchemnitz/archimedes_spiral/archimedes_spiral.py @@ -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() \ No newline at end of file diff --git a/extensions/fablabchemnitz/archimedes_spiral/meta.json b/extensions/fablabchemnitz/archimedes_spiral/meta.json new file mode 100644 index 0000000..edccd20 --- /dev/null +++ b/extensions/fablabchemnitz/archimedes_spiral/meta.json @@ -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" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/barrel_distorsion/barrel_distorsion.inx b/extensions/fablabchemnitz/barrel_distorsion/barrel_distorsion.inx new file mode 100644 index 0000000..56b87d2 --- /dev/null +++ b/extensions/fablabchemnitz/barrel_distorsion/barrel_distorsion.inx @@ -0,0 +1,17 @@ + + + Barrel Distortion + fablabchemnitz.de.barrel_distorsion + -1.0 + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/barrel_distorsion/barrel_distorsion.py b/extensions/fablabchemnitz/barrel_distorsion/barrel_distorsion.py new file mode 100644 index 0000000..4b2bcbb --- /dev/null +++ b/extensions/fablabchemnitz/barrel_distorsion/barrel_distorsion.py @@ -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() \ No newline at end of file diff --git a/extensions/fablabchemnitz/barrel_distorsion/meta.json b/extensions/fablabchemnitz/barrel_distorsion/meta.json new file mode 100644 index 0000000..7b2f46f --- /dev/null +++ b/extensions/fablabchemnitz/barrel_distorsion/meta.json @@ -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" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/bezier_envelope/bezier_envelope.inx b/extensions/fablabchemnitz/bezier_envelope/bezier_envelope.inx new file mode 100644 index 0000000..068cdaf --- /dev/null +++ b/extensions/fablabchemnitz/bezier_envelope/bezier_envelope.inx @@ -0,0 +1,16 @@ + + + Bezier Envelope + fablabchemnitz.de.bezier_envelope + + path + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/bezier_envelope/bezier_envelope.py b/extensions/fablabchemnitz/bezier_envelope/bezier_envelope.py new file mode 100644 index 0000000..b8386de --- /dev/null +++ b/extensions/fablabchemnitz/bezier_envelope/bezier_envelope.py @@ -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() \ No newline at end of file diff --git a/extensions/fablabchemnitz/bezier_envelope/meta.json b/extensions/fablabchemnitz/bezier_envelope/meta.json new file mode 100644 index 0000000..21a3569 --- /dev/null +++ b/extensions/fablabchemnitz/bezier_envelope/meta.json @@ -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" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/blobs/blobs.inx b/extensions/fablabchemnitz/blobs/blobs.inx new file mode 100644 index 0000000..31c3980 --- /dev/null +++ b/extensions/fablabchemnitz/blobs/blobs.inx @@ -0,0 +1,32 @@ + + + Blobs Texture + fablabchemnitz.de.blobs + + + + 10 + true + 1000 + 1000 + + + + 10 + 50. + 0. + 0.3 + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/blobs/blobs.py b/extensions/fablabchemnitz/blobs/blobs.py new file mode 100644 index 0000000..0ab642e --- /dev/null +++ b/extensions/fablabchemnitz/blobs/blobs.py @@ -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() \ No newline at end of file diff --git a/extensions/fablabchemnitz/blobs/meta.json b/extensions/fablabchemnitz/blobs/meta.json new file mode 100644 index 0000000..94a76fe --- /dev/null +++ b/extensions/fablabchemnitz/blobs/meta.json @@ -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" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/blueprint_maker/blueprint_maker.inx b/extensions/fablabchemnitz/blueprint_maker/blueprint_maker.inx new file mode 100644 index 0000000..8a9953a --- /dev/null +++ b/extensions/fablabchemnitz/blueprint_maker/blueprint_maker.inx @@ -0,0 +1,29 @@ + + + Blueprint Maker + fablabchemnitz.de.blueprint_maker + + + + + + + 1.000 + + + + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/blueprint_maker/blueprint_maker.py b/extensions/fablabchemnitz/blueprint_maker/blueprint_maker.py new file mode 100644 index 0000000..c77bfe6 --- /dev/null +++ b/extensions/fablabchemnitz/blueprint_maker/blueprint_maker.py @@ -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() \ No newline at end of file diff --git a/extensions/fablabchemnitz/blueprint_maker/meta.json b/extensions/fablabchemnitz/blueprint_maker/meta.json new file mode 100644 index 0000000..dd73981 --- /dev/null +++ b/extensions/fablabchemnitz/blueprint_maker/meta.json @@ -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" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/bounding_box/bounding_box.inx b/extensions/fablabchemnitz/bounding_box/bounding_box.inx new file mode 100644 index 0000000..5cbe950 --- /dev/null +++ b/extensions/fablabchemnitz/bounding_box/bounding_box.inx @@ -0,0 +1,32 @@ + + + Bounding Box + fablabchemnitz.de.bounding_box + + + + + + + + + + 0.000 + 0.000 + + true + false + + true + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/bounding_box/bounding_box.py b/extensions/fablabchemnitz/bounding_box/bounding_box.py new file mode 100644 index 0000000..af99974 --- /dev/null +++ b/extensions/fablabchemnitz/bounding_box/bounding_box.py @@ -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() \ No newline at end of file diff --git a/extensions/fablabchemnitz/bounding_box/meta.json b/extensions/fablabchemnitz/bounding_box/meta.json new file mode 100644 index 0000000..c6cf655 --- /dev/null +++ b/extensions/fablabchemnitz/bounding_box/meta.json @@ -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" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/bouwkamp_code/bouwkamp_code.inx b/extensions/fablabchemnitz/bouwkamp_code/bouwkamp_code.inx new file mode 100644 index 0000000..2ac5c8c --- /dev/null +++ b/extensions/fablabchemnitz/bouwkamp_code/bouwkamp_code.inx @@ -0,0 +1,28 @@ + + + Bouwkamp Code + fablabchemnitz.de.bouwkamp + + + 21, 112, 112, [50, 35, 27], [8, 19], [15, 17, 11], [6, 24], [29, 25, 9, 2], [7, 18], [16], [42], [4, 37], [33] + true + + + + + + + all + + + + + + + + diff --git a/extensions/fablabchemnitz/bouwkamp_code/bouwkamp_code.py b/extensions/fablabchemnitz/bouwkamp_code/bouwkamp_code.py new file mode 100644 index 0000000..8493f91 --- /dev/null +++ b/extensions/fablabchemnitz/bouwkamp_code/bouwkamp_code.py @@ -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() \ No newline at end of file diff --git a/extensions/fablabchemnitz/bouwkamp_code/meta.json b/extensions/fablabchemnitz/bouwkamp_code/meta.json new file mode 100644 index 0000000..d65760e --- /dev/null +++ b/extensions/fablabchemnitz/bouwkamp_code/meta.json @@ -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" + ] + } +] diff --git a/extensions/fablabchemnitz/braille_l18n/braille_l18n.inx b/extensions/fablabchemnitz/braille_l18n/braille_l18n.inx new file mode 100644 index 0000000..e36574e --- /dev/null +++ b/extensions/fablabchemnitz/braille_l18n/braille_l18n.inx @@ -0,0 +1,25 @@ + + + Convert To Localized Braille + fablabchemnitz.de.braille_l18n + + + + + + + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/braille_l18n/braille_l18n.py b/extensions/fablabchemnitz/braille_l18n/braille_l18n.py new file mode 100644 index 0000000..57cf2e4 --- /dev/null +++ b/extensions/fablabchemnitz/braille_l18n/braille_l18n.py @@ -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() \ No newline at end of file diff --git a/extensions/fablabchemnitz/braille_l18n/meta.json b/extensions/fablabchemnitz/braille_l18n/meta.json new file mode 100644 index 0000000..5b8ba0f --- /dev/null +++ b/extensions/fablabchemnitz/braille_l18n/meta.json @@ -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" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/can_generator/can_generator.inx b/extensions/fablabchemnitz/can_generator/can_generator.inx new file mode 100644 index 0000000..0541f16 --- /dev/null +++ b/extensions/fablabchemnitz/can_generator/can_generator.inx @@ -0,0 +1,29 @@ + + + Can Generator + fablabchemnitz.de.can_generator + + + 30 + 5 + 50 + 22.5 + 3.6 + false + + + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/can_generator/can_generator.py b/extensions/fablabchemnitz/can_generator/can_generator.py new file mode 100644 index 0000000..6b3361d --- /dev/null +++ b/extensions/fablabchemnitz/can_generator/can_generator.py @@ -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() \ No newline at end of file diff --git a/extensions/fablabchemnitz/can_generator/meta.json b/extensions/fablabchemnitz/can_generator/meta.json new file mode 100644 index 0000000..db033f3 --- /dev/null +++ b/extensions/fablabchemnitz/can_generator/meta.json @@ -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" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/card_layout_guides/card_layout_guides.inx b/extensions/fablabchemnitz/card_layout_guides/card_layout_guides.inx new file mode 100644 index 0000000..2a23116 --- /dev/null +++ b/extensions/fablabchemnitz/card_layout_guides/card_layout_guides.inx @@ -0,0 +1,29 @@ + + + Cards Layout Guides + fablabchemnitz.de.card_layout_guides + + + + + 60 + 30 + + + + + 2 + 2 + 15 + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/card_layout_guides/card_layout_guides.py b/extensions/fablabchemnitz/card_layout_guides/card_layout_guides.py new file mode 100644 index 0000000..56d7a47 --- /dev/null +++ b/extensions/fablabchemnitz/card_layout_guides/card_layout_guides.py @@ -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() \ No newline at end of file diff --git a/extensions/fablabchemnitz/card_layout_guides/meta.json b/extensions/fablabchemnitz/card_layout_guides/meta.json new file mode 100644 index 0000000..33dc28c --- /dev/null +++ b/extensions/fablabchemnitz/card_layout_guides/meta.json @@ -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" + ] + } +] diff --git a/extensions/fablabchemnitz/checkerboard/checkerboard.inx b/extensions/fablabchemnitz/checkerboard/checkerboard.inx new file mode 100644 index 0000000..7b0a6a8 --- /dev/null +++ b/extensions/fablabchemnitz/checkerboard/checkerboard.inx @@ -0,0 +1,37 @@ + + + Checkerboard + fablabchemnitz.de.checkerboard + + + 50px + 10 + 10 + true + + + 4286282751 + + + 8092671 + + + + + + + + + + + all + + + + + + + + diff --git a/extensions/fablabchemnitz/checkerboard/checkerboard.py b/extensions/fablabchemnitz/checkerboard/checkerboard.py new file mode 100644 index 0000000..992437b --- /dev/null +++ b/extensions/fablabchemnitz/checkerboard/checkerboard.py @@ -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() \ No newline at end of file diff --git a/extensions/fablabchemnitz/checkerboard/meta.json b/extensions/fablabchemnitz/checkerboard/meta.json new file mode 100644 index 0000000..24d5aa2 --- /dev/null +++ b/extensions/fablabchemnitz/checkerboard/meta.json @@ -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" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/circle_tangents/circle_tangents.inx b/extensions/fablabchemnitz/circle_tangents/circle_tangents.inx new file mode 100644 index 0000000..ed02802 --- /dev/null +++ b/extensions/fablabchemnitz/circle_tangents/circle_tangents.inx @@ -0,0 +1,26 @@ + + + Circle Tangents (Replaced by Snap Objects Feature) + fablabchemnitz.de.circle_tangents + + + + + + + + + + true + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/circle_tangents/circle_tangents.py b/extensions/fablabchemnitz/circle_tangents/circle_tangents.py new file mode 100644 index 0000000..4678fa7 --- /dev/null +++ b/extensions/fablabchemnitz/circle_tangents/circle_tangents.py @@ -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 . + + 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() \ No newline at end of file diff --git a/extensions/fablabchemnitz/circle_tangents/meta.json b/extensions/fablabchemnitz/circle_tangents/meta.json new file mode 100644 index 0000000..f9d4197 --- /dev/null +++ b/extensions/fablabchemnitz/circle_tangents/meta.json @@ -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" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/clones_in_perspective/clones_in_perspective.inx b/extensions/fablabchemnitz/clones_in_perspective/clones_in_perspective.inx new file mode 100644 index 0000000..35b66d6 --- /dev/null +++ b/extensions/fablabchemnitz/clones_in_perspective/clones_in_perspective.inx @@ -0,0 +1,18 @@ + + + Clones In Perspective + fablabchemnitz.de.clones_in_perspective + 5 + 0.8 + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/clones_in_perspective/clones_in_perspective.py b/extensions/fablabchemnitz/clones_in_perspective/clones_in_perspective.py new file mode 100644 index 0000000..d3ebf8f --- /dev/null +++ b/extensions/fablabchemnitz/clones_in_perspective/clones_in_perspective.py @@ -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() \ No newline at end of file diff --git a/extensions/fablabchemnitz/clones_in_perspective/meta.json b/extensions/fablabchemnitz/clones_in_perspective/meta.json new file mode 100644 index 0000000..832aea4 --- /dev/null +++ b/extensions/fablabchemnitz/clones_in_perspective/meta.json @@ -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" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/close_paths/close_paths.inx b/extensions/fablabchemnitz/close_paths/close_paths.inx new file mode 100644 index 0000000..6ca80f9 --- /dev/null +++ b/extensions/fablabchemnitz/close_paths/close_paths.inx @@ -0,0 +1,16 @@ + + + Close Paths + fablabchemnitz.de.close_paths + + path + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/close_paths/close_paths.py b/extensions/fablabchemnitz/close_paths/close_paths.py new file mode 100644 index 0000000..17d2243 --- /dev/null +++ b/extensions/fablabchemnitz/close_paths/close_paths.py @@ -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() \ No newline at end of file diff --git a/extensions/fablabchemnitz/close_paths/meta.json b/extensions/fablabchemnitz/close_paths/meta.json new file mode 100644 index 0000000..baa4f1a --- /dev/null +++ b/extensions/fablabchemnitz/close_paths/meta.json @@ -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" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/convert_to_polylines/convert_to_polylines.inx b/extensions/fablabchemnitz/convert_to_polylines/convert_to_polylines.inx new file mode 100644 index 0000000..70f25c0 --- /dev/null +++ b/extensions/fablabchemnitz/convert_to_polylines/convert_to_polylines.inx @@ -0,0 +1,16 @@ + + + Convert To Polylines + fablabchemnitz.de.convert_to_polylines + + path + + + + + + + + diff --git a/extensions/fablabchemnitz/convert_to_polylines/convert_to_polylines.py b/extensions/fablabchemnitz/convert_to_polylines/convert_to_polylines.py new file mode 100644 index 0000000..7f3a640 --- /dev/null +++ b/extensions/fablabchemnitz/convert_to_polylines/convert_to_polylines.py @@ -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() \ No newline at end of file diff --git a/extensions/fablabchemnitz/convert_to_polylines/meta.json b/extensions/fablabchemnitz/convert_to_polylines/meta.json new file mode 100644 index 0000000..31401a0 --- /dev/null +++ b/extensions/fablabchemnitz/convert_to_polylines/meta.json @@ -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" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/convex_hull/convex_hull.inx b/extensions/fablabchemnitz/convex_hull/convex_hull.inx new file mode 100644 index 0000000..941efdb --- /dev/null +++ b/extensions/fablabchemnitz/convex_hull/convex_hull.inx @@ -0,0 +1,16 @@ + + + Convex Hull + fablabchemnitz.de.convex_hull + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/convex_hull/convex_hull.py b/extensions/fablabchemnitz/convex_hull/convex_hull.py new file mode 100644 index 0000000..15ea672 --- /dev/null +++ b/extensions/fablabchemnitz/convex_hull/convex_hull.py @@ -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 element which contains the twist & is a child + # of the new 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() \ No newline at end of file diff --git a/extensions/fablabchemnitz/convex_hull/meta.json b/extensions/fablabchemnitz/convex_hull/meta.json new file mode 100644 index 0000000..29a1317 --- /dev/null +++ b/extensions/fablabchemnitz/convex_hull/meta.json @@ -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" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/create_hexmap/create_hexmap.inx b/extensions/fablabchemnitz/create_hexmap/create_hexmap.inx new file mode 100644 index 0000000..6c63643 --- /dev/null +++ b/extensions/fablabchemnitz/create_hexmap/create_hexmap.inx @@ -0,0 +1,63 @@ + + + Create Hexmap + fablabchemnitz.de.create_hexmap + + + + + + + + + + 10 + 10 + 0 + 1.0 + 10.0 + + + false + false + false + false + false + false + + + . + false + false + false + true + 1 + 1 + 1 + + + true + true + true + true + false + false + false + + + false + debug.txt + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/create_hexmap/create_hexmap.py b/extensions/fablabchemnitz/create_hexmap/create_hexmap.py new file mode 100644 index 0000000..1867007 --- /dev/null +++ b/extensions/fablabchemnitz/create_hexmap/create_hexmap.py @@ -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() \ No newline at end of file diff --git a/extensions/fablabchemnitz/create_hexmap/meta.json b/extensions/fablabchemnitz/create_hexmap/meta.json new file mode 100644 index 0000000..67d3512 --- /dev/null +++ b/extensions/fablabchemnitz/create_hexmap/meta.json @@ -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" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/eggmazing/eggmazing.inx b/extensions/fablabchemnitz/eggmazing/eggmazing.inx new file mode 100644 index 0000000..32988c0 --- /dev/null +++ b/extensions/fablabchemnitz/eggmazing/eggmazing.inx @@ -0,0 +1,22 @@ + + + Eggmazing + fablabchemnitz.de.eggmazing + + + + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/eggmazing/eggmazing.py b/extensions/fablabchemnitz/eggmazing/eggmazing.py new file mode 100644 index 0000000..9459cec --- /dev/null +++ b/extensions/fablabchemnitz/eggmazing/eggmazing.py @@ -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() \ No newline at end of file diff --git a/extensions/fablabchemnitz/eggmazing/meta.json b/extensions/fablabchemnitz/eggmazing/meta.json new file mode 100644 index 0000000..8441995 --- /dev/null +++ b/extensions/fablabchemnitz/eggmazing/meta.json @@ -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" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/estucheria/airplanetypelinearcase.inx b/extensions/fablabchemnitz/estucheria/airplanetypelinearcase.inx new file mode 100644 index 0000000..205b92b --- /dev/null +++ b/extensions/fablabchemnitz/estucheria/airplanetypelinearcase.inx @@ -0,0 +1,26 @@ + + + Estucheria - Airplane Type Linear Case + fablabchemnitz.de.airplanetypelinearcase + airplanetypelinearcase.svg + + + + 25.0 + 25.0 + 25.0 + 5.0 + 5.0 + 5.0 + + all + + + + + + + + diff --git a/extensions/fablabchemnitz/estucheria/airplanetypelinearcase.py b/extensions/fablabchemnitz/estucheria/airplanetypelinearcase.py new file mode 100644 index 0000000..cbe2fc6 --- /dev/null +++ b/extensions/fablabchemnitz/estucheria/airplanetypelinearcase.py @@ -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() diff --git a/extensions/fablabchemnitz/estucheria/airplanetypelinearcase.svg b/extensions/fablabchemnitz/estucheria/airplanetypelinearcase.svg new file mode 100644 index 0000000..884c88d --- /dev/null +++ b/extensions/fablabchemnitz/estucheria/airplanetypelinearcase.svg @@ -0,0 +1,228 @@ + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + A + L + H + T + C + P + + + + + + + + + + + + + + + + + diff --git a/extensions/fablabchemnitz/estucheria/automaticbottomcase.inx b/extensions/fablabchemnitz/estucheria/automaticbottomcase.inx new file mode 100644 index 0000000..7a03501 --- /dev/null +++ b/extensions/fablabchemnitz/estucheria/automaticbottomcase.inx @@ -0,0 +1,26 @@ + + + Estucheria - Automatic Bottom Case + fablabchemnitz.de.automaticbottomcase + automaticbottomcase.svg + + + + 25.0 + 25.0 + 25.0 + 5.0 + 5.0 + 5.0 + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/estucheria/automaticbottomcase.py b/extensions/fablabchemnitz/estucheria/automaticbottomcase.py new file mode 100644 index 0000000..ec50a67 --- /dev/null +++ b/extensions/fablabchemnitz/estucheria/automaticbottomcase.py @@ -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() \ No newline at end of file diff --git a/extensions/fablabchemnitz/estucheria/automaticbottomcase.svg b/extensions/fablabchemnitz/estucheria/automaticbottomcase.svg new file mode 100644 index 0000000..05fca8a --- /dev/null +++ b/extensions/fablabchemnitz/estucheria/automaticbottomcase.svg @@ -0,0 +1,236 @@ + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + A + L + H + T + P + + + + + + + + + + + + + + + C + + diff --git a/extensions/fablabchemnitz/estucheria/box4p.inx b/extensions/fablabchemnitz/estucheria/box4p.inx new file mode 100644 index 0000000..8387531 --- /dev/null +++ b/extensions/fablabchemnitz/estucheria/box4p.inx @@ -0,0 +1,23 @@ + + + Estucheria - 4 Point Base Box + fablabchemnitz.de.box4p + box4p.svg + + + + 25.0 + 25.0 + 25.0 + + all + + + + + + + + diff --git a/extensions/fablabchemnitz/estucheria/box4p.py b/extensions/fablabchemnitz/estucheria/box4p.py new file mode 100644 index 0000000..3883848 --- /dev/null +++ b/extensions/fablabchemnitz/estucheria/box4p.py @@ -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() \ No newline at end of file diff --git a/extensions/fablabchemnitz/estucheria/box4p.svg b/extensions/fablabchemnitz/estucheria/box4p.svg new file mode 100644 index 0000000..926e33f --- /dev/null +++ b/extensions/fablabchemnitz/estucheria/box4p.svg @@ -0,0 +1,212 @@ + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + A + L + H + + + + + + + + + + + + + diff --git a/extensions/fablabchemnitz/estucheria/doublerailingcase.inx b/extensions/fablabchemnitz/estucheria/doublerailingcase.inx new file mode 100644 index 0000000..b3ec4ed --- /dev/null +++ b/extensions/fablabchemnitz/estucheria/doublerailingcase.inx @@ -0,0 +1,24 @@ + + + Estucheria - Double Railing Case + fablabchemnitz.de.doublerailingcase + doublerailingcase.svg + + + + 25.0 + 25.0 + 25.0 + 5.0 + + all + + + + + + + + diff --git a/extensions/fablabchemnitz/estucheria/doublerailingcase.py b/extensions/fablabchemnitz/estucheria/doublerailingcase.py new file mode 100644 index 0000000..20c5a65 --- /dev/null +++ b/extensions/fablabchemnitz/estucheria/doublerailingcase.py @@ -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() \ No newline at end of file diff --git a/extensions/fablabchemnitz/estucheria/doublerailingcase.svg b/extensions/fablabchemnitz/estucheria/doublerailingcase.svg new file mode 100644 index 0000000..bfcb1e2 --- /dev/null +++ b/extensions/fablabchemnitz/estucheria/doublerailingcase.svg @@ -0,0 +1,220 @@ + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + A + L + H + P + + + + + + + + + + + + + + + + + + + diff --git a/extensions/fablabchemnitz/estucheria/girdle.inx b/extensions/fablabchemnitz/estucheria/girdle.inx new file mode 100644 index 0000000..6b3e5d1 --- /dev/null +++ b/extensions/fablabchemnitz/estucheria/girdle.inx @@ -0,0 +1,24 @@ + + + Estucheria - Girdle + fablabchemnitz.de.girdle + girdle.svg + + + + 25.0 + 25.0 + 25.0 + 5.0 + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/estucheria/girdle.py b/extensions/fablabchemnitz/estucheria/girdle.py new file mode 100644 index 0000000..3f4531a --- /dev/null +++ b/extensions/fablabchemnitz/estucheria/girdle.py @@ -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() \ No newline at end of file diff --git a/extensions/fablabchemnitz/estucheria/girdle.svg b/extensions/fablabchemnitz/estucheria/girdle.svg new file mode 100644 index 0000000..48ca22e --- /dev/null +++ b/extensions/fablabchemnitz/estucheria/girdle.svg @@ -0,0 +1,180 @@ + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + A + L + H + P + + + + + + + + + diff --git a/extensions/fablabchemnitz/estucheria/linearcase.inx b/extensions/fablabchemnitz/estucheria/linearcase.inx new file mode 100644 index 0000000..09ac6e5 --- /dev/null +++ b/extensions/fablabchemnitz/estucheria/linearcase.inx @@ -0,0 +1,26 @@ + + + Estucheria - Linear Case + fablabchemnitz.de.linearcase + linearcase.svg + + + + 25.0 + 25.0 + 25.0 + 5.0 + 5.0 + 5.0 + + all + + + + + + + + diff --git a/extensions/fablabchemnitz/estucheria/linearcase.py b/extensions/fablabchemnitz/estucheria/linearcase.py new file mode 100644 index 0000000..b763ed7 --- /dev/null +++ b/extensions/fablabchemnitz/estucheria/linearcase.py @@ -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() \ No newline at end of file diff --git a/extensions/fablabchemnitz/estucheria/linearcase.svg b/extensions/fablabchemnitz/estucheria/linearcase.svg new file mode 100644 index 0000000..933822b --- /dev/null +++ b/extensions/fablabchemnitz/estucheria/linearcase.svg @@ -0,0 +1,217 @@ + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + A + L + H + T + C + P + + + diff --git a/extensions/fablabchemnitz/estucheria/meta.json b/extensions/fablabchemnitz/estucheria/meta.json new file mode 100644 index 0000000..6297abf --- /dev/null +++ b/extensions/fablabchemnitz/estucheria/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Estucheria - ", + "id": "fablabchemnitz.de.estucheria.", + "path": "estucheria", + "dependent_extensions": null, + "original_name": "", + "original_id": "org.inkscape.estucheria.", + "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" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/estucheria/swissbottomcase.inx b/extensions/fablabchemnitz/estucheria/swissbottomcase.inx new file mode 100644 index 0000000..178a3f9 --- /dev/null +++ b/extensions/fablabchemnitz/estucheria/swissbottomcase.inx @@ -0,0 +1,26 @@ + + + Estucheria - Swiss Bottom Case + fablabchemnitz.de.swissbottomcase + swissbottomcase.svg + + + + 25.0 + 25.0 + 25.0 + 5.0 + 5.0 + 5.0 + + all + + + + + + + + diff --git a/extensions/fablabchemnitz/estucheria/swissbottomcase.py b/extensions/fablabchemnitz/estucheria/swissbottomcase.py new file mode 100644 index 0000000..e141756 --- /dev/null +++ b/extensions/fablabchemnitz/estucheria/swissbottomcase.py @@ -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() diff --git a/extensions/fablabchemnitz/estucheria/swissbottomcase.svg b/extensions/fablabchemnitz/estucheria/swissbottomcase.svg new file mode 100644 index 0000000..a129015 --- /dev/null +++ b/extensions/fablabchemnitz/estucheria/swissbottomcase.svg @@ -0,0 +1,232 @@ + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + A + L + H + P + T + + + + + + + + + + C + + diff --git a/extensions/fablabchemnitz/fibonacci_pattern/fibonacci_pattern.inx b/extensions/fablabchemnitz/fibonacci_pattern/fibonacci_pattern.inx new file mode 100644 index 0000000..b6eabcb --- /dev/null +++ b/extensions/fablabchemnitz/fibonacci_pattern/fibonacci_pattern.inx @@ -0,0 +1,20 @@ + + + Fibonacci Pattern + fablabchemnitz.de.fibonacci_pattern + 2 + 5 + 400 + 20 + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/fibonacci_pattern/fibonacci_pattern.py b/extensions/fablabchemnitz/fibonacci_pattern/fibonacci_pattern.py new file mode 100644 index 0000000..33ee8b7 --- /dev/null +++ b/extensions/fablabchemnitz/fibonacci_pattern/fibonacci_pattern.py @@ -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() \ No newline at end of file diff --git a/extensions/fablabchemnitz/fibonacci_pattern/meta.json b/extensions/fablabchemnitz/fibonacci_pattern/meta.json new file mode 100644 index 0000000..ce68aff --- /dev/null +++ b/extensions/fablabchemnitz/fibonacci_pattern/meta.json @@ -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" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/flash_burst/flash_burst.inx b/extensions/fablabchemnitz/flash_burst/flash_burst.inx new file mode 100644 index 0000000..161fca2 --- /dev/null +++ b/extensions/fablabchemnitz/flash_burst/flash_burst.inx @@ -0,0 +1,27 @@ + + + Flash Burst + fablabchemnitz.de.flash_burst + 10 + 10 + + 350 + 350 + + 5 + 10 + + 3 + 5 + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/flash_burst/flash_burst.py b/extensions/fablabchemnitz/flash_burst/flash_burst.py new file mode 100644 index 0000000..d5abab3 --- /dev/null +++ b/extensions/fablabchemnitz/flash_burst/flash_burst.py @@ -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() + diff --git a/extensions/fablabchemnitz/flash_burst/meta.json b/extensions/fablabchemnitz/flash_burst/meta.json new file mode 100644 index 0000000..89b28e8 --- /dev/null +++ b/extensions/fablabchemnitz/flash_burst/meta.json @@ -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" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/flevobezier/flevobezier.inx b/extensions/fablabchemnitz/flevobezier/flevobezier.inx new file mode 100644 index 0000000..e9f1f63 --- /dev/null +++ b/extensions/fablabchemnitz/flevobezier/flevobezier.inx @@ -0,0 +1,16 @@ + + + Flevobézier + fablabchemnitz.de.flevobezier + + path + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/flevobezier/flevobezier.py b/extensions/fablabchemnitz/flevobezier/flevobezier.py new file mode 100644 index 0000000..0492c6e --- /dev/null +++ b/extensions/fablabchemnitz/flevobezier/flevobezier.py @@ -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() \ No newline at end of file diff --git a/extensions/fablabchemnitz/flevobezier/meta.json b/extensions/fablabchemnitz/flevobezier/meta.json new file mode 100644 index 0000000..4b4e3ba --- /dev/null +++ b/extensions/fablabchemnitz/flevobezier/meta.json @@ -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" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/flip/flip.inx b/extensions/fablabchemnitz/flip/flip.inx new file mode 100644 index 0000000..7137768 --- /dev/null +++ b/extensions/fablabchemnitz/flip/flip.inx @@ -0,0 +1,16 @@ + + + Flip + fablabchemnitz.de.flip + + path + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/flip/flip.py b/extensions/fablabchemnitz/flip/flip.py new file mode 100644 index 0000000..3d083b9 --- /dev/null +++ b/extensions/fablabchemnitz/flip/flip.py @@ -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() \ No newline at end of file diff --git a/extensions/fablabchemnitz/flip/meta.json b/extensions/fablabchemnitz/flip/meta.json new file mode 100644 index 0000000..ce81c2e --- /dev/null +++ b/extensions/fablabchemnitz/flip/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Flip", + "id": "fablabchemnitz.de.flip", + "path": "flip", + "dependent_extensions": null, + "original_name": "Flip", + "original_id": "aconz2.Flip", + "license": "Unlicense License", + "license_url": "https://github.com/aconz2/inkscape-extension-flip/blob/main/UNLICENSE", + "comment": "", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/flip", + "fork_url": "https://github.com/aconz2/inkscape-extension-flip", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Flip", + "inkscape_gallery_url": null, + "main_authors": [ + "github.com/aconz2", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/fret_ruler/fret_ruler.inx b/extensions/fablabchemnitz/fret_ruler/fret_ruler.inx new file mode 100644 index 0000000..97a61fb --- /dev/null +++ b/extensions/fablabchemnitz/fret_ruler/fret_ruler.inx @@ -0,0 +1,175 @@ + + + Fret Ruler + fablabchemnitz.de.fret_ruler + + + + + + + + + + + + + + + + + + + + 0 + 12tet + + + + + + + + 25.5 + 1.35 + 18 + + false + + 25.5 + 7 + + + 0.1 + 0.125 + + true + true + + + + + false + 2.0 + false + 3,5,7,10,12,12,15 + false + + + + + + 0.014 + + + + true + 9.75 + 5 + true + 0.5 + 0.3 + + + + + + + + true + 12 + true + diatonic + + + + + + + + + + + + + + path + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/fret_ruler/fret_ruler.py b/extensions/fablabchemnitz/fret_ruler/fret_ruler.py new file mode 100644 index 0000000..124ca49 --- /dev/null +++ b/extensions/fablabchemnitz/fret_ruler/fret_ruler.py @@ -0,0 +1,503 @@ +#!/usr/bin/env python3 +# Distributed under the terms of the GNU Lesser General Public License v3.0 +### Author: Neon22 - github 2016 + + +### +import inkex +import fret_scale as fs +import os # for scala file filtering +from math import radians, cos, sin, pi +from lxml import etree + +###---------------------------------------------------------------------------- +### Styles - color and size settings +Black = "#000000" +Font_height = 5 +# factor used for marker radius +marker_rad_factor = 4 +Line_style = { 'stroke' : Black, + 'stroke-width' : '0.2px', + 'fill' : "none" } +Dash_style = { 'stroke' : Black, + 'stroke-width' : '0.1px', + 'stroke-dasharray' : '0.9,0.9', + 'fill' : "none" } +Label_style = { 'font-size' : str(int(Font_height))+'px', + 'font-family' : 'arial', + 'text-anchor' : 'end', # middle + 'fill' : Black } +Centerline_style = { 'stroke' : Black, + 'stroke-width' : '0.1px', + 'stroke-dasharray' : '1.2,0.7,0.3,0.7', + 'fill' : "none" } + + +# Helper functions +def build_line(x1, y1, x2, y2, unitFactor): + path = 'M %s,%s L %s,%s' % (x1*unitFactor, y1*unitFactor, x2*unitFactor, y2*unitFactor) + return path + +def build_notch(x,y, notch_width, unitFactor, dir=1): + """ draw a notch around the x value + - dir=-1 means notch is on other side + """ + w_2 = notch_width/2 + x1 = x - w_2 + x2 = x + w_2 + y2 = y + notch_width*dir + path = 'L %s,%s L %s,%s' % (x1*unitFactor, y*unitFactor, x1*unitFactor, y2*unitFactor) + path += 'L %s,%s L %s,%s' % (x2*unitFactor, y2*unitFactor, x2*unitFactor, y*unitFactor) + return path + +def draw_center_cross(x,y, parent, length=2, style=Line_style): + " center cross for holes " + d = 'M {0},{1} l {2},0 M {3},{4} l 0,{2}'.format(x-length,y, length*2, x,y-length) + cross_attribs = { inkex.addNS('label','inkscape'): 'Center cross', + 'style': str(inkex.Style(style)), 'd': d } + etree.SubElement(parent, inkex.addNS('path','svg'), cross_attribs ) + +def draw_SVG_circle(cx, cy, radius, parent, name='circle', style=Line_style): + " structure an SVG circle entity under parent " + circ_attribs = {'style': str(inkex.Style(style)), + 'cx': str(cx), 'cy': str(cy), + 'r': str(radius), + inkex.addNS('label','inkscape'): name} + circle = etree.SubElement(parent, inkex.addNS('circle','svg'), circ_attribs ) + +def draw_circle_marker(x,y, radius, parent): + " circle with cross at center " + draw_center_cross(x, y, parent, radius/5.0) + draw_SVG_circle(x, y, radius, parent) + + +### +class FretRuler(inkex.EffectExtension): + + def add_arguments(self, pars): + pars.add_argument('--method', default='12th Root of 2', help="Method to calculate scale") + pars.add_argument('--draw_style', default='Ruler', help="How to draw the Ruler/NEck") + pars.add_argument("--nth", type=int,default=12, help="For different number of notes in a scale") + pars.add_argument('--scala_filename', default='12tet', help="Name of file in scales directory") + pars.add_argument("--units", default="in", help="The units of entered dimensions") + pars.add_argument("--length", type=float, default=25.5, help="Length of the Scale (and Ruler)") + pars.add_argument("--width", type=float, default=1.5, help="Width of the Ruler (= Nut if drawing a neck)") + pars.add_argument("--frets", type=int, default=18, help="number of frets on the scale") + # + pars.add_argument("--fanned", type=inkex.Boolean, default=False, help="Two scales on either side of the Neck") + pars.add_argument("--basslength", type=float, default=25.5, help="Length of the Bass side Scale") + pars.add_argument("--perpendicular", type=int, default=7, help="Fret number which is perpendicular to the Neck") + # + pars.add_argument("--linewidth", type=float, default=0.1, help="Width of drawn lines") + pars.add_argument("--notch_width", type=float, default=0.125, help="Width of Fret notches on Router template") + pars.add_argument("--annotate", type=inkex.Boolean, default=True, help="Annotate with Markers etc") + pars.add_argument("--centerline", type=inkex.Boolean, default=True, help="Draw a centerline") + # Neck + pars.add_argument("--constant_width", type=inkex.Boolean, default=True, help="Use Bridge width as well to make Neck") + pars.add_argument("--width_bridge", type=float, default=2.0, help="Width at the Bridge (drawing Neck not Ruler)") + pars.add_argument("--show_markers", type=inkex.Boolean, default=False, help="Show Neck Marker Positions") + pars.add_argument('--markers', default='3,5,7,10,12,12,15', help="List of frets to draw markers on") + # + pars.add_argument("--nutcomp", type=inkex.Boolean, default=False, help="Modify Nut position") + pars.add_argument("--nutcomp_value", default="0.012in (0.30mm)", help="Preset (usual) Nut compensation values") + pars.add_argument("--nutcomp_manual", type=float, default=0.014, help="Manual distance to move Nut closer to Bridge") + # + pars.add_argument("--show_curves", type=inkex.Boolean, default=False, help="Show a neck curvature ruler") + pars.add_argument("--neck_radius", type=float, default=2.0, help="Radius of Neck curvature") + pars.add_argument("--arc_length", type=float, default=2.0, help="Length of Arc") + pars.add_argument("--block_mode", type=inkex.Boolean, default=False, help="Draw block or finger style") + pars.add_argument("--arc_height", type=float, default=2.0, help="height of Arc") + pars.add_argument("--string_spacing", type=float, default=2.0, help="Spacing between strings") + # + pars.add_argument("--filter_tones", type=inkex.Boolean, default=True, help="Only show Scala files with this many notes in a scale.") + pars.add_argument("--scale", type=int, default=12, help="number of Notes in the scale") + pars.add_argument("--filter_label", type=inkex.Boolean, default=True, help="Only show Scala files with this keyword in them.") + pars.add_argument("--keywords", default="diatonic", help="Keywords to search for") + # here so we can have tabs + pars.add_argument("-t", "--active-tab", default='ruler', help="Active tab.") + + def filter_scala_files(self, parent): + """ Look in the scale directory for files. + - show only files matching the filters + """ + filter_tones = self.options.filter_tones + filter_names = self.options.filter_label + numtones = self.options.scale + keywords = self.options.keywords + keywords = keywords.strip().split(',') + keywords = [k.lower() for k in keywords] + # + probable_dir = os.getcwd()+'/scales/' + files = os.listdir(probable_dir) + # inkex.utils.debug("%s"%([os.getcwd(),len(files)])) + # Display filenames in document + filenames = [["Searched %d files"%(len(files)), "Found no matches", 0]] + for f in files: + fname = probable_dir+f + data = fs.read_scala(fname, False) + # filter out files that don't match + if filter_tones and filter_names: + if numtones == data[1]: + if filter_names: + for k in keywords: + if data[0].find(k) > -1 or f.find(k) > -1: + filenames.append([f, data[0], data[1]]) + elif filter_tones: + if numtones == data[1]: + filenames.append([f, data[0], data[1]]) + elif filter_names: + for k in keywords: + if data[0].find(k) > -1 or f.find(k) > -1: + filenames.append([f, data[0], data[1]]) + # inkex.utils.debug("%s"%(filenames)) + # gathered them all - display them + if len(filenames) != 0: + filenames[0][1] = "Found %d matches"%(len(filenames)-1) + x = 0 + y = 0 + Label_style['text-anchor'] = 'start' + for f in filenames: + label = f[0] + if f[2] != 0: + label += " - (%d tones)"%(f[2]) + self.draw_label(x, y, label, parent) + self.draw_label(x+Font_height*2, y+Font_height*1.2, f[1], parent) + if y ==0: y += Font_height + y += Font_height*2.8 + Label_style['text-anchor'] = 'end' + +### + def draw_label(self, x,y, label, parent, transform=False, style=Label_style): + " add a text entity " + text_atts = {'style':str(inkex.Style(style)), + 'x': str(x), 'y': str(y) } + if transform: text_atts['transform'] = transform + text = etree.SubElement(parent, 'text', text_atts) + text.text = "%s" %(label) + +### + def draw_ruler(self, neck, parent, show_numbers=False): + " draw the ruler with the centre of nut at 0,0 (unless fanned)" + # fanned frets have a bass side as well as the normal(treble) side scale length + # assume fanned + treble_length = neck.length + bass_length = treble_length if not neck.fanned else neck.bass_scale + y1 = neck.nut_width/2 + y2 = neck.bridge_width/2 + startx = 0 + endx = 0 + # if neck is fanned - adjust start, end + if neck.fanned: + if neck.fan_offset > 0: + startx = neck.fan_offset + else: + endx = -neck.fan_offset + pts = [[treble_length+startx,-y2], [bass_length+endx,y2], [endx,y1]] + # Create the boundary(neck) paths + path = 'M %s,%s ' % (startx*self.convFactor, -y1*self.convFactor) + for i in range(3): + path += " L %s,%s "%(pts[i][0]*self.convFactor, pts[i][1]*self.convFactor) + path += "Z" + line_attribs = {'style' : str(inkex.Style(Line_style)), + inkex.addNS('label','inkscape') : 'Outline' } + line_attribs['d'] = path + ell = etree.SubElement(parent, inkex.addNS('path','svg'), line_attribs ) + # Draw the fret lines + distances = neck.frets # do the zeroth value as well + for count, xt in enumerate(distances): # seq of x offsets for each fret + xb = xt if not neck.fanned else neck.bass_frets[count] + # if neck is not straight, calc the extra bit to draw in Y + yt = yb = y1 + if y1 != y2: # neck not straight + yt = y1 + ((xt-startx)/float(treble_length) * (y2-y1)) + yb = y1 + ((xb-endx)/float(bass_length) * (y2-y1)) + path = build_line(xt, -yt, xb, yb, self.convFactor) + line_attribs['d'] = path + ell = etree.SubElement(parent, inkex.addNS('path','svg'), line_attribs) + # Fret Numbers on odd frets(+octave) + if show_numbers and (count%2 == 0 or count == neck.notes_in_scale-1): + # try to push the lower fret numbers to the right a little + Label_style['text-anchor'] = 'start' if count < 9 else 'middle' + label_pos = neck.find_mid_point(count, -neck.nut_width/3) + self.draw_label(label_pos[0]*self.convFactor, label_pos[1]*self.convFactor, count+1, parent) + Label_style['text-anchor'] = 'end' + + def draw_router_template(self, neck, parent, notch_width, show_numbers=False): + " draw the ruler as a notched router template " + length = neck.length + y = neck.nut_width/2 + startx = notch_width*6 + pts = [[length,-y], [length,y], [-startx,y]] + path = 'M %s,%s ' % (-startx*self.convFactor, -y*self.convFactor) # start + distances = [0] + distances.extend(neck.frets) + # style + line_attribs = {'style' : str(inkex.Style(Line_style)), + inkex.addNS('label','inkscape') : 'Outline' } + # draw the fret notches, lines, labels + for count, x in enumerate(distances): + path += build_notch(x,-y, notch_width, self.convFactor) + if show_numbers and (count%2 == 1 or count == 0 or count == neck.notes_in_scale): + Label_style['text-anchor'] = 'start' if count < 9 else 'middle' + self.draw_label(x*self.convFactor-Font_height, -y*self.convFactor+Font_height*2.2, count, parent) + Label_style['text-anchor'] = 'end' + # other side markers + path2 = build_line(x, y, x, notch_width*2-y, self.convFactor) + line_attribs['d'] = path2 + ell = etree.SubElement(parent, inkex.addNS('path','svg'), line_attribs) + # close other side of template + for i in range(3): + path += " L %s,%s "%(pts[i][0]*self.convFactor, pts[i][1]*self.convFactor) + path += "Z" + # Draw + line_attribs['d'] = path + etree.SubElement(parent, inkex.addNS('path','svg'), line_attribs ) + + def draw_neck_curve_ruler(self, neck, radius, arc_length, arc_height, string_spacing, parent): + " draw arcs for curved fretboards " + # perfect world draw ruler and lines to curved ruler. + # mode = 'block1' + block_mode = self.options.block_mode + tab_length = arc_height*3 * self.convFactor + diam_in = radius * 2 * self.convFactor + angle_d = 180*arc_length / (2*pi*radius) + angle = radians(angle_d) + dist = arc_height*self.convFactor + path = "M%s %s L%s %s" %(diam_in + dist,0, diam_in,0) + x_a = diam_in * cos(angle) + y_a = diam_in * sin(angle) + x_b = (diam_in + dist) * cos(angle) + y_b = (diam_in + dist) * sin(angle) + path += " A %s,%s 0 0 1 %s %s" % (diam_in, diam_in, x_a, y_a) + path += " L%s %s" %(x_b, y_b) + if block_mode: + # use a solid block style + # add a midpoint for users to play with + path += " L%s %s" % (diam_in+dist+(x_b-diam_in-dist)/2, y_b/2) + tab_length = 0 + else: # tab mode + # need another arc with tab sections + small_angle = radians(90*string_spacing / (2*pi*radius)) + angle2 = angle/2 + small_angle + angle3 = angle/2 - small_angle + x_c = (diam_in + dist) * cos(angle2) + y_c = (diam_in + dist) * sin(angle2) + x_d = (diam_in + dist + tab_length) * cos(angle2) + y_d = (diam_in + dist + tab_length) * sin(angle2) + x_e = (diam_in + dist + tab_length) * cos(angle3) + y_e = (diam_in + dist + tab_length) * sin(angle3) + x_f = (diam_in + dist) * cos(angle3) + y_f = (diam_in + dist) * sin(angle3) + path += " A %s,%s 0 0 0 %s %s" % (diam_in, diam_in, x_c, y_c) + path += " L%s %s" %(x_d, y_d) + path += " L%s %s" %(x_e, y_e) + path += " L%s %s" %(x_f, y_f) + path += " A %s,%s 0 0 0 %s %s" % (diam_in, diam_in, diam_in + dist, 0) + + # close path + path += 'z' + ypos = diam_in + dist + tab_length + self.options.width*self.convFactor + line_attribs = {'style' : str(inkex.Style(Line_style)), inkex.addNS('label','inkscape') : 'Neck Curve', + 'transform': 'rotate(%f) translate(%s,%s)' % (-angle_d/2 -90, -ypos,-dist)} + line_attribs['d'] = path + etree.SubElement(parent, inkex.addNS('path','svg'), line_attribs ) + # label + size = "%d"%radius if radius-int(radius) == 0 else "%4.2f"%(radius) + Label_style['text-anchor'] = 'start' + self.draw_label(0, 0, "Radius: %s%s"% (size, neck.units), parent, + transform='translate(%s,%s)'%(0,ypos-diam_in-dist/2)) + Label_style['text-anchor'] = 'end' + + def draw_title(self, neck, parent, initial="Fret Ruler:"): + " Draw list of labels far right of ruler/Neck " + labels = [initial] + length = "%d"%neck.length if neck.length-int(neck.length) == 0 else "%4.2f"%(neck.length) + if neck.fanned: + basslength = "%d"%neck.bass_scale if neck.bass_scale-int(neck.bass_scale) == 0 else "%4.2f"%(neck.bass_scale) + labels.append("Scale(Fanned): %s%s - %s%s" %(length, neck.units, basslength, neck.units)) + else: # not fanned + labels.append("Scale: %s%s, %d frets" %(length, neck.units, len(neck.frets))) + # + label2 = "Method: %s" % (neck.method.title()) + if neck.method == 'scala': + label2 += " (%s) %d tones" %(neck.scala.split('/')[-1], len(neck.scala_notes)) + labels.append(label2) + labels.append('"%s"' %(neck.description)) + else: + labels.append(label2) + # unit formatting + units = self.options.units + precision = 1 if units=='mm' else 2 + widthN = self.options.width + widthB = self.options.width_bridge + label_w = "{:4.{prec}f}{}".format(widthN, units, prec=precision) + if not self.options.constant_width: + label_w += "(Nut) - {:4.{prec}f}{}(Bridge)".format(widthB, units, prec=precision) + labels.append("Width: %s"%(label_w)) + if not self.options.constant_width and len(neck.frets)>11: + distance12 = neck.frets[11] + # inkex.utils.debug("%s"%([distance12/float(neck.length)])) + width12 = widthN + (distance12/float(neck.length) * (widthB-widthN)) + labels.append("(at 12th fret: {:4.{prec}f}{})".format(width12, units, prec=precision)) + # where to draw + starty = widthN if self.options.constant_width else widthB + y = -starty/2*self.convFactor + Font_height*1.2 + x_offset = 0 + if neck.fanned and self.options.draw_style != 'template': + x_offset = neck.fan_offset + x = neck.length*self.convFactor - Font_height*1.5 + x_offset*self.convFactor + # Draw + for label in labels: + self.draw_label(x,y, label, parent) + y += Font_height*1.2 + + def draw_nut_compensation(self, neck, distance, parent): + " " + # inkex.utils.debug("%s"%([distance])) + startx = 0 + endx = 0 + if neck.fanned: + if neck.fan_offset > 0: + startx = neck.fan_offset + else: + endx = -neck.fan_offset + y = self.options.width/2 + path = build_line(startx+distance, -y, endx+distance, y, self.convFactor) + line_attribs = {'style' : str(inkex.Style(Dash_style)), 'd':path, + inkex.addNS('label','inkscape') : 'Nut Compensation' } + etree.SubElement(parent, inkex.addNS('path','svg'), line_attribs) + + def draw_neck_markers(self, neck, parent): + " draw symbol at fret pos. N possible " + # list may contain several occurences of a fret - meaning draw dots equidistant + positions = neck.frets + try: # user input weirdness + locations = self.options.markers.strip().split(",") + counts = [[int(i),locations.count(i)] for i in locations if i] + fret_counts = [] + for f in counts: + if f not in fret_counts: fret_counts.append(f) + except: + inkex.errormsg("Could not parse list of fret numbers. E.g. 3,5,7,7") + fret_counts = [[3,1]] + # marker radius based on thinnest of the (to be marked) fret spacings + spacings = [neck.frets[f-1] - neck.frets[max(0,f-2)] for f,c in fret_counts if f < len(neck.frets)+1] + thinnest = min(spacings) + marker_radius = thinnest/marker_rad_factor*self.convFactor + for fret, count in fret_counts: + if fret <= len(positions): # ignore if > #frets on this neck + # inkex.utils.debug("%s"%([fret,count,positions[fret]])) + fret = fret-1 + if count == 1: # if odd, draw in center + markerpos = neck.find_mid_point(fret, 0) + draw_circle_marker(markerpos[0]*self.convFactor, markerpos[1]*self.convFactor, marker_radius, parent) + else: # draw several at that fret + sep = neck.nut_width/float(count+2) + for i in range(count): + markerpos = neck.find_mid_point(fret, sep*i*2 - sep*(count-1)) + draw_circle_marker(markerpos[0]*self.convFactor, markerpos[1]*self.convFactor, marker_radius, parent) + + +### + def effect(self): + # calc units conversion + self.convFactor = self.svg.unittouu("1" + self.options.units) + # fix line width + Line_style['stroke-width'] = self.svg.unittouu(str(self.options.linewidth) + "mm") + # Usually we want 12 tone octaves + numtones = 12 + if self.options.method == 'Nroot2': + numtones = int(self.options.nth) + self.options.method = '%droot2'%(numtones) + # Usually we don't want a scala file + scala_filename=False + if self.options.method == 'scala': + scala_filename = "scales/"+self.options.scala_filename + if scala_filename[-4:] != ".scl": + scala_filename += ".scl" + # Create group center of view + t = 'translate(%s,%s)' % (self.svg.namedview.center[0]-self.options.length*self.convFactor/2, self.svg.namedview.center[1]) + grp_attribs = {inkex.addNS('label','inkscape'):'Fret Ruler', 'transform':t} + grp = etree.SubElement(self.svg.get_current_layer(), 'g', grp_attribs) + page = self.options.active_tab[1:-1] + draw_style = self.options.draw_style + # check if on Scala filters page + if page == 'filters': + # display filtered scala files + self.filter_scala_files(grp) + else: # Regular action of drawing a Ruler... + # select which style to draw based on user choice and what page they're on... + # if on Ruler page then use draw_style + title = "Fret Ruler:" + if page == 'neck': draw_style = 'neck' + if page == 'ruler' and draw_style=='ruler' or draw_style=='template': + # override constant width if on Ruler page + self.options.constant_width = True + # calc fret widths + fret_width = self.options.width + if (page == 'neck' or draw_style=='neck'): + title = "Neck Ruler:" + if not self.options.constant_width: + fret_width = [self.options.width, self.options.width_bridge] + # Make the Neck + neck = fs.Neck(self.options.length, units=self.options.units, fret_width=fret_width) + neck.calc_fret_offsets(self.options.length, self.options.frets, self.options.method, + numtones=numtones, scala_filename=scala_filename) + if self.options.fanned: + # fanned frets so calc bass scale and adjust + perpendicular = min(self.options.perpendicular, len(neck.frets)) + off = neck.set_fanned(self.options.basslength, perpendicular) + if draw_style=='template': + notch = self.options.notch_width + title = "Router Template:" + self.draw_router_template(neck, grp, notch, self.options.annotate) + else: + self.draw_ruler(neck, grp, self.options.annotate) + self.draw_title(neck, grp, title) + if self.options.centerline and self.options.draw_style != 'template': + path = build_line(-0.5,0, max(neck.length, neck.bass_scale)+0.5, 0, self.convFactor) + line_attribs = {'style' : str(inkex.Style(Centerline_style)), 'd':path, + inkex.addNS('label','inkscape') : 'Centerline' } + etree.SubElement(grp, inkex.addNS('path','svg'), line_attribs) + # Neck specials + if page == 'neck' or draw_style=='neck': + # Nut compensation + if self.options.nutcomp: + value = self.options.nutcomp_value + try: + compensation = float(value) if value != 'manual' else float(self.options.nutcomp_manual) + self.draw_nut_compensation(neck, compensation, grp) + except: + inkex.errormsg("Could not determine Nut compensation. Use a number.") + # Markers + if self.options.show_markers: + self.draw_neck_markers(neck, grp) + # inkex.utils.debug("#%s#"%(ordered_chords)) + if self.options.show_curves: + # position below max height of title text + self.draw_neck_curve_ruler(neck, self.options.neck_radius, self.options.arc_length, self.options.arc_height, self.options.string_spacing, grp) + + + + + +# Create effect instance and apply it. +if __name__ == '__main__': + FretRuler().run() + +### TODO: +# - draw option for fret0 hole to hang ruler from +# - draw strings +# - how many strings +# - separation distance +# - work out interval offsets +# - calc bridge compensation +# - calc stretch compensation +# - draw side view with bridge, relief distances + +#BUGS: +# + + +# Links: +# Nut compensation: http://www.lmii.com/scale-length-intonation \ No newline at end of file diff --git a/extensions/fablabchemnitz/fret_ruler/fret_scale.py b/extensions/fablabchemnitz/fret_ruler/fret_scale.py new file mode 100644 index 0000000..4366fda --- /dev/null +++ b/extensions/fablabchemnitz/fret_ruler/fret_scale.py @@ -0,0 +1,399 @@ +#!/usr/bin/env python3 +# Distributed under the terms of the GNU Lesser General Public License v3.0 +### Author: Neon22 - github 2016 + +### fret scale calculation code + +from math import log, floor + +def fret_calc_ratio(length, howmany, ratio): + " given the ratio between notes, calc distance between frets " + # typically 18, 17.817, 17.835 for equal temperment scales + distances = [] + prev = 0 + for i in range(howmany): + distance = length / ratio + distances.append(prev+distance) + length -= distance + prev += distance + # print "%02d %6.4f %s" %(i, prev, distance) + return distances + +def fret_calc_root2(length, howmany, numtones=12): + " using Nroot2 method, calc distance between frets " + distances = [] + for i in range(howmany): + # Calculating Fret Spacing for a Single Fret + # d = s-(s/ (2^ (n/12))) + distance = length - (length / (pow(2, (i+1)/(float(numtones))) )) + distances.append(distance) + # print "%02d %6.4f" %(i, distance) + return distances + +def fret_calc_scala(length, howmany, scala_notes): + " use ratios from scala file, calc distance between frets " + distances = [] + for i in range(howmany): + if i < len(scala_notes): + r = scala_notes[i] + else: + end = pow(scala_notes[-1], int(i / float(len(scala_notes)))) + r = end * scala_notes[i%len(scala_notes)] + distance = length - (length / r) + distances.append(distance) + return distances + +def cents_to_ratio(cents): + " given a value in cents, calculate the ratio " + return pow(2, cents / 1200.0) + +def parse_scala(scala, filename, verbose=True): + """ Parse the readlines() from scala file into: + - description, numnotes, + - lists of pretty ratios, numeric ratios + """ + description = "" + numnotes = 0 + notes = [] + ratios = [] + error = False + # print scala + for line in scala: + try: + # take out leading and trailing spaces - get everything up to first space if exists + line = line.strip() # hold onto this for when we need the description + first = line.split()[0] # first element in the line + # print line + if first and first[0] != "!": # ignore all blank and comment lines + if not description: + # expecting description line first + # may contain unprintable characters - force into unicode + description = unicode(line, errors='ignore') + elif numnotes == 0: + # expecting notes count after description + numnotes = int(first) + else: # expecting sequences of notes + notes.append(first) # for later ref + # remove comments at end of line if exist + if first.count("!") > 0: + first = first[:first.find("!")] + if first.find('.') > -1: # cents + ratios.append(cents_to_ratio(float(first))) + elif first.find("/") > -1: # ratio + num, denom = first.split('/') + ratios.append(int(num)/float(denom)) + else: + ratios.append(int(first)) + except: + error = "ERROR: Failed to load "+filename + # + if verbose: + print ("Found:", description) + print ("",numnotes, "notes found.") + for n,r in zip(notes,ratios): + print (" %4.4f : %s"%(r, n)) + print (" check: indicated=found : %d=%d"%(numnotes,len(notes))) + if error: + return [error, numnotes, notes, ratios] + else: + return [description, numnotes, notes, ratios] + +def read_scala(filename, verbose=False): + " read and parse scala file into interval ratios " + try: + inf = open(filename, 'rB') + content = inf.readlines() + inf.close() + flag = verbose + # if filename.find("dyadic") > -1: flag = True + return parse_scala(content, filename, flag) + except: + return ["ERROR: Failed to load "+filename, 2, [1], [1.01]] + + +### frequency to note +def log_note(freq): + " find the octave the note is in " + octave = (log(freq) - log(261.626)) / log (2) + 4.0 + return octave + +def freq_to_note(freq): + lnote = log_note(freq) + octave = floor(lnote) + cents = 1200 * (lnote - octave) + notes = ['C','C#','D','D#','E','F','F#','G','G#','A','A#','B'] + offset = 50.0 + x = 1 + if cents < 50: + note = "C" + elif cents >= 1150: + note = "C" + cents -= 1200 + octave += 1 + else: + for j in range(1,12): + if offset <= cents < (offset + 100): + note = notes[x] + cents -= (j * 100) + break + offset += 100 + x += 1 + return "%s%d"%(note, int(octave)), "%4.2f"%(cents) + + +def int_or_float(value): + " true if value is an int or a float " + return type(value) == type(1) or type(value) == type(1.0) + + +### class to hold info about instrument necks +class Neck(object): + def __init__(self, length, strings=['G','C','E','A'], units='in', spacing=0.4, fret_width=1.5): + " " + # coerce single spacing value into a list of nut/bridge spacing + self.set_spacing(spacing) + # same for fret_width + self.set_width(fret_width) + # + self.length = length + self.strings = strings + self.units = units + self.frets = [] # Treble side frets if fanned + self.bass_frets =[] + self.fanned = False + self.bass_scale = 0 + self.fanned_vertical = False + self.method = '12root2' + self.notes_in_scale = False + # Scala + self.scala = False + self.description = False + self.scala_notes = False + self.scala_ratios = False + def __repr__(self): + extra = "" + if len(self.frets)>0: + extra += "%d frets"%(len(self.frets)) + if self.method == 'scala': + extra += "(%s)" %(self.scala.split('/')[-1]) # filename + return ""%(self.method, self.length, self.units, extra, len(self.strings)) + + def set_width(self, fret_width): + " get both values from this " + if int_or_float(fret_width): + fret_width = [fret_width,fret_width] + elif type(fret_width) != type([]): + fret_width = [1,1] + self.nut_width = fret_width[0] + self.bridge_width = fret_width[1] + + def set_spacing(self, spacing): + " get both values from this " + if int_or_float(spacing): + spacing = [spacing,spacing] + elif type(spacing) != type([]): + spacing = [1,1] + self.nut_spacing = spacing[0] + self.bridge_spacing = spacing[1] + + def set_fanned(self, bass_scale, vertical_fret): + """ keep existing treble calc and create Bass calc + - must have called calc_fret_offsets() before + (so notes_in_scale is set) + """ + # adjust the position of the treble side if required. + # calc fret_offset and if treble or bass side needs to be moved + # if treble - move self.frets + # if bass, add offset as calculated + treble = self.frets + # print treble + if self.method == 'scala': + bass = self.calc_fret_offsets(bass_scale, len(self.frets), method=self.method, scala_filename=self.scala) + else: + bass = self.calc_fret_offsets(bass_scale, len(self.frets), method=self.method, numtones=self.notes_in_scale) + offset = 0 if vertical_fret ==0 else bass[vertical_fret - 1] - treble[vertical_fret - 1] + # print "offset", offset, "bass",bass + if offset > 0: + # shift treble + for i in range(len(treble)): + treble[i] += offset + else: # shift bass + for i in range(len(bass)): + bass[i] -= offset + self.frets = treble + self.bass_frets = bass + self.bass_scale = bass_scale + self.fanned_vertical = vertical_fret + self.fan_offset = offset + self.fanned = True + return offset + + def find_mid_point(self, fret_index, width_offset): + """ find midpoint of fret, fret-1 along neck + and ///y width where width_offset=0 means center of neck + """ + y_factor = (width_offset + self.nut_width/2) / float(self.nut_width) + # assume fanned + tpos_f1 = self.frets[fret_index] + bpos_f1 = tpos_f1 if not self.fanned else self.bass_frets[fret_index] + if self.fanned: + if self.fan_offset >= 0: + tpos_f0 = self.fan_offset if fret_index<=1 else self.frets[fret_index-1] + bpos_f0 = 0 if fret_index<=1 else self.bass_frets[fret_index-1] + else: + bpos_f0 = -self.fan_offset if fret_index<=1 else self.bass_frets[fret_index-1] + tpos_f0 = 0 if fret_index<=1 else self.frets[fret_index-1] + else: + tpos_f0 = 0 if fret_index<=1 else self.frets[fret_index-1] + bpos_f0 = 0 if fret_index<=1 else tpos_f0 + # + mid_tpos = tpos_f0 + (tpos_f1 - tpos_f0)/2 + mid_bpos = bpos_f0 + (bpos_f1 - bpos_f0)/2 + # print fret_index, y_factor + # print " %4.2f %4.2f %4.2f"% (tpos_f0, tpos_f1, mid_tpos) + # print " %4.2f %4.2f %4.2f"% (bpos_f0, bpos_f1, mid_bpos) + # the mid_xx positions are self.nut_width apart + return [mid_tpos + (mid_bpos-mid_tpos)*y_factor, width_offset/self.nut_width*1.5] + + def calc_fret_offsets(self, length, howmany, method='12root2', numtones=12, scala_filename=False): + " calc fret positions from Nut for all methods " + frets = False # store them in here + if scala_filename: + scala_notes = read_scala(scala_filename) + self.method = 'scala' + self.scala = scala_filename + self.description = scala_notes[0] + self.scala_notes = scala_notes[2] + self.scala_ratios = scala_notes[3] # [-1] + frets = fret_calc_scala(length, howmany, self.scala_ratios) + self.notes_in_scale = len(self.scala_ratios) + elif method.find('root2') > -1: + self.method = method + frets = fret_calc_root2(length, howmany, numtones) + self.notes_in_scale = numtones + elif method == '18': + self.method = method + ratio = 18 + frets = fret_calc_ratio(length, howmany, ratio) + self.notes_in_scale = 12 + elif method == '17.817': + self.method = method + ratio = 17.81715374510580 + frets = fret_calc_ratio(length, howmany, ratio) + self.notes_in_scale = 12 + elif method == '17.835': + self.method = method + ratio = 17.835 + frets = fret_calc_ratio(length, howmany, ratio) + self.notes_in_scale = 12 + # update the iv + self.frets = frets + return frets + + def show_frets(self): + " pretty print " + for i,d in enumerate(self.frets): + print ("%2d: %4.4f" %(i+1,d)) + if self.bass_frets: + for i,d in enumerate(self.bass_frets): + print ("%2d: %4.4f" %(i+1,d)) + + def compare_methods(self, howmany, verbose=True): + " show differences in length for the main methods (not scala) " + distances = [] + differences = [] + methods = ['12root2', '18', '17.817', '17.835'] + n = Neck(30) # long one to maximise errors + for method in methods: + distances.append(n.calc_fret_offsets(n.length, howmany, method)) + # print distances[-1] + for i in range(1, len(methods)): + differences.append( [a-b for (a,b) in zip(distances[0], distances[i])] ) + if verbose: + print("Differences from 12root2") + for i,m in enumerate(methods[1:]): + print ("\nMethod = %s\n " %(m)) + for d in differences[i]: + print ("%2.3f " %(d)) + print("") + # package + combined = [] + for i,m in enumerate(methods[1:]): + combined.append([m, max(differences[i]), differences[i]]) + return combined + +# Gibson "rule of 18" base scale is in sys 18. +# Martin 24.9 (24.84), 25.4 (act 25.34) rough approx and round up. not actually the scale length +# The difference between 17.817 and 17.835 came from rounding early and carrying the roundoff error through the rest of the work. +# where r = twelfth root of two and put the first fret where it would make the sounding length of the string 1/r of its original length + + +### tests +if __name__ == "__main__": + n = Neck(24) + f = n.calc_fret_offsets(n.length, 12, '12root2') + n.show_frets() + print (n) + errors = n.compare_methods(22, False) + for m,e,d in errors: + print ("for method '%s': max difference from 12Root2 = %4.3f%s (on highest fret)"%(m,e, n.units)) + # + n = Neck(24) + f = n.calc_fret_offsets(n.length, 22, 'scala', scala_filename='scales/diat_chrom.scl') + n.show_frets() + print ("Fanning") + # n.set_fanned(25,0) + # n.show_frets() + # print n + # print n.description + # print n.scala + # print n.scala_notes + # print n.scala_ratios + + # similar to scale=10 to scale = 9.94 but slightly diff neaer the nut. + + # scala_notes = read_scala("scales/alembert2.scl")#, True) + # print "Notes=",len(scala_notes[-1]), scala_notes[1] + # for d in fret_calc_scala(24, scala_notes[-1]): print d + + # test load all scala files + # import os + # probable_dir = "scales/" + # files = os.listdir(probable_dir) + # for f in files: + # fname = probable_dir+f + # # print f + # data = read_scala(fname) + # # print " ",data[0] + # if data[0][:5] == "ERROR": + # print "!!!! ERROR",fname + + ## freq conversion + print("") + for f in [440,443,456,457, 500,777, 1086]: + print (f, freq_to_note(f)) + + ## fanned frets + # print + # for f in [1,11]: + # print n.find_mid_point(f,-0.75) + + # get to this eventually + string_compensation = [ + 0.0086, 0.0119, 0.0107, 0.0124, 0.0151, 0.0175, 0.020, 0.0222, 0.0244, 0.0263, + 0.0282, 0.030, 0.0371, 0.4235 + ] + +### Optionally: +# how many strings, +# (associated sequence of intervals) + + +### refs: +#http://fretfocus.anastigmatix.net/ +#http://windworld.com/features/tools-resources/exmis-fret-placement-calculator/ +#http://www.huygens-fokker.org/scala/ + +# superstart: +# notes on the fretboard +# https://www.youtube.com/watch?v=-jW1Xx0t3ZI \ No newline at end of file diff --git a/extensions/fablabchemnitz/fret_ruler/meta.json b/extensions/fablabchemnitz/fret_ruler/meta.json new file mode 100644 index 0000000..431b766 --- /dev/null +++ b/extensions/fablabchemnitz/fret_ruler/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Fret Ruler", + "id": "fablabchemnitz.de.fret_ruler", + "path": "fret_ruler", + "dependent_extensions": null, + "original_name": "Fret Ruler", + "original_id": "Neon22.github.fret_ruler", + "license": "GNU GPL v3", + "license_url": "https://github.com/Neon22/inkscape_fret_ruler/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/fret_ruler", + "fork_url": "https://github.com/Neon22/inkscape_fret_ruler", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Fret+Ruler", + "inkscape_gallery_url": null, + "main_authors": [ + "github.com/Neon22", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/gears/gears.inx b/extensions/fablabchemnitz/gears/gears.inx new file mode 100644 index 0000000..058d58f --- /dev/null +++ b/extensions/fablabchemnitz/gears/gears.inx @@ -0,0 +1,94 @@ + + + Gears + fablabchemnitz.de.gears + + + 24 + 1.0 + + + + + + + + + + + + + + 20.0 + false + false + false + + + 0 + 0 + false + 4.0 + 3 + 3.0 + 10.0 + + + + + + + + + + + false + 10 + 5 + 5 + + + + + + + + true + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/gears/gears.py b/extensions/fablabchemnitz/gears/gears.py new file mode 100644 index 0000000..9d45add --- /dev/null +++ b/extensions/fablabchemnitz/gears/gears.py @@ -0,0 +1,661 @@ +#!/usr/bin/env python3 +''' +Copyright (C) 2007 Aaron Spike (aaron @ ekips.org) +Copyright (C) 2007 Tavmjong Bah (tavmjong @ free.fr) +Copyright (C) http://cnc-club.ru/forum/viewtopic.php?f=33&t=434&p=2594#p2500 +Copyright (C) 2014 Jürgen Weigert (juewei@fabmail.org) +Copyright (C) 2020 Spadino (spada.andrea @ gmail DOT 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 + +2014-03-20 jw@suse.de 0.2 Option --accuracy=0 for automatic added. +2014-03-21 sent upstream: https://bugs.launchpad.net/inkscape/+bug/1295641 +2014-03-21 jw@suse.de 0.3 Fixed center of rotation for gears with odd number of teeth. +2014-04-04 juewei 0.7 Revamped calc_unit_factor(). +2014-04-05 juewei 0.7a Correctly positioned rack gear. + The geometry above the meshing line is wrong. +2014-04-06 juewei 0.7b Undercut detection added. Reference: + http://nptel.ac.in/courses/IIT-MADRAS/Machine_Design_II/pdf/2_2.pdf + Manually merged https://github.com/jnweiger/inkscape-gears-dev/pull/15 +2014-04-07 juewei 0.7c Manually merged https://github.com/jnweiger/inkscape-gears-dev/pull/17 +2014-04-09 juewei 0.8 Fixed https://github.com/jnweiger/inkscape-gears-dev/issues/19 + Ring gears are ready for production now. Thanks neon22 for driving this. + Profile shift implemented (Advanced Tab), fixing + https://github.com/jnweiger/inkscape-gears-dev/issues/9 +2015-05-29 juewei 0.9 ported to inkscape 0.91 + AttributeError: 'module' object inkex has no attribute 'uutounit + Fixed https://github.com/jnweiger/inkscape-gears-dev +2020-7-4 spadino 1.0 ported to inkscape 1.0 +''' + +import inkex +from lxml import etree +from os import devnull # for debugging +from math import pi, cos, sin, tan, radians, degrees, ceil, asin, acos, sqrt +two_pi = 2 * pi + + +__version__ = '1.0' + +def uutounit(self,nn,uu): + return self.svg.uutounit(nn,uu) + +def linspace(a,b,n): + """ return list of linear interp of a to b in n steps + - if a and b are ints - you'll get an int result. + - n must be an integer + """ + return [a+x*(b-a)/(n-1) for x in range(0,n)] + +def involute_intersect_angle(Rb, R): + " " + Rb, R = float(Rb), float(R) + return (sqrt(R**2 - Rb**2) / (Rb)) - (acos(Rb / R)) + +def point_on_circle(radius, angle): + " return xy coord of the point at distance radius from origin at angle " + x = radius * cos(angle) + y = radius * sin(angle) + return (x, y) + +def points_to_bbox(p): + """ from a list of points (x,y pairs) + - return the lower-left xy and upper-right xy + """ + llx = urx = p[0][0] + lly = ury = p[0][1] + for x in p[1:]: + if x[0] < llx: llx = x[0] + elif x[0] > urx: urx = x[0] + if x[1] < lly: lly = x[1] + elif x[1] > ury: ury = x[1] + return (llx, lly, urx, ury) + +def points_to_bbox_center(p): + """ from a list of points (x,y pairs) + - find midpoint of bounding box around all points + - return (x,y) + """ + bbox = points_to_bbox(p) + return ((bbox[0]+bbox[2])/2.0, (bbox[1]+bbox[3])/2.0) + +def points_to_svgd(p): + " convert list of points into a closed SVG path list" + f = p[0] + p = p[1:] + svgd = 'M%.4f,%.4f' % f + for x in p: + svgd += 'L%.4f,%.4f' % x + svgd += 'z' + return svgd + +def draw_SVG_circle(parent, r, cx, cy, name, style): + " add an SVG circle entity to parent " + circ_attribs = {'style': str(inkex.Style(style)), + 'cx': str(cx), 'cy': str(cy), + 'r': str(r), + inkex.addNS('label','inkscape'):name} + circle = etree.SubElement(parent, inkex.addNS('circle','svg'), circ_attribs ) + + +### Undercut support functions +def undercut_min_teeth(pitch_angle, k=1.0): + """ computes the minimum tooth count for a + spur gear so that no undercut with the given pitch_angle (in deg) + and an addendum = k * metric_module, where 0 < k < 1 + Note: + The return value should be rounded upwards for perfect safety. E.g. + min_teeth = int(math.ceil(undercut_min_teeth(20.0))) # 18, not 17 + """ + x = sin(radians(pitch_angle)) + return 2*k /(x*x) + +def undercut_max_k(teeth, pitch_angle=20.0): + """ computes the maximum k value for a given teeth count and pitch_angle + so that no undercut occurs. + """ + x = sin(radians(pitch_angle)) + return 0.5 * teeth * x * x + +def undercut_min_angle(teeth, k=1.0): + """ computes the minimum pitch angle, to that the given teeth count (and + profile shift) cause no undercut. + """ + return degrees(asin(min(0.856, sqrt(2.0*k/teeth)))) # max 59.9 deg + + +def have_undercut(teeth, pitch_angle=20.0, k=1.0): + """ returns true if the specified number of teeth would + cause an undercut. + """ + return (teeth < undercut_min_teeth(pitch_angle, k)) + + +## gather all basic gear calculations in one place +def gear_calculations(num_teeth, circular_pitch, pressure_angle, clearance=0, ring_gear=False, profile_shift=0.): + """ Put base calcs for spur/ring gears in one place. + - negative profile shifting helps against undercut. + """ + diametral_pitch = pi / circular_pitch + pitch_diameter = num_teeth / diametral_pitch + pitch_radius = pitch_diameter / 2.0 + addendum = 1 / diametral_pitch + #dedendum = 1.157 / diametral_pitch # auto calc clearance + dedendum = addendum + dedendum *= 1+profile_shift + addendum *= 1-profile_shift + if ring_gear: + addendum = addendum + clearance # our method + else: + dedendum = dedendum + clearance # our method + # + # + base_radius = pitch_diameter * cos(radians(pressure_angle)) / 2.0 + outer_radius = pitch_radius + addendum + root_radius = pitch_radius - dedendum + # Tooth thickness: Tooth width along pitch circle. + tooth_thickness = ( pi * pitch_diameter ) / ( 2.0 * num_teeth ) + # we don't use these + working_depth = 2 / diametral_pitch + whole_depth = 2.157 / diametral_pitch + #outside_diameter = (num_teeth + 2) / diametral_pitch + # + return (pitch_radius, base_radius, + addendum, dedendum, outer_radius, root_radius, + tooth_thickness + ) + + +def generate_rack_points(tooth_count, pitch, addendum, pressure_angle, + rack_base_height, tab_length, clearance=0, draw_guides=False): + """ Return path (suitable for svg) of the Rack gear. + - rack gear uses straight sides + - involute on a circle of infinite radius is a simple linear ramp + - the meshing circle touches at y = 0, + - the highest elevation of the teeth is at y = +addendum + - the lowest elevation of the teeth is at y = -addendum-clearance + - the rack_base_height extends downwards from the lowest elevation. + - we generate this middle tooth exactly centered on the y=0 line. + (one extra tooth on the right hand side, if number of teeth is even) + """ + spacing = 0.5 * pitch # rolling one pitch distance on the spur gear pitch_diameter. + # roughly center rack in drawing, exact position is so that it meshes + # nicely with the spur gear. + # -0.5*spacing has a gap in the center. + # +0.5*spacing has a tooth in the center. + fudge = +0.5 * spacing + + tas = tan(radians(pressure_angle)) * addendum + tasc = tan(radians(pressure_angle)) * (addendum+clearance) + base_top = addendum+clearance + base_bot = addendum+clearance+rack_base_height + + x_lhs = -pitch * int(0.5*tooth_count-.5) - spacing - tab_length - tasc + fudge + #inkex.utils.debug("angle=%s spacing=%s"%(pressure_angle, spacing)) + # Start with base tab on LHS + points = [] # make list of points + points.append((x_lhs, base_bot)) + points.append((x_lhs, base_top)) + x = x_lhs + tab_length+tasc + + # An involute on a circle of infinite radius is a simple linear ramp. + # We need to add curve at bottom and use clearance. + for i in range(tooth_count): + # move along path, generating the next 'tooth' + # pitch line is at y=0. the left edge hits the pitch line at x + points.append((x-tasc, base_top)) + points.append((x+tas, -addendum)) + points.append((x+spacing-tas, -addendum)) + points.append((x+spacing+tasc, base_top)) + x += pitch + x -= spacing # remove last adjustment + # add base on RHS + x_rhs = x+tasc+tab_length + points.append((x_rhs, base_top)) + points.append((x_rhs, base_bot)) + # We don't close the path here. Caller does it. + # points.append((x_lhs, base_bot)) + + # Draw line representing the pitch circle of infinite diameter + guide_path = None + if draw_guides: + p = [] + p.append( (x_lhs + 0.5 * tab_length, 0) ) + p.append( (x_rhs - 0.5 * tab_length, 0) ) + guide_path = points_to_svgd(p) + # return points ready for use in an SVG 'path' + return (points, guide_path) + + +def generate_spur_points(teeth, base_radius, pitch_radius, outer_radius, root_radius, accuracy_involute, accuracy_circular): + """ given a set of core gear params + - generate the svg path for the gear + """ + half_thick_angle = two_pi / (4.0 * teeth ) #?? = pi / (2.0 * teeth) + pitch_to_base_angle = involute_intersect_angle( base_radius, pitch_radius ) + pitch_to_outer_angle = involute_intersect_angle( base_radius, outer_radius ) - pitch_to_base_angle + + start_involute_radius = max(base_radius, root_radius) + radii = linspace(start_involute_radius, outer_radius, accuracy_involute) + angles = [involute_intersect_angle(base_radius, r) for r in radii] + + centers = [(x * two_pi / float( teeth) ) for x in range( teeth ) ] + points = [] + + for c in centers: + # Angles + pitch1 = c - half_thick_angle + base1 = pitch1 - pitch_to_base_angle + offsetangles1 = [ base1 + x for x in angles] + points1 = [ point_on_circle( radii[i], offsetangles1[i]) for i in range(0,len(radii)) ] + + pitch2 = c + half_thick_angle + base2 = pitch2 + pitch_to_base_angle + offsetangles2 = [ base2 - x for x in angles] + points2 = [ point_on_circle( radii[i], offsetangles2[i]) for i in range(0,len(radii)) ] + + points_on_outer_radius = [ point_on_circle(outer_radius, x) for x in linspace(offsetangles1[-1], offsetangles2[-1], accuracy_circular) ] + + if root_radius > base_radius: + pitch_to_root_angle = pitch_to_base_angle - involute_intersect_angle(base_radius, root_radius ) + root1 = pitch1 - pitch_to_root_angle + root2 = pitch2 + pitch_to_root_angle + points_on_root = [point_on_circle (root_radius, x) for x in linspace(root2, root1+(two_pi/float(teeth)), accuracy_circular) ] + p_tmp = points1 + points_on_outer_radius[1:-1] + points2[::-1] + points_on_root[1:-1] # [::-1] reverses list; [1:-1] removes first and last element + else: + points_on_root = [point_on_circle (root_radius, x) for x in linspace(base2, base1+(two_pi/float(teeth)), accuracy_circular) ] + p_tmp = points1 + points_on_outer_radius[1:-1] + points2[::-1] + points_on_root # [::-1] reverses list + + points.extend( p_tmp ) + return (points) + + +def generate_spokes_path(root_radius, spoke_width, spoke_count, mount_radius, mount_hole, + unit_factor, unit_label): + """ given a set of constraints + - generate the svg path for the gear spokes + - lies between mount_radius (inner hole) and root_radius (bottom of the teeth) + - spoke width also defines the spacing at the root_radius + - mount_radius is adjusted so that spokes fit if there is room + - if no room (collision) then spokes not drawn + """ + # Spokes + collision = False # assume we draw spokes + messages = [] # messages to send back about changes. + path = '' + r_outer = root_radius - spoke_width + # checks for collision with spokes + # check for mount hole collision with inner spokes + if mount_radius <= mount_hole/2: + adj_factor = (r_outer - mount_hole/2) / 5 + if adj_factor < 0.1: + # not enough reasonable room + collision = True + else: + mount_radius = mount_hole/2 + adj_factor # small fix + messages.append("Mount support too small. Auto increased to %2.2f%s." % (mount_radius/unit_factor*2, unit_label)) + + # then check to see if cross-over on spoke width + if spoke_width * spoke_count +0.5 >= two_pi * mount_radius: + adj_factor = 1.2 # wrong value. its probably one of the points distances calculated below + mount_radius += adj_factor + messages.append("Too many spokes. Increased Mount support by %2.3f%s" % (adj_factor/unit_factor, unit_label)) + + # check for collision with outer rim + if r_outer <= mount_radius: + # not enough room to draw spokes so cancel + collision = True + if collision: # don't draw spokes if no room. + messages.append("Not enough room for Spokes. Decrease Spoke width.") + else: # draw spokes + for i in range(spoke_count): + points = [] + start_a, end_a = i * two_pi / spoke_count, (i+1) * two_pi / spoke_count + # inner circle around mount + asin_factor = spoke_width/mount_radius/2 + # check if need to clamp radius + asin_factor = max(-1.0, min(1.0, asin_factor)) # no longer needed - resized above + a = asin(asin_factor) + points += [ point_on_circle(mount_radius, start_a + a), point_on_circle(mount_radius, end_a - a)] + # is inner circle too small + asin_factor = spoke_width/r_outer/2 + # check if need to clamp radius + asin_factor = max(-1.0, min(1.0, asin_factor)) # no longer needed - resized above + a = asin(asin_factor) + points += [point_on_circle(r_outer, end_a - a), point_on_circle(r_outer, start_a + a) ] + + path += ( + "M %f,%f" % points[0] + + "A %f,%f %s %s %s %f,%f" % tuple((mount_radius, mount_radius, 0, 0 if spoke_count!=1 else 1, 1 ) + points[1]) + + "L %f,%f" % points[2] + + "A %f,%f %s %s %s %f,%f" % tuple((r_outer, r_outer, 0, 0 if spoke_count!=1 else 1, 0 ) + points[3]) + + "Z" + ) + return (path, messages) + + +class Gears(inkex.EffectExtension): + def __init__(self): + inkex.Effect.__init__(self) + # an alternate way to get debug info: + # could use inkex.utils.debug(string) instead... + # try: + # self.tty = open("/dev/tty", 'w') + # except: + # self.tty = open(devnull, 'w') # '/dev/null' for POSIX, 'nul' for Windows. + # # print >>self.tty, "gears-dev " + __version__ + + self.arg_parser.add_argument("-t", "--teeth", type=int, default=24, help="Number of teeth") + self.arg_parser.add_argument("-s", "--system", default='CP', help="Select system: 'CP' (Cyclic Pitch (default)), 'DP' (Diametral Pitch), 'MM' (Metric Module)") + self.arg_parser.add_argument("-d", "--dimension", type=float, default=1.0, help="Tooth size, depending on system (which defaults to CP)") + self.arg_parser.add_argument("-a", "--angle", type=float, default=20.0, help="Pressure Angle (common values: 14.5, 20, 25 degrees)") + self.arg_parser.add_argument("-p", "--profile_shift", type=float, default=20.0, help="Profile shift [in percent of the module]. Negative values help against undercut") + self.arg_parser.add_argument("-u", "--units", default='mm', help="Units this dialog is using") + self.arg_parser.add_argument("-A", "--accuracy", type=int, default=0, help="Accuracy of involute: automatic: 5..20 (default), best: 20(default), medium 10, low: 5; good acuracy is important with a low tooth count") + # Clearance: Radial distance between top of tooth on one gear to bottom of gap on another. + self.arg_parser.add_argument("-cl", "--clearance", type=float, default=0.0, help="Clearance between bottom of gap of this gear and top of tooth of another") + self.arg_parser.add_argument("-an", "--annotation", type=inkex.Boolean, default=False, help="Draw annotation text") + self.arg_parser.add_argument("-i", "--internal_ring", type=inkex.Boolean, default=False, help="Ring (or Internal) gear style (default: normal spur gear)") + self.arg_parser.add_argument("-mh", "--mount_hole", type=float, default=5, help="Mount hole diameter") + self.arg_parser.add_argument("-md", "--mount_diameter", type=float, default=15, help="Mount support diameter") + self.arg_parser.add_argument("-sc", "--spoke_count", type=int, default=3, help="Spokes count") + self.arg_parser.add_argument("-sw", "--spoke_width", type=float, default=5, help="Spoke width") + self.arg_parser.add_argument("-hr", "--holes_rounding", type=float, default=5, help="Holes rounding") + self.arg_parser.add_argument("-at", "--active_tab", default='', help="Active tab. Not used now.") + self.arg_parser.add_argument("-x", "--centercross", type=inkex.Boolean, default=False, help="Draw cross in center") + self.arg_parser.add_argument("-c", "--pitchcircle", type=inkex.Boolean, default=False, help="Draw pitch circle (for mating)") + self.arg_parser.add_argument("-r", "--draw_rack", type=inkex.Boolean, default=False, help="Draw rack gear instead of spur gear") + self.arg_parser.add_argument("-rl", "--rack_teeth_length", type=int, default=12, help="Length (in teeth) of rack") + self.arg_parser.add_argument("-rh", "--rack_base_height", type=float, default=8, help="Height of base of rack") + self.arg_parser.add_argument("-rt", "--rack_base_tab", type=float, default=14, help="Length of tabs on ends of rack") + self.arg_parser.add_argument("-ua", "--undercut_alert", type=inkex.Boolean, default=False, help="Let the user confirm a warning dialog if undercut occurs. This dialog also shows helpful hints against undercut") + + def add_text(self, node, text, position, text_height=12): + """ Create and insert a single line of text into the svg under node. + - use 'text' type and label as anootation + - where color is Ponoko Orange - so ignored when lasercutting + """ + line_style = {'font-size': '%dpx' % text_height, 'font-style':'normal', 'font-weight': 'normal', + 'fill': '#F6921E', 'font-family': 'Bitstream Vera Sans,sans-serif', + 'text-anchor': 'middle', 'text-align': 'center'} + line_attribs = {inkex.addNS('label','inkscape'): 'Annotation', + 'style': str(inkex.Style(line_style)), + 'x': str(position[0]), + 'y': str((position[1] + text_height) * 1.2) + } + line = etree.SubElement(node, inkex.addNS('text','svg'), line_attribs) + line.text = text + + + def calc_unit_factor(self): + """ return the scale factor for all dimension conversions. + - The document units are always irrelevant as + everything in inkscape is expected to be in 90dpi pixel units + """ + # namedView = self.document.getroot().find(inkex.addNS('namedview', 'sodipodi')) + # doc_units = uutounit(self, 1.0, namedView.get(inkex.addNS('document-units', 'inkscape'))) + dialog_units = uutounit(self, 1.0, self.options.units) + unit_factor = 1.0 / dialog_units + return unit_factor + + def calc_circular_pitch(self): + """ We use math based on circular pitch. + Expressed in inkscape units which is 90dpi 'pixel' units. + """ + dimension = self.options.dimension + # print >> self.tty, "unit_factor=%s, doc_units=%s, dialog_units=%s (%s), system=%s" % (unit_factor, doc_units, dialog_units, self.options.units, self.options.system) + if self.options.system == 'CP': # circular pitch + circular_pitch = dimension + elif self.options.system == 'DP': # diametral pitch + circular_pitch = pi / dimension + elif self.options.system == 'MM': # module (metric) + circular_pitch = dimension * pi / 25.4 + else: + inkex.utils.debug("unknown system '%s', try CP, DP, MM" % self.options.system) + # circular_pitch defines the size in inches. + # We divide the internal inch factor (px = 90dpi), to remove the inch + # unit. + # The internal inkscape unit is always px, + # it is independent of the doc_units! + return circular_pitch / uutounit(self, 1.0, 'in') + + + + def effect(self): + """ Calculate Gear factors from inputs. + - Make list of radii, angles, and centers for each tooth and + iterate through them + - Turn on other visual features e.g. cross, rack, annotations, etc + """ + path_stroke = '#000000' # might expose one day + path_fill = 'none' # no fill - just a line + path_stroke_width = uutounit(self, 0.1, 'mm') # might expose one day + path_stroke_light = uutounit(self, 0.05, 'mm') # guides are thinner + # + warnings = [] # list of extra messages to be shown in annotations + # calculate unit factor for units defined in dialog. + unit_factor = self.calc_unit_factor() + # User defined options + teeth = self.options.teeth + # Angle of tangent to tooth at circular pitch wrt radial line. + angle = self.options.angle + # Clearance: Radial distance between top of tooth on one gear to + # bottom of gap on another. + clearance = self.options.clearance * unit_factor + mount_hole = self.options.mount_hole * unit_factor + # for spokes + mount_radius = self.options.mount_diameter * 0.5 * unit_factor + spoke_count = self.options.spoke_count + spoke_width = self.options.spoke_width * unit_factor + holes_rounding = self.options.holes_rounding * unit_factor # unused + # visible guide lines + centercross = self.options.centercross # draw center or not (boolean) + pitchcircle = self.options.pitchcircle # draw pitch circle or not (boolean) + # Accuracy of teeth curves + accuracy_involute = 20 # Number of points of the involute curve + accuracy_circular = 9 # Number of points on circular parts + if self.options.accuracy is not None: + if self.options.accuracy == 0: + # automatic + if teeth < 10: accuracy_involute = 20 + elif teeth < 30: accuracy_involute = 12 + else: accuracy_involute = 6 + else: + accuracy_involute = self.options.accuracy + accuracy_circular = max(3, int(accuracy_involute/2) - 1) # never less than three + # print >>self.tty, "accuracy_circular=%s accuracy_involute=%s" % (accuracy_circular, accuracy_involute) + # Pitch (circular pitch): Length of the arc from one tooth to the next) + # Pitch diameter: Diameter of pitch circle. + pitch = self.calc_circular_pitch() + # Replace section below with this call to get the combined gear_calculations() above + (pitch_radius, base_radius, addendum, dedendum, + outer_radius, root_radius, tooth) = gear_calculations(teeth, pitch, angle, clearance, self.options.internal_ring, self.options.profile_shift*0.01) + + # Detect Undercut of teeth +## undercut = int(ceil(undercut_min_teeth( angle ))) +## needs_undercut = teeth < undercut #? no longer needed ? + if have_undercut(teeth, angle, 1.0): + min_teeth = int(ceil(undercut_min_teeth(angle, 1.0))) + min_angle = undercut_min_angle(teeth, 1.0) + .1 + max_k = undercut_max_k(teeth, angle) + msg = "Undercut Warning: This gear (%d teeth) will not work well.\nTry tooth count of %d or more,\nor a pressure angle of %.1f [deg] or more,\nor try a profile shift of %d %%.\nOr other decent combinations." % (teeth, min_teeth, min_angle, int(100. * max_k) - 100.) + # alas annotation cannot handle the degree symbol. Also it ignore + # newlines. + # so split and make a list + warnings.extend(msg.split("\n")) + #if self.options.undercut_alert: + # inkex.utils.debug(msg) + #else: + # print >> self.tty, msg + + # All base calcs done. Start building gear + points = generate_spur_points(teeth, base_radius, pitch_radius, outer_radius, root_radius, accuracy_involute, accuracy_circular) + +## half_thick_angle = two_pi / (4.0 * teeth ) #?? = pi / (2.0 * teeth) +## pitch_to_base_angle = involute_intersect_angle( base_radius, pitch_radius ) +## pitch_to_outer_angle = involute_intersect_angle( base_radius, outer_radius ) - pitch_to_base_angle +## +## start_involute_radius = max(base_radius, root_radius) +## radii = linspace(start_involute_radius, outer_radius, accuracy_involute) +## angles = [involute_intersect_angle(base_radius, r) for r in radii] +## +## centers = [(x * two_pi / float( teeth) ) for x in range( teeth ) ] +## points = [] +## +## for c in centers: +## # Angles +## pitch1 = c - half_thick_angle +## base1 = pitch1 - pitch_to_base_angle +## offsetangles1 = [ base1 + x for x in angles] +## points1 = [ point_on_circle( radii[i], offsetangles1[i]) for i in range(0,len(radii)) ] +## +## pitch2 = c + half_thick_angle +## base2 = pitch2 + pitch_to_base_angle +## offsetangles2 = [ base2 - x for x in angles] +## points2 = [ point_on_circle( radii[i], offsetangles2[i]) for i in range(0,len(radii)) ] +## +## points_on_outer_radius = [ point_on_circle(outer_radius, x) for x in linspace(offsetangles1[-1], offsetangles2[-1], accuracy_circular) ] +## +## if root_radius > base_radius: +## pitch_to_root_angle = pitch_to_base_angle - involute_intersect_angle(base_radius, root_radius ) +## root1 = pitch1 - pitch_to_root_angle +## root2 = pitch2 + pitch_to_root_angle +## points_on_root = [point_on_circle (root_radius, x) for x in linspace(root2, root1+(two_pi/float(teeth)), accuracy_circular) ] +## p_tmp = points1 + points_on_outer_radius[1:-1] + points2[::-1] + points_on_root[1:-1] # [::-1] reverses list; [1:-1] removes first and last element +## else: +## points_on_root = [point_on_circle (root_radius, x) for x in linspace(base2, base1+(two_pi/float(teeth)), accuracy_circular) ] +## p_tmp = points1 + points_on_outer_radius[1:-1] + points2[::-1] + points_on_root # [::-1] reverses list +## +## points.extend( p_tmp ) + + path = points_to_svgd( points ) + bbox_center = points_to_bbox_center( points ) + + # Spokes (add to current path) + if not self.options.internal_ring: # only draw internals if spur gear + spokes_path, msg = generate_spokes_path(root_radius, spoke_width, spoke_count, mount_radius, mount_hole, + unit_factor, self.options.units) + warnings.extend(msg) + path += spokes_path + + # Draw mount hole + # A : rx,ry x-axis-rotation, large-arch-flag, sweepflag x,y + r = mount_hole / 2 + path += ( + "M %f,%f" % (0,r) + + "A %f,%f %s %s %s %f,%f" % (r,r, 0,0,0, 0,-r) + + "A %f,%f %s %s %s %f,%f" % (r,r, 0,0,0, 0,r) + ) + else: + # its a ring gear + # which only has an outer ring where width = spoke width + r = outer_radius + spoke_width + path += ( + "M %f,%f" % (0,r) + + "A %f,%f %s %s %s %f,%f" % (r,r, 0,0,0, 0,-r) + + "A %f,%f %s %s %s %f,%f" % (r,r, 0,0,0, 0,r) + ) + + # Embed gear in group to make animation easier: + # Translate group, Rotate path. + t = 'translate(' + str( self.svg.namedview.center[0] ) + ',' + str( self.svg.namedview.center[1] ) + ')' + g_attribs = { inkex.addNS('label','inkscape'):'Gear' + str( teeth ), + inkex.addNS('transform-center-x','inkscape'): str(-bbox_center[0]), + inkex.addNS('transform-center-y','inkscape'): str(-bbox_center[1]), + 'transform':t, + 'info':'N:'+str(teeth)+'; Pitch:'+ str(pitch) + '; Pressure Angle: '+str(angle) } + # add the group to the current layer + g = etree.SubElement(self.svg.get_current_layer(), 'g', g_attribs ) + + # Create gear path under top level group + style = { 'stroke': path_stroke, 'fill': path_fill, 'stroke-width': path_stroke_width } + gear_attribs = { 'style': str(inkex.Style(style)), 'd': path } + gear = etree.SubElement(g, inkex.addNS('path','svg'), gear_attribs ) + + # Add center + if centercross: + style = {'stroke': path_stroke, 'fill': path_fill, + 'stroke-width': path_stroke_light} + cs = str(pitch / 3) # centercross length + d = 'M-'+cs+',0L'+cs+',0M0,-'+cs+'L0,'+cs # 'M-10,0L10,0M0,-10L0,10' + center_attribs = {inkex.addNS('label', 'inkscape'): 'Center cross', + 'style': str(inkex.Style(style)), 'd': d} + center = etree.SubElement( + g, inkex.addNS('path', 'svg'), center_attribs) + + # Add pitch circle (for mating) + if pitchcircle: + style = { 'stroke': path_stroke, 'fill': path_fill, 'stroke-width': path_stroke_light } + draw_SVG_circle(g, pitch_radius, 0, 0, 'Pitch circle', style) + + # Add Rack (below) + if self.options.draw_rack: + rack_base_height = self.options.rack_base_height * unit_factor + tab_width = self.options.rack_base_tab * unit_factor + tooth_count = self.options.rack_teeth_length + (points, guide_path) = generate_rack_points(tooth_count, pitch, addendum, angle, + rack_base_height, tab_width, clearance, pitchcircle) + path = points_to_svgd(points) + # position below Gear, so that it meshes nicely + # xoff = 0 ## if teeth % 4 == 2. + # xoff = -0.5*pitch ## if teeth % 4 == 0. + # xoff = -0.75*pitch ## if teeth % 4 == 3. + # xoff = -0.25*pitch ## if teeth % 4 == 1. + xoff = (-0.5, -0.25, 0, -0.75)[teeth % 4] * pitch + t = 'translate(' + str( xoff ) + ',' + str( pitch_radius ) + ')' + g_attribs = { inkex.addNS('label', 'inkscape'): 'RackGear' + str(tooth_count), + 'transform': t } + rack = etree.SubElement(g, 'g', g_attribs) + + # Create SVG Path for gear + style = {'stroke': path_stroke, 'fill': 'none', 'stroke-width': path_stroke_width } + gear_attribs = { 'style': str(inkex.Style(style)), 'd': path } + gear = etree.SubElement( + rack, inkex.addNS('path', 'svg'), gear_attribs) + if guide_path is not None: + style2 = { 'stroke': path_stroke, 'fill': 'none', 'stroke-width': path_stroke_light } + gear_attribs2 = { 'style': str(inkex.Style(style2)), 'd': guide_path } + gear = etree.SubElement( + rack, inkex.addNS('path', 'svg'), gear_attribs2) + + + # Add Annotations (above) + if self.options.annotation: + outer_dia = outer_radius * 2 + if self.options.internal_ring: + outer_dia += 2 * spoke_width + notes = [] + notes.extend(warnings) + #notes.append('Document (%s) scale conversion = %2.4f' % (self.document.getroot().find(inkex.addNS('namedview', 'sodipodi')).get(inkex.addNS('document-units', 'inkscape')), unit_factor)) + notes.extend(['Teeth: %d CP: %2.4f(%s) ' % (teeth, pitch / unit_factor, self.options.units), + 'DP: %2.3f Module: %2.4f' % (pi / pitch * unit_factor, pitch / pi * 25.4), + 'Pressure Angle: %2.2f degrees' % (angle), + 'Pitch diameter: %2.3f %s' % (pitch_radius * 2 / unit_factor, self.options.units), + 'Outer diameter: %2.3f %s' % (outer_dia / unit_factor, self.options.units), + 'Base diameter: %2.3f %s' % (base_radius * 2 / unit_factor, self.options.units)#, + #'Addendum: %2.4f %s' % (addendum / unit_factor, self.options.units), + #'Dedendum: %2.4f %s' % (dedendum / unit_factor, self.options.units) + ]) + # text height relative to gear size. + # ranges from 10 to 22 over outer radius size 60 to 360 + text_height = max(10, min(10+(outer_dia-60)/24, 22)) + # position above + y = - outer_radius - (len(notes)+1) * text_height * 1.2 + for note in notes: + self.add_text(g, note, [0,y], text_height) + y += text_height * 1.2 + +if __name__ == '__main__': + Gears().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/gears/meta.json b/extensions/fablabchemnitz/gears/meta.json new file mode 100644 index 0000000..e457850 --- /dev/null +++ b/extensions/fablabchemnitz/gears/meta.json @@ -0,0 +1,24 @@ +[ + { + "name": "Gears", + "id": "fablabchemnitz.de.gears", + "path": "gears", + "dependent_extensions": null, + "original_name": "Gear-dev", + "original_id": "com.gihub.jnweiger.inkscape-gears-dev", + "license": "GNU GPL v2", + "license_url": "https://github.com/ssentinel/inkscape-gears-dev/blob/master/gears-dev.py", + "comment": "fork of https://github.com/jnweiger/inkscape-gears-dev", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/gears", + "fork_url": "https://github.com/ssentinel/inkscape-gears-dev", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Gears", + "inkscape_gallery_url": null, + "main_authors": [ + "github.com/ssentinel", + "github.com/Neon22", + "github.com/jnweiger", + "github.com/eggsactly", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/gears2/__pycache__/involute.cpython-310.pyc b/extensions/fablabchemnitz/gears2/__pycache__/involute.cpython-310.pyc new file mode 100644 index 0000000..c03873d Binary files /dev/null and b/extensions/fablabchemnitz/gears2/__pycache__/involute.cpython-310.pyc differ diff --git a/extensions/fablabchemnitz/gears2/gears2.inx b/extensions/fablabchemnitz/gears2/gears2.inx new file mode 100644 index 0000000..1ffd2c1 --- /dev/null +++ b/extensions/fablabchemnitz/gears2/gears2.inx @@ -0,0 +1,48 @@ + + + Gears2 + fablabchemnitz.de.gears2 + + + 24 + 20 + + + + + + 5 + + + + + + + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/gears2/gears2.py b/extensions/fablabchemnitz/gears2/gears2.py new file mode 100644 index 0000000..d996d9e --- /dev/null +++ b/extensions/fablabchemnitz/gears2/gears2.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +''' +Copyright (C) 2013 Matthew Dockrey (gfish @ cyphertext.net) + +Based on http://arc.id.au/GearDrawing.html by Dr A.R.Collins +And on the original gears.py by Aaron Spike and Tavmjong Bah +''' + +import inkex +from math import * +from lxml import etree + +from involute import * + +class Gears(inkex.EffectExtension): + + def add_arguments(self, pars): + pars.add_argument("--tab", default="Options", help="The tab selected when OK was pressed") + pars.add_argument("-t", "--teeth", type=int, default=24, help="Number of teeth") + pars.add_argument("-p", "--pressure_angle", type=float, default="20", help="Pressure angle") + pars.add_argument("-y", "--size_type", type=int, default="1", help="Size type (1 = module (mm), 2 = pitch diameter (inches), 3 = diametral pitch (inches)") + pars.add_argument("-s", "--size", type=float, default="5", help="Size") + pars.add_argument("-o", "--orientation", type=int, default="1", help="Gear orientation") + + def effect(self): + Z = self.options.teeth + phi = self.options.pressure_angle + size_type = self.options.size_type + size = self.options.size + orientation = self.options.orientation + + # Convert size to module (mm) if needed + if (size_type == 2): + # Pitch diameter + size = 25.4 * size / Z + elif (size_type == 3): + # Diametral pitch + size = 25.4 / size + + m = self.svg.unittouu(str(size) + "mm") + + if (orientation == 2): + svg = CreateInternalGear(m, Z, phi) + else: + svg = CreateExternalGear(m, Z, phi) + + # Insert as a new element + gear_style = { 'stroke': '#000000', + 'stroke-width': self.svg.unittouu(str(0.1) + "mm"), + 'fill': 'none' + } + g_attribs = {inkex.addNS('label','inkscape'): 'Gear ' + str(Z), + 'transform': 'translate(' + str( self.svg.namedview.center[0] ) + ',' + str( self.svg.namedview.center[1] ) + ')', + 'style' : str(inkex.Style(gear_style)), + 'd' : svg } + g = etree.SubElement(self.svg.get_current_layer(), inkex.addNS('path','svg'), g_attribs) + +if __name__ == '__main__': + Gears().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/gears2/involute.py b/extensions/fablabchemnitz/gears2/involute.py new file mode 100644 index 0000000..c781771 --- /dev/null +++ b/extensions/fablabchemnitz/gears2/involute.py @@ -0,0 +1,348 @@ +#!/usr/bin/env python3 +# Based on gearUtils-03.js by Dr A.R.Collins +# Latest version: + +# Calculation of Bezier coefficients for +# Higuchi et al. approximation to an involute. +# ref: YNU Digital Eng Lab Memorandum 05-1 + +from math import * + +def rotate(p, t): + return (p[0] * cos(t) - p[1] * sin(t), p[0] * sin(t) + p[1] * cos(t)) + +def SVG_move(p, t): + pp = rotate(p, t) + return 'M ' + str(pp[0]) + ',' + str(pp[1]) + '\n' + +def SVG_line(p, t): + pp = rotate(p, t) + return 'L ' + str(pp[0]) + ',' + str(pp[1]) + '\n' + +def SVG_circle(p, r, sweep, t): + pp = rotate(p, t) + return 'A ' + str(r) + ',' + str(r) + ' 0 0,' + str(sweep) + ' ' + str(pp[0]) + ',' + str(pp[1]) + '\n' + +def SVG_curve(p, c1, c2, t): + pp = rotate(p, t) + c1p = rotate(c1, t) + c2p = rotate(c2, t) + return 'C ' + str(pp[0]) + ',' + str(pp[1]) + ' ' + str(c1p[0]) + ',' + str(c1p[1]) + ' ' + str(c2p[0]) + ',' + str(c2p[1]) + '\n' + +def SVG_curve2(p1, c11, c12, p2, c21, c22, t): + p1p = rotate(p1, t) + c11p = rotate(c11, t) + c12p = rotate(c12, t) + p2p = rotate(p2, t) + c21p = rotate(c21, t) + c22p = rotate(c22, t) + return 'C ' + str(p1p[0]) + ',' + str(p1p[1]) + ' ' + str(c11p[0]) + ',' + str(c11p[1]) + ' ' + str(c12p[0]) + ',' + str(c12p[1]) + ' ' + str(p2p[0]) + ',' + str(p2p[1]) + ' ' + str(c21p[0]) + ',' + str(c21p[1]) + ' ' + str(c22p[0]) + ',' + str(c22p[1]) + '\n' + +def SVG_close(): + return 'Z\n' + +def genInvolutePolar(Rb, R): # Rb = base circle radius + # returns the involute angle as function of radius R. + return (sqrt(R*R - Rb*Rb) / Rb) - acos(Rb / R) + +def rotate(pt, rads): # rotate pt by rads radians about origin + sinA = sin(rads) + cosA = cos(rads) + return [pt[0] * cosA - pt[1] * sinA, + pt[0] * sinA + pt[1] * cosA] + +def toCartesian(radius, angle): # convert polar coords to cartesian + return [radius * cos(angle), radius * sin(angle)] + +def CreateExternalGear(m, Z, phi): + # ****** external gear specifications + addendum = m # distance from pitch circle to tip circle + dedendum = 1.25 * m # pitch circle to root, sets clearance + clearance = dedendum - addendum + + # Calculate radii + Rpitch = Z * m / 2 # pitch circle radius + Rb = Rpitch*cos(phi * pi / 180) # base circle radius + Ra = Rpitch + addendum # tip (addendum) circle radius + Rroot = Rpitch - dedendum # root circle radius + fRad = 1.5 * clearance # fillet radius, max 1.5*clearance + Rf = sqrt((Rroot + fRad) * (Rroot + fRad) - (fRad * fRad)) # radius at top of fillet + if (Rb < Rf): + Rf = Rroot + clearance + + # ****** calculate angles (all in radians) + pitchAngle = 2 * pi / Z # angle subtended by whole tooth (rads) + baseToPitchAngle = genInvolutePolar(Rb, Rpitch) + pitchToFilletAngle = baseToPitchAngle # profile starts at base circle + if (Rf > Rb): # start profile at top of fillet (if its greater) + pitchToFilletAngle -= genInvolutePolar(Rb, Rf) + + filletAngle = atan(fRad / (fRad + Rroot)) # radians + + # ****** generate Higuchi involute approximation + fe = 1 # fraction of profile length at end of approx + fs = 0.01 # fraction of length offset from base to avoid singularity + if (Rf > Rb): + fs = (Rf * Rf - Rb * Rb) / (Ra * Ra - Rb * Rb) # offset start to top of fillet + + # approximate in 2 sections, split 25% along the involute + fm = fs + (fe - fs) / 4 # fraction of length at junction (25% along profile) + dedBez = BezCoeffs(m, Z, phi, 3, fs, fm) + addBez = BezCoeffs(m, Z, phi, 3, fm, fe) + + dedInv = dedBez.involuteBezCoeffs() + addInv = addBez.involuteBezCoeffs() + + # join the 2 sets of coeffs (skip duplicate mid point) + inv = dedInv + addInv[1:] + + # create the back profile of tooth (mirror image) + invR = [0 for i in range(0, len(inv))] # involute profile along back of tooth + for i in range(0, len(inv)): + # rotate all points to put pitch point at y = 0 + pt = rotate(inv[i], -baseToPitchAngle - pitchAngle / 4) + inv[i] = pt + # generate the back of tooth profile nodes, mirror coords in X axis + invR[i] = [pt[0], -pt[1]] + + # ****** calculate section junction points R=back of tooth, Next=front of next tooth) + fillet = toCartesian(Rf, -pitchAngle / 4 - pitchToFilletAngle) # top of fillet + filletR = [fillet[0], -fillet[1]] # flip to make same point on back of tooth + rootR = toCartesian(Rroot, pitchAngle / 4 + pitchToFilletAngle + filletAngle) + rootNext = toCartesian(Rroot, 3 * pitchAngle / 4 - pitchToFilletAngle - filletAngle) + filletNext = rotate(fillet, pitchAngle) # top of fillet, front of next tooth + + # Draw the shapes in SVG + t_inc = 2.0 * pi / float(Z) + thetas = [(x * t_inc) for x in range(Z)] + + svg = SVG_move(fillet, 0) # start at top of fillet + + for theta in thetas: + if (Rf < Rb): + svg += SVG_line(inv[0], theta) # line from fillet up to base circle + + svg += SVG_curve2(inv[1], inv[2], inv[3], + inv[4], inv[5], inv[6], theta) + + svg += SVG_circle(invR[6], Ra, 1, theta) # arc across addendum circle + + # svg = SVG_move(invR[6]) # TEMP + svg += SVG_curve2(invR[5], invR[4], invR[3], + invR[2], invR[1], invR[0], theta) + + if (Rf < Rb): + svg += SVG_line(filletR, theta) # line down to topof fillet + + if (rootNext[1] > rootR[1]): # is there a section of root circle between fillets? + svg += SVG_circle(rootR, fRad, 0, theta) # back fillet + svg += SVG_circle(rootNext, Rroot, 1, theta) # root circle arc + + svg += SVG_circle(filletNext, fRad, 0, theta) + + svg += SVG_close() + + return svg + +def CreateInternalGear(m, Z, phi): + addendum = 0.6 * m # pitch circle to tip circle (ref G.M.Maitra) + dedendum = 1.25 * m # pitch circle to root radius, sets clearance + + # Calculate radii + Rpitch = Z * m / 2 # pitch radius + Rb = Rpitch * cos(phi * pi / 180) # base radius + Ra = Rpitch - addendum # addendum radius + Rroot = Rpitch + dedendum# root radius + clearance = 0.25 * m # gear dedendum - pinion addendum + Rf = Rroot - clearance # radius of top of fillet (end of profile) + fRad = 1.5 * clearance # fillet radius, 1 .. 1.5*clearance + + # ****** calculate subtended angles + pitchAngle = 2 * pi / Z # angle between teeth (rads) + baseToPitchAngle = genInvolutePolar(Rb, Rpitch) + tipToPitchAngle = baseToPitchAngle # profile starts from base circle + if (Ra > Rb): + tipToPitchAngle -= genInvolutePolar(Rb, Ra) # start profile from addendum + pitchToFilletAngle = genInvolutePolar(Rb, Rf) - baseToPitchAngle + filletAngle = 1.414 * clearance / Rf # to make fillet tangential to root + + # ****** generate Higuchi involute approximation + fe = 1 # fraction of involute length at end of approx (fillet circle) + fs = 0.01 # fraction of length offset from base to avoid singularity + if (Ra > Rb): + fs = (Ra*Ra - Rb*Rb) / (Rf*Rf - Rb*Rb) # start profile from addendum (tip circle) + + # approximate in 2 sections, split 25% along the profile + fm = fs + (fe - fs) / 4 + + addBez = BezCoeffs(m, Z, phi, 3, fs, fm) + dedBez = BezCoeffs(m, Z, phi, 3, fm, fe) + addInv = addBez.involuteBezCoeffs() + dedInv = dedBez.involuteBezCoeffs() + + # join the 2 sets of coeffs (skip duplicate mid point) + invR = addInv + dedInv[1:] + + # create the front profile of tooth (mirror image) + inv = [0 for i in range(0, len(invR))] # back involute profile + for i in range(0, len(inv)): + # rotate involute to put center of tooth at y = 0 + pt = rotate(invR[i], pitchAngle / 4 - baseToPitchAngle) + invR[i] = pt + # generate the back of tooth profile, flip Y coords + inv[i] = [pt[0], -pt[1]] + + # ****** calculate coords of section junctions + fillet = [inv[6][0], inv[6][1]] # top of fillet, front of tooth + tip = toCartesian(Ra, -pitchAngle / 4 + tipToPitchAngle) # tip, front of tooth + tipR = [tip[0], -tip[1]] # addendum, back of tooth + rootR = toCartesian(Rroot, pitchAngle / 4 + pitchToFilletAngle + filletAngle) + rootNext = toCartesian(Rroot, 3 * pitchAngle / 4 - pitchToFilletAngle - filletAngle) + filletNext = rotate(fillet, pitchAngle) # top of fillet, front of next tooth + + # Draw the shapes in SVG + t_inc = 2.0 * pi / float(Z) + thetas = [(x * t_inc) for x in range(Z)] + + svg = SVG_move(fillet, 0) # start at top of fillet + + for theta in thetas: + svg += SVG_curve2(inv[5], inv[4], inv[3], + inv[2], inv[1], inv[0], theta) + + if (Ra < Rb): + svg += SVG_line(tip, theta) # line from end of involute to addendum (tip) + + svg += SVG_circle(tipR, Ra, 1, theta) # arc across tip circle + + if (Ra < Rb): + svg += SVG_line(invR[0], theta) # line from addendum to start of involute + + svg += SVG_curve2(invR[1], invR[2], invR[3], + invR[4], invR[5], invR[6], theta) + + if (rootR[1] < rootNext[1]): # there is a section of root circle between fillets + svg += SVG_circle(rootR, fRad, 1, theta) # fillet on back of tooth + + svg += SVG_circle(rootNext, Rroot, 1, theta) # root circle arc + + svg += SVG_circle(filletNext, fRad, 1, theta) # fillet on next + + return svg + +class BezCoeffs: + def chebyExpnCoeffs(self, j, func): + N = 50 # a suitably large number N>>p + c = 0 + for k in range(1, N + 1): + c += func(cos(pi * (k - 0.5) / N)) * cos(pi * j * (k - 0.5) / N) + + return 2 *c / N + + def chebyPolyCoeffs(self, p, func): + coeffs = [0, 0, 0, 0] + fnCoeff = [0, 0, 0, 0] + T = [[1, 0, 0, 0, 0], + [0, 1, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0] + ] + + # now generate the Chebyshev polynomial coefficient using + # formula T(k+1) = 2xT(k) - T(k-1) which yields + # T = [ [ 1, 0, 0, 0, 0, 0], # T0(x) = +1 + # [ 0, 1, 0, 0, 0, 0], # T1(x) = 0 +x + # [-1, 0, 2, 0, 0, 0], # T2(x) = -1 0 +2xx + # [ 0, -3, 0, 4, 0, 0], # T3(x) = 0 -3x 0 +4xxx + # [ 1, 0, -8, 0, 8, 0], # T4(x) = +1 0 -8xx 0 +8xxxx + # [ 0, 5, 0,-20, 0, 16], # T5(x) = 0 5x 0 -20xxx 0 +16xxxxx + # ... ] + + for k in range(1, p + 1): + for j in range(0, len(T[k]) - 1): + T[k + 1][j + 1] = 2 * T[k][j] + for j in range(0, len(T[k - 1])): + T[k + 1][j] -= T[k - 1][j] + + # convert the chebyshev function series into a simple polynomial + # and collect like terms, out T polynomial coefficients + for k in range(0, p + 1): + fnCoeff[k] = self.chebyExpnCoeffs(k, func) + coeffs[k] = 0 + + for k in range(0, p + 1): + for pwr in range(0, p + 1): + coeffs[pwr] += fnCoeff[k] * T[k][pwr] + + coeffs[0] -= self.chebyExpnCoeffs(0, func) / 2 # fix the 0th coeff + + return coeffs + + # Equation of involute using the Bezier parameter t as variable + def involuteXbez(self, t): + # map t (0 <= t <= 1) onto x (where -1 <= x <= 1) + x = t * 2 - 1 + # map theta (where ts <= theta <= te) from x (-1 <=x <= 1) + theta = x * (self.te - self.ts) / 2 + (self.ts + self.te) / 2 + return self.Rb * (cos(theta) + theta * sin(theta)) + + def involuteYbez(self, t): + # map t (0 <= t <= 1) onto x (where -1 <= x <= 1) + x = t * 2 - 1 + # map theta (where ts <= theta <= te) from x (-1 <=x <= 1) + theta = x * (self.te - self.ts) / 2 + (self.ts + self.te) / 2 + return self.Rb * (sin(theta) - theta * cos(theta)) + + def binom(self, n, k): + coeff = 1 + for i in range(n - k + 1, n + 1): + coeff *= i + + for i in range(1, k + 1): + coeff /= i + + return coeff + + def bezCoeff(self, i, func): + # generate the polynomial coeffs in one go + polyCoeffs = self.chebyPolyCoeffs(self.p, func) + + bc = 0 + for j in range(0, i + 1): + bc += self.binom(i, j) * polyCoeffs[j] / self.binom(self.p, j) + + return bc + + def involuteBezCoeffs(self): + # calc Bezier coeffs + bzCoeffs = [] + for i in range(0, self.p + 1): + bcoeff = [0, 0] + bcoeff[0] = self.bezCoeff(i, self.involuteXbez) + bcoeff[1] = self.bezCoeff(i, self.involuteYbez) + bzCoeffs.append(bcoeff) + + return bzCoeffs + + # Parameters: + # module - sets the size of teeth (see gear design texts) + # numTeeth - number of teeth on the gear + # pressure angle - angle in degrees, usually 14.5 or 20 + # order - the order of the Bezier curve to be fitted [3, 4, 5, ..] + # fstart - fraction of distance along tooth profile to start + # fstop - fraction of distance along profile to stop + def __init__(self, module, numTeeth, pressureAngle, order, fstart, fstop): + self.Rpitch = module * numTeeth / 2 # pitch circle radius + self.phi = pressureAngle # pressure angle + self.Rb = self.Rpitch * cos(self.phi * pi / 180) # base circle radius + self.Ra = self.Rpitch + module # addendum radius (outer radius) + self.ta = sqrt(self.Ra * self.Ra - self.Rb * self.Rb) / self.Rb # involute angle at addendum + self.stop = fstop + + if (fstart < self.stop): + self.start = fstart + + self.te = sqrt(self.stop) * self.ta # involute angle, theta, at end of approx + self.ts = sqrt(self.start) * self.ta # involute angle, theta, at start of approx + self.p = order # order of Bezier approximation \ No newline at end of file diff --git a/extensions/fablabchemnitz/gears2/meta.json b/extensions/fablabchemnitz/gears2/meta.json new file mode 100644 index 0000000..6d94f01 --- /dev/null +++ b/extensions/fablabchemnitz/gears2/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Gears2", + "id": "fablabchemnitz.de.gears2", + "path": "gears2", + "dependent_extensions": null, + "original_name": "Gears2", + "original_id": "com.attoparsec.filter.gears", + "license": "GNU GPL v2", + "license_url": "https://gitlab.com/inkscape/extensions/-/blob/master/render_gears.py", + "comment": "", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/gears2", + "fork_url": "https://github.com/attoparsec/inkscape-extensions", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Gears2", + "inkscape_gallery_url": null, + "main_authors": [ + "github.com/attoparsec", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/grid_strip_creator/grid_strip_creator.inx b/extensions/fablabchemnitz/grid_strip_creator/grid_strip_creator.inx new file mode 100644 index 0000000..c7290ca --- /dev/null +++ b/extensions/fablabchemnitz/grid_strip_creator/grid_strip_creator.inx @@ -0,0 +1,32 @@ + + + Grid Strip Creator (qlocktwo) + fablabchemnitz.de.grid_strip_creator + 230.0 + 20.0 + 11 + 10 + false + 12.5 + 12.5 + 1.0 + 10.0 + false + false + + 3.0 + false + false + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/grid_strip_creator/grid_strip_creator.py b/extensions/fablabchemnitz/grid_strip_creator/grid_strip_creator.py new file mode 100644 index 0000000..eccabfa --- /dev/null +++ b/extensions/fablabchemnitz/grid_strip_creator/grid_strip_creator.py @@ -0,0 +1,345 @@ +#!/usr/bin/env python3 +''' +Grid Strip Creator v1.0 (30/11/2014) + + +Copyright (C) 2014 Thomas Gebert - tsgebert **AT** web.de + +## This basic extension allows you to automatically draw guides in inkscape for hexagons. + +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 xml.etree import ElementTree as ET +from lxml import etree + +def printDebug(string): + inkex.errormsg(string) + +class GridStripCreator(inkex.EffectExtension): + + def add_arguments(self, pars): + pars.add_argument('--length', type = float, default = 230.0, help = 'Length of strip') + pars.add_argument('--width', type = float, default = 20.0, help = 'Width of strip') + pars.add_argument('--cellheight', type = float, default = 12.5, help = 'height of cell') + pars.add_argument('--cellwidth', type = float, default = 12.5, help = 'Width of cell') + pars.add_argument('--scalecells', type = inkex.Boolean, default = False, help = 'Scale cells over length') + pars.add_argument('--cellnumx', type = int, default = 11, help = 'Number of cells x') + pars.add_argument('--cellnumy', type = int, default = 10, help = 'Number of cells y') + pars.add_argument('--notchdepth', type = float, default = 1.0, help = 'Depth of notch') + pars.add_argument('--notchwidth', type = float, default = 10.0, help = 'Width of notch') + pars.add_argument('--notchhorizontal', type = inkex.Boolean, default = False, help = 'Make notches on horizontal strip') + pars.add_argument('--notchvertical', type = inkex.Boolean, default = False, help = 'Make notches on vertical strip') + pars.add_argument('--notch2width', type = float, default = 3.0, help = 'Width of notch') + pars.add_argument('--notchxcorner', type = inkex.Boolean, default = False, help = 'Make notches on corner of horizontal strip') + pars.add_argument('--notchycorner', type = inkex.Boolean, default = False, help = 'Make notches on corner of vertical strip') + + def effect(self): + # Get access to main SVG document element and get its dimensions. + svg = self.document.getroot() + # getting the parent tag of the guide + nv = self.document.xpath('/svg:svg/sodipodi:namedview',namespaces=inkex.NSS)[0] + + documentUnits = inkex.addNS('document-units', 'inkscape') + # inkex.utils.debug(nv.get(documentUnits)) + uunits = nv.get(documentUnits) + message="Units="+uunits + # inkex.utils.debug(message) + + # Get script's options value. + stripwidth=self.svg.unittouu(str(self.options.width)+uunits) + striplength=self.svg.unittouu(str(self.options.length)+uunits) + + cellheight=self.svg.unittouu(str(self.options.cellheight)+uunits) + cellwidth=self.svg.unittouu(str(self.options.cellwidth)+uunits) + + scalecells=(self.options.scalecells) + + cellnumx=(self.options.cellnumx) + cellnumy=(self.options.cellnumy) + + notchdepth=self.svg.unittouu(str(self.options.notchdepth)+uunits) + notchwidth=self.svg.unittouu(str(self.options.notchwidth)+uunits) + + notchhorizontal=(self.options.notchhorizontal) + notchvertical=(self.options.notchvertical) + +# notch2depth=self.svg.unittouu(str(self.options.notch2depth)+uunits) + notch2width=self.svg.unittouu(str(self.options.notch2width)+uunits) + + notch2depth= stripwidth/2 + + notchxcorner=(self.options.notchxcorner) + notchycorner=(self.options.notchycorner) + + if scalecells: + cellwidth=(striplength-4*notch2width)/cellnumx + cellheight=(striplength-4*notch2width)/cellnumy + notchxcorner=False + notchycorner=False + + linewidth=self.svg.unittouu(str(0.2)+uunits) + + distx=(striplength-cellnumx*cellwidth)/2 + disty=(striplength-cellnumy*cellheight)/2 + + celldistx=(cellwidth-notchwidth)/2 + celldisty=(cellheight-notch2width)/2 + + # getting the width and height attributes of the canvas + width = float(self.svg.unittouu(svg.attrib['width'])) + height = float(self.svg.unittouu(svg.attrib['height'])) + + parent = self.svg.get_current_layer() + layername='' + if notchhorizontal: + layername=layername+'VLED ' + if notchvertical: + layername=layername+'HLED ' + + # Create a new layer + layer = etree.SubElement(svg,'g') + layer.set(inkex.addNS('label', 'inkscape'),layername+'Long strips') + layer.set(inkex.addNS('groupmode','inkscape'), 'layer') + + + grp_name = 'group_horizontal_strip_long' + grp_attribs = {inkex.addNS('label','inkscape'):grp_name} + grp = etree.SubElement(layer, 'g', grp_attribs) #the group to put everything in + + style = { 'stroke': '#000000', 'stroke-width':str(linewidth), 'fill': 'none' } + + for num in range(0,2): + pathstring='M '+str(1)+','+str(1)+' L ' + if notchxcorner: + pathstring+=str(stripwidth-2*notchdepth)+','+str(1) # Obere Querkante + pathstring+=' L '+str(stripwidth-2*notchdepth)+','+str(notchwidth) # Erste Kerbe aussen + pathstring+=' L '+str(stripwidth)+','+str(notchwidth) # Ausrueckung + else: + pathstring+=str(stripwidth)+','+str(1) + if notchhorizontal: + pathstring+=' L '+str(stripwidth)+','+str(distx) # Distance to corner + y=distx + for i in range(0,cellnumx): + pathstring+=' L '+str(stripwidth)+','+str(y+celldistx) # Abstand + pathstring+=' L '+str(stripwidth-notchdepth)+','+str(y+celldistx) # Einrueckung + pathstring+=' L '+str(stripwidth-notchdepth)+','+str(y+celldistx+notchwidth) # Kerbe + pathstring+=' L '+str(stripwidth)+','+str(y+celldistx+notchwidth) # Ausrueckung + pathstring+=' L '+str(stripwidth)+','+str(y+2*celldistx+notchwidth) # Abstand + y=y+2*celldistx+notchwidth + if notchxcorner: + pathstring+=' L '+str(stripwidth)+','+str(striplength-notchwidth) # Untere rechte Ecke + pathstring+=' L '+str(stripwidth-2*notchdepth)+','+str(striplength-notchwidth) # Untere rechte Ecke + pathstring+=' L '+str(stripwidth-2*notchdepth)+','+str(striplength) # Untere rechte Ecke + else: + pathstring+=' L '+str(stripwidth)+','+str(striplength) + pathstring+=' L '+str(1)+','+str(striplength) # Linke untere Ecke + + + + y=striplength-distx+notch2width/2 + + pathstring+=' L '+str(1)+','+str(y) # Distance to corner + pathstring+=' L '+str(notch2depth)+','+str(y) # Einrueckung + + + for i in range(0,cellnumx): + pathstring+=' L '+str(notch2depth)+','+str(y-notch2width) # Kerbe + pathstring+=' L '+str(1)+','+str(y-notch2width) # Ausrueckung + pathstring+=' L '+str(1)+','+str(y-notch2width-cellwidth+notch2width) # Abstand + pathstring+=' L '+str(notch2depth)+','+str(y-notch2width-cellwidth+notch2width) # Einrueckung + y=y-notch2width-cellwidth+notch2width + + pathstring+=' L '+str(notch2depth)+','+str(y-notch2width) # Kerbe + pathstring+=' L '+str(1)+','+str(y-notch2width) # Ausrueckung + + pathstring+=' L '+str(1)+','+str(1)+' z' + + strip_transform= 'rotate(' + str(90)+')' + strip_transform+=' translate('+str(stripwidth*num)+','+str(1)+')' + strip_attribs = {'style':str(inkex.Style(style)), + inkex.addNS('label','inkscape'):"strip horizontal long", + 'transform': strip_transform, + 'd':pathstring} + etree.SubElement(grp, inkex.addNS('path','svg'), strip_attribs ) + + + celldisty=(cellheight-notch2width-notchwidth)/2 + + grp_name = 'group_vertical_strip_long' + grp_attribs = {inkex.addNS('label','inkscape'):grp_name} + grp = etree.SubElement(layer, 'g', grp_attribs) #the group to put everything in + + + for num in range(0,2): + y=disty-notch2width/2 + pathstring='M '+str(1)+','+str(1) + if notchycorner: + pathstring+=' L '+str(stripwidth-2*notchdepth)+','+str(1) # Obere Querkante + pathstring+=' L '+str(stripwidth-2*notchdepth)+','+str(notchwidth) + pathstring+=' L '+str(stripwidth)+','+str(notchwidth) + else: + pathstring+=' L '+str(stripwidth)+','+str(1) + pathstring+=' L '+str(stripwidth)+','+str(y) # Distance to corner + + for i in range(0,cellnumy): + pathstring+=' L '+str(stripwidth-notch2depth)+','+str(y) # Einrueckung + pathstring+=' L '+str(stripwidth-notch2depth)+','+str(y+notch2width) # Kerbe + pathstring+=' L '+str(stripwidth)+','+str(y+notch2width) # Ausrueckung + if notchvertical: + pathstring+=' L '+str(stripwidth)+','+str(y+notch2width+celldisty) # Abstand + pathstring+=' L '+str(stripwidth-notchdepth)+','+str(y+notch2width+celldisty) # Einrueckung + pathstring+=' L '+str(stripwidth-notchdepth)+','+str(y+notch2width+celldisty+notchwidth) # Kerbe + pathstring+=' L '+str(stripwidth)+','+str(y+notch2width+celldisty+notchwidth) # Ausrueckung + pathstring+=' L '+str(stripwidth)+','+str(y+notch2width+2*celldisty+notchwidth) # Abstand + y=y+notch2width+2*celldisty+notchwidth + + + pathstring+=' L '+str(stripwidth-notch2depth)+','+str(y) # Einrueckung + pathstring+=' L '+str(stripwidth-notch2depth)+','+str(y+notch2width) # Kerbe + pathstring+=' L '+str(stripwidth)+','+str(y+notch2width) # Ausrueckung + + if notchycorner: + pathstring+=' L '+str(stripwidth)+','+str(striplength-notchwidth) # Untere rechte Ecke + pathstring+=' L '+str(stripwidth-2*notchdepth)+','+str(striplength-notchwidth) # Untere rechte Ecke + pathstring+=' L '+str(stripwidth-2*notchdepth)+','+str(striplength) # Untere rechte Ecke + else: + pathstring+=' L '+str(stripwidth)+','+str(striplength) + pathstring+=' L '+str(1)+','+str(striplength) # Linke untere Ecke + pathstring+=' L '+str(1)+','+str(1)+' z' + + strip_transform= 'translate('+str(num*stripwidth)+','+str(1)+')' + strip_attribs = {'style':str(inkex.Style(style)), + inkex.addNS('label','inkscape'):"strip vertical long", + 'transform': strip_transform, + 'd':pathstring} + etree.SubElement(grp, inkex.addNS('path','svg'), strip_attribs ) + + # Create a new layer + layer = etree.SubElement(svg,'g') + layer.set(inkex.addNS('label', 'inkscape'), layername+'Horizontal strips short') + layer.set(inkex.addNS('groupmode','inkscape'), 'layer') + + grp_name = 'group horizontal_strip_short' + grp_attribs = {inkex.addNS('label','inkscape'):grp_name} + grp = etree.SubElement(layer, 'g', grp_attribs) #the group to put everything in + striplength=cellnumx*cellwidth+4*notch2width + distx=(striplength-cellnumx*cellwidth)/2 + disty=(striplength-cellnumy*cellheight)/2 + + style = { 'stroke': '#000000', 'stroke-width':str(linewidth), 'fill': 'none' } + + for num in range(1,cellnumy): + + pathstring='M '+str(1)+','+str(1)+' L ' + pathstring+=str(stripwidth)+','+str(1) + if notchhorizontal: + pathstring+=' L '+str(stripwidth)+','+str(distx) # Distance to corner + y=distx + for i in range(0,cellnumx): + pathstring+=' L '+str(stripwidth)+','+str(y+celldistx) # Abstand + pathstring+=' L '+str(stripwidth-notchdepth)+','+str(y+celldistx) # Einrueckung + pathstring+=' L '+str(stripwidth-notchdepth)+','+str(y+celldistx+notchwidth) # Kerbe + pathstring+=' L '+str(stripwidth)+','+str(y+celldistx+notchwidth) # Ausrueckung + pathstring+=' L '+str(stripwidth)+','+str(y+2*celldistx+notchwidth) # Abstand + y=y+2*celldistx+notchwidth + pathstring+=' L '+str(stripwidth)+','+str(striplength) + pathstring+=' L '+str(1)+','+str(striplength) # Linke untere Ecke + + y=striplength-distx+notch2width/2 + + pathstring+=' L '+str(1)+','+str(y) # Distance to corner + pathstring+=' L '+str(notch2depth)+','+str(y) # Einrueckung + + for i in range(0,cellnumx): + pathstring+=' L '+str(notch2depth)+','+str(y-notch2width) # Kerbe + pathstring+=' L '+str(1)+','+str(y-notch2width) # Ausrueckung + pathstring+=' L '+str(1)+','+str(y-notch2width-cellwidth+notch2width) # Abstand + pathstring+=' L '+str(notch2depth)+','+str(y-notch2width-cellwidth+notch2width) # Einrueckung + y=y-notch2width-cellwidth+notch2width + + pathstring+=' L '+str(notch2depth)+','+str(y-notch2width) # Kerbe + pathstring+=' L '+str(1)+','+str(y-notch2width) # Ausrueckung + + pathstring+=' L '+str(1)+','+str(1)+' z' + + strip_transform='rotate(' + str(90)+')' + strip_transform+=' translate('+str((num+1)*stripwidth+2)+','+str(1)+')' + stripname="strip horizontal short"+str(num) + strip_attribs = {'style':str(inkex.Style(style)), + inkex.addNS('label','inkscape'):stripname, + 'transform': strip_transform, + 'd':pathstring} + etree.SubElement(grp, inkex.addNS('path','svg'), strip_attribs ) + + + # Create a new layer + layer = etree.SubElement(svg,'g') + layer.set(inkex.addNS('label', 'inkscape'), layername+'Vertical strips short') + layer.set(inkex.addNS('groupmode','inkscape'), 'layer') + + grp_name = 'group vertical_strip_short' + grp_attribs = {inkex.addNS('label','inkscape'):grp_name} + grp = etree.SubElement(layer, 'g', grp_attribs) #the group to put everything in + + striplength=cellnumx*cellwidth+4*notch2width + distx=(striplength-cellnumx*cellwidth)/2 + disty=(striplength-cellnumy*cellheight)/2 + + striplength=cellnumy*cellheight+4*notch2width + distx=(striplength-cellnumx*cellwidth)/2 + disty=(striplength-cellnumy*cellheight)/2 + + celldisty=(cellheight-notch2width-notchwidth)/2 + + for num in range(1,cellnumx): + y=disty-notch2width/2 + pathstring='M '+str(1)+','+str(1) + pathstring+=' L '+str(stripwidth)+','+str(1) + pathstring+=' L '+str(stripwidth)+','+str(y) # Distance to corner + + for i in range(0,cellnumy): + pathstring+=' L '+str(stripwidth-notch2depth)+','+str(y) # Einrueckung + pathstring+=' L '+str(stripwidth-notch2depth)+','+str(y+notch2width) # Kerbe + pathstring+=' L '+str(stripwidth)+','+str(y+notch2width) # Ausrueckung + if notchvertical: + pathstring+=' L '+str(stripwidth)+','+str(y+notch2width+celldisty) # Abstand + pathstring+=' L '+str(stripwidth-notchdepth)+','+str(y+notch2width+celldisty) # Einrueckung + pathstring+=' L '+str(stripwidth-notchdepth)+','+str(y+notch2width+celldisty+notchwidth) # Kerbe + pathstring+=' L '+str(stripwidth)+','+str(y+notch2width+celldisty+notchwidth) # Ausrueckung + pathstring+=' L '+str(stripwidth)+','+str(y+notch2width+2*celldisty+notchwidth) # Abstand + y=y+notch2width+2*celldisty+notchwidth + + + pathstring+=' L '+str(stripwidth-notch2depth)+','+str(y) # Einrueckung + pathstring+=' L '+str(stripwidth-notch2depth)+','+str(y+notch2width) # Kerbe + pathstring+=' L '+str(stripwidth)+','+str(y+notch2width) # Ausrueckung + + pathstring+=' L '+str(stripwidth)+','+str(striplength) + pathstring+=' L '+str(1)+','+str(striplength) # Linke untere Ecke + pathstring+=' L '+str(1)+','+str(1)+' z' + + + strip_transform= 'translate('+str((num+1)*stripwidth+10)+','+str(1)+')' + stripname="strip vertical short"+str(num) + strip_attribs = {'style':str(inkex.Style(style)), + inkex.addNS('label','inkscape'):stripname, + 'transform': strip_transform, + 'd':pathstring} + etree.SubElement(grp, inkex.addNS('path','svg'), strip_attribs ) + +if __name__ == '__main__': + GridStripCreator().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/grid_strip_creator/meta.json b/extensions/fablabchemnitz/grid_strip_creator/meta.json new file mode 100644 index 0000000..22e7cf4 --- /dev/null +++ b/extensions/fablabchemnitz/grid_strip_creator/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Grid Strip Creator (qlocktwo)", + "id": "fablabchemnitz.de.grid_strip_creator", + "path": "grid_strip_creator", + "dependent_extensions": null, + "original_name": "Grid strip creator", + "original_id": "org.inkscape.effect.gridstripcreator", + "license": "GNU GPL v2", + "license_url": "https://github.com/fablabnbg/inkscape-gridstripcreator/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/grid_strip_creator", + "fork_url": "https://github.com/fablabnbg/inkscape-gridstripcreator", + "documentation_url": "https://stadtfabrikanten.org/pages/viewpage.action?pageId=55018608", + "inkscape_gallery_url": null, + "main_authors": [ + "github.com/fablabnbg", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/hatch_fill/hatch_fill.inx b/extensions/fablabchemnitz/hatch_fill/hatch_fill.inx new file mode 100644 index 0000000..a1683d5 --- /dev/null +++ b/extensions/fablabchemnitz/hatch_fill/hatch_fill.inx @@ -0,0 +1,47 @@ + + + Hatch Fill + fablabchemnitz.de.hatch_fill + + + + 3.0 + 45 + false + true + 3.0 + true + 1.0 + 3.0 + + + + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/hatch_fill/hatch_fill.py b/extensions/fablabchemnitz/hatch_fill/hatch_fill.py new file mode 100644 index 0000000..ff7218f --- /dev/null +++ b/extensions/fablabchemnitz/hatch_fill/hatch_fill.py @@ -0,0 +1,2253 @@ +#!/usr/bin/env python3 +# coding=utf-8 +# HatchFill.py +# +# Generate hatch fills for the closed paths (polygons) in the currently +# selected document elements. If no elements are selected, then all the +# polygons throughout the document are hatched. The fill rule is an odd/even +# rule: odd numbered intersections (1, 3, 5, etc.) are a hatch line entering +# a polygon while even numbered intersections (2, 4, 6, etc.) are the same +# hatch line exiting the polygon. +# +# This extension first decomposes the selected , , , +# , , , and elements into individual +# moveto and lineto coordinates using the same procedure that eggbot.py uses +# for plotting. These coordinates are then used to build vertex lists. +# Only the vertex lists corresponding to polygons (closed paths) are +# kept. Note that a single graphical element may be composed of several +# subpaths, each subpath potentially a polygon. +# +# Once the lists of all the vertices are built, potential hatch lines are +# "projected" through the bounding box containing all of the vertices. +# For each potential hatch line, all intersections with all the polygon +# edges are determined. These intersections are stored as decimal fractions +# indicating where along the length of the hatch line the intersection +# occurs. These values will always be in the range [0, 1]. A value of 0 +# indicates that the intersection is at the start of the hatch line, a value +# of 0.5 midway, and a value of 1 at the end of the hatch line. +# +# For a given hatch line, all the fractional values are sorted and any +# duplicates removed. Duplicates occur, for instance, when the hatch +# line passes through a polygon vertex and thus intersects two edges +# segments of the polygon: the end of one edge and the start of +# another. +# +# Once sorted and duplicates removed, an odd/even rule is applied to +# determine which segments of the potential hatch line are within +# polygons. These segments found to be within polygons are then saved +# and become the hatch fill lines which will be drawn. +# +# With each saved hatch fill line, information about which SVG graphical +# element it is within is saved. This way, the hatch fill lines can +# later be grouped with the element they are associated with. This makes +# it possible to manipulate the two -- graphical element and hatch lines -- +# as a single object within Inkscape. +# +# Note: we also save the transformation matrix for each graphical element. +# That way, when we group the hatch fills with the element they are +# filling, we can invert the transformation. That is, in order to compute +# the hatch fills, we first have had apply ALL applicable transforms to +# all the graphical elements. We need to do that so that we know where in +# the drawing each of the graphical elements are relative to one another. +# However, this means that the hatch lines have been computed in a setting +# where no further transforms are needed. If we then put these hatch lines +# into the same groups as the elements being hatched in the ORIGINAL +# drawing, then the hatch lines will have transforms applied again. So, +# once we compute the hatch lines, we need to invert the transforms of +# the group they will be placed in and apply this inverse transform to the +# hatch lines. Hence the need to save the transform matrix for every +# graphical element. +# Written by Daniel C. Newman for the Eggbot Project +# dan dot newman at mtbaldy dot us +# Updated by Windell H. Oskay, 6/14/2012 +# Added tolerance parameter +# Update by Daniel C. Newman, 6/20/2012 +# Add min span/gap width +# Updated by Windell H. Oskay, 1/8/2016 +# Added live preview and correct issue with nonzero min gap +# https://github.com/evil-mad/EggBot/issues/32 +# Updated by Sheldon B. Michaels, 1/11/2016 thru 3/15/2016 +# shel at shel dot net +# Added feature: Option to inset the hatch segments from boundaries +# Added feature: Option to join hatch segments that are "nearby", to minimize pen lifts +# The joins are made using cubic Bezier segments. +# https://github.com/evil-mad/EggBot/issues/36 +# Updated by Nathan Depew, 12/6/2017 +# Modified hatch fill to create hatches as a relevant object it found on the SVG tree +# This prevents extremely complex plots from generating glitches +# Modifications are limited to recursivelyTraverseSvg and effect methods +# +# Forked July 2020 +# Updated for Inkscape v1.0 +# Updated code to remove deprecation warnings +# Added script to format xml & Python. +# Not tested on Python 2. +# Current software version: +# (v0.9.0b, July 2020) # forked from Evil-Mad EggBot (v2.3.2, September 29, 2019) +# +# 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 +from lxml import etree +import inkex +from inkex import Transform +from inkex.paths import Path, CubicSuperPath + +N_PAGE_WIDTH = 3200 +N_PAGE_HEIGHT = 800 + +F_MINGAP_SMALL_VALUE = 0.0000000001 +# Was 0.00001 in the original version which did not have joined lines. +# Reducing this by a factor of 10^5 decreased probability of occurrence of +# the bug in the original, which got confused when the path barely +# grazed a corner. + +BEZIER_OVERSHOOT_MULTIPLIER = 0.75 # evaluation of cubic Bezier curve equation value, +# at x = 0, with +# endpoints at ( -0.5, 0 ), ( +0.5, 0 ) +# and control points at ( -0.5, 1.0 ), ( +0.5, 1.0 ) + +RADIAN_TOLERANCE_FOR_COLINEAR = 0.1 +# Pragmatically adjusted to allow adjacent segments from the same scan line, even short ones, +# to be classified as having the same angle + +RADIAN_TOLERANCE_FOR_ALTERNATING_DIRECTION = 0.1 +# Pragmatic adjustment again, as with colinearity tolerance + +RECURSION_LIMIT = 500 +# Pragmatic - if too high, risk runtime python error; +# if too low, miss some chances for reducing pen lifts + +EXTREME_POS = 1.0e70 # Extremely large positive number +EXTREME_NEG = -1.0e70 # Extremely large negative number + +MIN_HATCH_FRACTION = 0.25 +# Minimum hatch length, as a fraction of the hatch spacing. + +""" +Geometry 101: Determining if two lines intersect + +A line L is defined by two points in space P1 and P2. Any point P on the +line L satisfies + + P = P1 + s (P2 - P1) + +for some value of the real number s in the range (-infinity, infinity). +If we confine s to the range [0, 1] then we've described the line segment +with end points P1 and P2. + +Consider now the line La defined by the points P1 and P2, and the line Lb +defined by the points P3 and P4. Any points Pa and Pb on the lines La and +Lb therefore satisfy + + Pa = P1 + sa (P2 - P1) + Pb = P3 + sb (P4 - P3) + +for some values of the real numbers sa and sb. To see if these two lines +La and Lb intersect, we wish to see if there are finite values sa and sb +for which + + Pa = Pb + +Or, equivalently, we ask if there exists values of sa and sb for which +the equation + + P1 + sa (P2 - P1) = P3 + sb (P4 - P3) + +holds. If we confine ourselves to a two-dimensional plane, and take + + P1 = (x1, y1) + P2 = (x2, y2) + P3 = (x3, y3) + P4 = (x4, y4) + +we then find that we have two equations in two unknowns, sa and sb, + + x1 + sa ( x2 - x1 ) = x3 + sb ( x4 - x3 ) + y1 + sa ( y2 - y1 ) = y3 + sb ( y4 - y3 ) + +Solving these two equations for sa and sb yields, + + sa = [ ( y1 - y3 ) ( x4 - x3 ) - ( y4 - y3 ) ( x1 - x3 ) ] / d + sb = [ ( y1 - y3 ) ( x2 - x1 ) - ( y2 - y1 ) ( x1 - x3 ) ] / d + +where the denominator, d, is given by + + d = ( y4 - y3 ) ( x2 - x1 ) - ( y2 - y1 ) ( x4 - x3 ) + +Substituting these back for the point (x, y) of intersection gives + + x = x1 + sa ( x2 - x1 ) + y = y1 + sa ( y2 - y1 ) + +Note that + +1. The lines are parallel when d = 0 +2. The lines are coincident d = 0 and the numerators for sa & sb are zero +3. For line segments, sa and sb are in the range [0, 1]; any value outside + that range indicates that the line segments do not intersect. +""" + + +def intersect(p1, p2, p3, p4): + """ + Determine if two line segments defined by the four points p1 & p2 and + p3 & p4 intersect. If they do intersect, then return the fractional + point of intersection "sa" along the first line at which the + intersection occurs. + """ + + # Precompute these values -- note that we're basically shifting from + # + # p = p1 + s (p2 - p1) + # + # to + # + # p = p1 + s d + # + # where D is a direction vector. The solution remains the same of + # course. We'll just be computing D once for each line rather than + # computing it a couple of times. + + d21x = p2[0] - p1[0] + d21y = p2[1] - p1[1] + d43x = p4[0] - p3[0] + d43y = p4[1] - p3[1] + + # Denominator + d = d21x * d43y - d21y * d43x + + # Return now if the denominator is zero + if d == 0: + return -1.0 + + # For our purposes, the first line segment given + # by p1 & p2 is the LONG hatch line running through + # the entire drawing. And, p3 & p4 describe the + # usually much shorter line segment from a polygon. + # As such, we compute sb first as it's more likely + # to indicate "no intersection". That is, sa is + # more likely to indicate an intersection with a + # much a long line containing p3 & p4. + + nb = (p1[1] - p3[1]) * d21x - (p1[0] - p3[0]) * d21y + + # Could first check if abs(nb) > abs(d) or if + # the signs differ. + sb = float(nb) / float(d) + if sb < 0 or sb > 1: + return -1.0 + + na = (p1[1] - p3[1]) * d43x - (p1[0] - p3[0]) * d43y + sa = float(na) / float(d) + if sa < 0 or sa > 1: + return -1.0 + + return sa + + +def interstices(self, p1, p2, paths, hatches, b_hold_back_hatches, f_hold_back_steps): + """ + For the line L defined by the points p1 & p2, determine the segments + of L which lie within the polygons described by the paths stored in + "paths" + + p1 -- (x,y) coordinate [list] + p2 -- (x,y) coordinate [list] + paths -- Dictionary of all the paths to check for intersections + + When an intersection of the line L is found with a polygon edge, then + the fractional distance along the line L is saved along with the + lxml.etree node which contained the intersecting polygon edge. This + fractional distance is always in the range [0, 1]. + + Once all polygons have been checked, the list of fractional distances + corresponding to intersections is sorted and any duplicates removed. + It is then assumed that the first intersection is the line L entering + a polygon; the second intersection the line leaving the polygon. This + line segment defined by the first and second intersection points is + thus a hatch fill line we sought to generate. In general, our hatch + fills become the line segments described by intersection i and i+1 + with i an odd value (1, 3, 5, ...). Since we know the lxml.etree node + corresponding to each intersection, we can then correlate the hatch + fill lines to the graphical elements in the original SVG document. + This enables us to group hatch lines with the elements being hatched. + + The hatch line segments are returned by populating a dictionary. + The dictionary is keyed off of the lxml.etree node pointer. Each + dictionary value is a list of 4-tuples, + + (x1, y1, x2, y2) + + where (x1, y1) and (x2, y2) are the (x,y) coordinates of the line + segment's starting and ending points. + """ + + d_and_a = [] + # p1 & p2 is the hatch line + # p3 & p4 is the polygon edge to check + for path in paths: + for subpath in paths[path]: + p3 = subpath[0] + for p4 in subpath[1:]: + s = intersect(p1, p2, p3, p4) + if 0.0 <= s <= 1.0: + # Save this intersection point along the hatch line + if b_hold_back_hatches: + # We will need to know how the hatch meets the polygon segment, so that we can + # calculate the end of a shorter line that stops short + # of the polygon segment. + # We compute the angle now while we have the information required, + # but do _not_ apply it now, as we need the real,original, intersects + # for the odd/even inside/outside operations yet to come. + # Note that though the intersect() routine _could_ compute the join angle, + # we do it here because we go thru here much less often than we go thru intersect(). + angle_hatch_radians = math.atan2( + -(p2[1] - p1[1]), (p2[0] - p1[0]) + ) # from p1 toward p2, cartesian coordinates + angle_segment_radians = math.atan2( + -(p4[1] - p3[1]), (p4[0] - p3[0]) + ) # from p3 toward p4, cartesian coordinates + angle_difference_radians = ( + angle_hatch_radians - angle_segment_radians + ) + # coerce to range -pi to +pi + if angle_difference_radians > math.pi: + angle_difference_radians -= 2 * math.pi + elif angle_difference_radians < -math.pi: + angle_difference_radians += 2 * math.pi + f_sin_of_join_angle = math.sin(angle_difference_radians) + f_abs_sin_of_join_angle = abs(f_sin_of_join_angle) + if ( + f_abs_sin_of_join_angle != 0.0 + ): # Worrying about case of intersecting a segment parallel to the hatch + prelim_length_to_be_removed = ( + f_hold_back_steps / f_abs_sin_of_join_angle + ) + b_unconditionally_excise_hatch = False + else: + b_unconditionally_excise_hatch = True + + if not b_unconditionally_excise_hatch: + # The relevant end of the segment is the end from which the hatch approaches at an acute angle. + intersection = [0, 0] + intersection[0] = p1[0] + s * ( + p2[0] - p1[0] + ) # compute intersection point of hatch with segment + intersection[1] = p1[1] + s * ( + p2[1] - p1[1] + ) # intersecting hatch line starts at p1, vectored toward p2, + # but terminates at intersection + # Note that atan2 returns answer in range -pi to pi + # Which end is the approach end of the hatch to the segment? + # The dot product tells the answer: + # if dot product is positive, p2 is at the p4 end, + # else p2 is at the p3 end + # We really don't need to take the time to actually take + # the cosine of the angle, we are just interested in + # the quadrant within which the angle lies. + # I'm sure there is an elegant way to do this, but I'll settle for results just now. + # If the angle is in quadrants I or IV then p4 is the relevant end, otherwise p3 is + # nb: Y increases down, rather than up + # nb: difference angle has been forced to the range -pi to +pi + if abs(angle_difference_radians) < math.pi / 2: + # It's near the p3 the relevant end from which the hatch departs + dist_intersection_to_relevant_end = math.hypot( + p3[0] - intersection[0], p3[1] - intersection[1] + ) + dist_intersection_to_irrelevant_end = math.hypot( + p4[0] - intersection[0], p4[1] - intersection[1] + ) + else: + # It's near the p4 end from which the hatch departs + dist_intersection_to_relevant_end = math.hypot( + p4[0] - intersection[0], p4[1] - intersection[1] + ) + dist_intersection_to_irrelevant_end = math.hypot( + p3[0] - intersection[0], p3[1] - intersection[1] + ) + + # Now, the problem defined in issue 22 is that we may not need to remove the + # entire preliminary length we've calculated. This problem occurs because + # we have so far been considering the polygon segment as a line of infinite extent. + # Thus, we may be holding back at a point where no holdback is required, when + # calculated holdback is well beyond the position of the segment end. + + # To make matters worse, we do not currently know whether we're + # starting a hatch or terminating a hatch, because the duplicates have + # yet to be removed. All we can do then, is calculate the required + # line shortening for both possibilities - and then choose the correct + # one after duplicate-removal, when actually finalizing the hatches. + + # Let's see if either end, or perhaps both ends, has a case of excessive holdback + + # First, default assumption is that neither end has excessive holdback + length_remove_starting_hatch = prelim_length_to_be_removed + length_remove_ending_hatch = prelim_length_to_be_removed + + # Now check each of the two ends + if prelim_length_to_be_removed > ( + dist_intersection_to_relevant_end + f_hold_back_steps + ): + # Yes, would be excessive holdback approaching from this direction + length_remove_starting_hatch = ( + dist_intersection_to_relevant_end + + f_hold_back_steps + ) + if prelim_length_to_be_removed > ( + dist_intersection_to_irrelevant_end + f_hold_back_steps + ): + # Yes, would be excessive holdback approaching from other direction + length_remove_ending_hatch = ( + dist_intersection_to_irrelevant_end + + f_hold_back_steps + ) + + d_and_a.append( + ( + s, + path, + length_remove_starting_hatch, + length_remove_ending_hatch, + ) + ) + else: + d_and_a.append( + (s, path, 123456.0, 123456.0) + ) # Mark for complete hatch excision, hatch is parallel to segment + # Just a random number guaranteed large enough to be longer than any hatch length + else: + d_and_a.append( + (s, path, 0, 0) + ) # zero length to be removed from hatch + + p3 = p4 + + # Return now if there were no intersections + if len(d_and_a) == 0: + return None + + d_and_a.sort() + + # Remove duplicate intersections. A common case where these arise + # is when the hatch line passes through a vertex where one line segment + # ends and the next one begins. + + # Having sorted the data, it's trivial to just scan through + # removing duplicates as we go and then truncating the array + + n = len(d_and_a) + i_last = 1 + i = 1 + last = d_and_a[0] + while i < n: + if (abs(d_and_a[i][0] - last[0])) > F_MINGAP_SMALL_VALUE: + d_and_a[i_last] = last = d_and_a[i] + i_last += 1 + i += 1 + d_and_a = d_and_a[:i_last] + if len(d_and_a) < 2: + return + + # Now, entries with even valued indices into sa[] are where we start + # a hatch line and odd valued indices where we end the hatch line. + + i = 0 + while i < (len(d_and_a) - 1): + if d_and_a[i][1] not in hatches: + hatches[d_and_a[i][1]] = [] + + x1 = p1[0] + d_and_a[i][0] * (p2[0] - p1[0]) + y1 = p1[1] + d_and_a[i][0] * (p2[1] - p1[1]) + x2 = p1[0] + d_and_a[i + 1][0] * (p2[0] - p1[0]) + y2 = p1[1] + d_and_a[i + 1][0] * (p2[1] - p1[1]) + + # These are the hatch ends if we are _not_ holding off from the boundary. + if not b_hold_back_hatches: + hatches[d_and_a[i][1]].append([[x1, y1], [x2, y2]]) + else: + # User wants us to perform a pseudo inset operation. + # We will accomplish this by trimming back the ends of the hatches. + # The amount by which to trim back depends on the angle between the + # intersecting hatch line with the intersecting polygon segment, and + # may well be different at the two different ends of the hatch line. + + # To visualize this, imagine a hatch intersecting a segment that is + # close to parallel with it. The length of the hatch would have to be + # drastically reduced in order that its closest approach to the + # segment be reduced to the desired distance. + + # Imagine a Cartesian coordinate system, with the X axis representing the + # polygon segment, and a line running through the origin with a small + # positive slope being the intersecting hatch line. + + # We see that we want a Y value of the specified hatch width, and that + # at that Y, the distance from the origin to that point is the + # hypotenuse of the triangle. + # Y / cutlength = sin(angle) + # therefore: + # cutlength = Y / sin(angle) + # Fortunately, we have already stored this angle for exactly this purpose. + # For each end, trim back the hatch line by the amount required by + # its own angle. If the resultant diminished hatch is too short, + # remove it from consideration by marking it as already drawn - a + # fiction, but is much quicker than actually removing the hatch from the list. + + f_min_allowed_hatch_length = self.options.hatchSpacing * MIN_HATCH_FRACTION + f_initial_hatch_length = math.hypot(x2 - x1, y2 - y1) + # We did as much as possible of the inset operation back when we were finding intersections. + # We did it back then because at that point we knew more about the geometry than we know now. + # Now we don't know where the ends of the segments are, so we can't address issue 22 here. + f_length_to_be_removed_from_pt1 = d_and_a[i][3] + f_length_to_be_removed_from_pt2 = d_and_a[i + 1][2] + + if ( + f_initial_hatch_length + - (f_length_to_be_removed_from_pt1 + f_length_to_be_removed_from_pt2) + ) <= f_min_allowed_hatch_length: + pass # Just don't insert it into the hatch list + else: + """ + Use: + def RelativeControlPointPosition( self, distance, fDeltaX, fDeltaY, deltaX, deltaY ): + # returns the point, relative to 0, 0 offset by deltaX, deltaY, + # which extends a distance of "distance" at a slope defined by fDeltaX and fDeltaY + """ + pt1 = self.RelativeControlPointPosition( + f_length_to_be_removed_from_pt1, x2 - x1, y2 - y1, x1, y1 + ) + pt2 = self.RelativeControlPointPosition( + f_length_to_be_removed_from_pt2, x1 - x2, y1 - y2, x2, y2 + ) + hatches[d_and_a[i][1]].append([[pt1[0], pt1[1]], [pt2[0], pt2[1]]]) + + # Remember the relative start and end of this hatch segment + last_d_and_a = [d_and_a[i], d_and_a[i + 1]] + + i += 2 + + +def inverseTransform(tran): + """ + An SVG transform matrix looks like + + [ a c e ] + [ b d f ] + [ 0 0 1 ] + + And it's inverse is + + [ d -c cf - de ] + [ -b a be - af ] * ( ad - bc ) ** -1 + [ 0 0 1 ] + + And, no reasonable 2d coordinate transform will have + the products ad and bc equal. + + SVG represents the transform matrix column by column as + matrix(a b c d e f) while Inkscape extensions store the + transform matrix as + + [[a, c, e], [b, d, f]] + + To invert the transform stored Inkscape style, we wish to + produce + + [[d/D, -c/D, (cf - de)/D], [-b/D, a/D, (be-af)/D]] + + where + + D = 1 / (ad - bc) + """ + D = tran.a * tran.d - tran.b * tran.c + if D == 0: + return None + + return [ + [tran.d / D, -tran.c / D, (tran.c * tran.f - tran.d * tran.e) / D], + [-tran.b / D, tran.a / D, (tran.b * tran.e - tran.a * tran.f) / D], + ] + + +import inkex.bezier + + +def subdivideCubicPath(sp, flat, i=1): + """ + Break up a bezier curve into smaller curves, each of which + is approximately a straight line within a given tolerance + (the "smoothness" defined by [flat]). + + to avoid recurrence. + """ + + while True: + while True: + if i >= len(sp): + return + + p0 = sp[i - 1][1] + p1 = sp[i - 1][2] + p2 = sp[i][0] + p3 = sp[i][1] + + b = (p0, p1, p2, p3) + + if inkex.bezier.maxdist(b) > flat: + break + + i += 1 + + one, two = inkex.bezier.beziersplitatt(b, 0.5) + sp[i - 1][2] = one[1] + sp[i][0] = two[2] + p = [one[2], one[3], two[1]] + sp[i:1] = [p] + + +def distanceSquared(p1, p2): + """ + Pythagorean distance formula WITHOUT the square root. Since + we just want to know if the distance is less than some fixed + fudge factor, we can just square the fudge factor once and run + with it rather than compute square roots over and over. + """ + + dx = p2[0] - p1[0] + dy = p2[1] - p1[1] + + return dx * dx + dy * dy + + +class HatchFill(inkex.Effect): + def __init__(self): + + inkex.Effect.__init__(self) + + self.xmin, self.ymin = (0.0, 0.0) + self.xmax, self.ymax = (0.0, 0.0) + self.paths = {} + self.grid = [] + self.hatches = {} + self.transforms = {} + + # For handling an SVG viewbox attribute, we will need to know the + # values of the document's width and height attributes as well + # as establishing a transform from the viewbox to the display. + self.docWidth = float(N_PAGE_WIDTH) + self.docHeight = float(N_PAGE_HEIGHT) + self.docTransform = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]] + + self.arg_parser.add_argument( "--holdBackSteps", type=float, default=3.0, help="How far hatch strokes stay from boundary (steps)", ) + self.arg_parser.add_argument( "--hatchScope", type=float, default=3.0, help="Radius searched for segments to join (units of hatch width)", ) + self.arg_parser.add_argument( "--holdBackHatchFromEdges", type=inkex.Boolean, default=True, help="Stay away from edges, so no need for inset", ) + self.arg_parser.add_argument( "--reducePenLifts", type=inkex.Boolean, default=True, help="Reduce plotting time by joining some hatches", ) + self.arg_parser.add_argument( "--crossHatch", type=inkex.Boolean, default=False, help="Generate a cross hatch pattern", ) + self.arg_parser.add_argument( "--hatchAngle", type=float, default=90.0, help="Angle of inclination for hatch lines", ) + self.arg_parser.add_argument( "--hatchSpacing", type=float, default=10.0, help="Spacing between hatch lines", ) + self.arg_parser.add_argument( "--tolerance", type=float, default=20.0, help="Allowed deviation from original paths", ) + self.arg_parser.add_argument( "--tab", default="splash") + + def getDocProps(self): + + """ + Get the document's height and width attributes from the tag. + Use a default value in case the property is not present or is + expressed in units of percentages. + """ + + self.docHeight = getLength(self, "height", N_PAGE_HEIGHT) + self.docWidth = getLength(self, "width", N_PAGE_WIDTH) + + if self.docHeight is None or self.docWidth is None: + return False + else: + return True + + def handleViewBox(self): + + """ + Set up the document-wide transform in the event that the document has an SVG viewbox + """ + + if self.getDocProps(): + viewbox = self.document.getroot().get("viewBox") + if viewbox: + vinfo = viewbox.strip().replace(",", " ").split(" ") + if vinfo[2] != 0 and vinfo[3] != 0: + sx = self.docWidth / float(vinfo[2]) + sy = self.docHeight / float(vinfo[3]) + # self.docTransform = Transform('scale({0:f},{1:f})'.format(sx, sy)) + self.docTransform = Transform( + f"scale({sx}, {sy})" + ).matrix + + def addPathVertices(self, path, node=None, transform=None): + + """ + Decompose the path data from an SVG element into individual + subpaths, each starting with an absolute move-to (x, y) + coordinate followed by one or more absolute line-to (x, y) + coordinates. Each subpath is stored as a list of (x, y) + coordinates, with the first entry understood to be a + move-to coordinate and the rest line-to coordinates. A list + is then made of all the subpath lists and then stored in the + self.paths dictionary using the path's lxml.etree node pointer + as the dictionary key. + """ + + if not path or len(path) == 0: + return + + # parsePath() may raise an exception. This is okay + sp = inkex.paths.Path(path).to_arrays() + if not sp or len(sp) == 0: + return + + # Get a cubic super duper path + p = CubicSuperPath(sp) + if not p or len(p) == 0: + return + + # Apply any transformation + if transform is not None: + Path(p).transform(transform) + + # Now traverse the simplified path + subpaths = [] + subpath_vertices = [] + for sp in p: + # We've started a new subpath + # See if there is a prior subpath and whether we should keep it + if len(subpath_vertices): + if distanceSquared(subpath_vertices[0], subpath_vertices[-1]) < 1: + # Keep the prior subpath: it appears to be a closed path + subpaths.append(subpath_vertices) + subpath_vertices = [] + subdivideCubicPath(sp, float(self.options.tolerance / 100)) + for csp in sp: + # Add this vertex to the list of vertices + subpath_vertices.append(csp[1]) + + # Handle final subpath + if len(subpath_vertices): + if distanceSquared(subpath_vertices[0], subpath_vertices[-1]) < 1: + # Path appears to be closed so let's keep it + subpaths.append(subpath_vertices) + + # Empty path? + if len(subpaths) == 0: + return + + # And add this path to our dictionary of paths + self.paths[node] = subpaths + + # And save the transform for this element in a dictionary keyed + # by the element's lxml node pointer + self.transforms[node] = transform + + def getBoundingBox(self): + + """ + Determine the bounding box for our collection of polygons + """ + + self.xmin, self.xmax = EXTREME_POS, EXTREME_NEG + self.ymin, self.ymax = EXTREME_POS, EXTREME_NEG + for path in self.paths: + for subpath in self.paths[path]: + for vertex in subpath: + if vertex[0] < self.xmin: + self.xmin = vertex[0] + elif vertex[0] > self.xmax: + self.xmax = vertex[0] + if vertex[1] < self.ymin: + self.ymin = vertex[1] + elif vertex[1] > self.ymax: + self.ymax = vertex[1] + + def recursivelyTraverseSvg( + self, a_node_list, mat_current=None, parent_visibility="visible" + ): + """ + Recursively walk the SVG document, building polygon vertex lists + for each graphical element we support. + + Rendered SVG elements: + , , , , , , + + Supported SVG elements: + , + + Ignored SVG elements: + , , , , + + All other SVG elements trigger an error (including ) + + Once a supported graphical element is found, we call functions to + create a hatchfill specific to this element. These hatches and their + corresponding transforms are stored in self.hatches and self.transforms + These two dictionaries are used when we return to the effect method + in joinFillsWithNode() + + """ + if mat_current is None: + mat_current = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]] + for node in a_node_list: + + """ + Initialize dictionary for each new node + This allows us to create hatch fills as if each + object to be hatched has been selected individually + + """ + self.xmin, self.ymin = (0.0, 0.0) + self.xmax, self.ymax = (0.0, 0.0) + self.paths = {} + self.grid = [] + + # Ignore invisible nodes + v = node.get("visibility", parent_visibility) + if v == "inherit": + v = parent_visibility + if v == "hidden" or v == "collapse": + pass + + # first apply the current matrix transform to this node's transform + mat_new = Transform(mat_current) @ Transform(Transform(node.get("transform")).matrix) + + if node.tag in [inkex.addNS("g", "svg"), "g"]: + self.recursivelyTraverseSvg(node, mat_new, parent_visibility=v) + + elif node.tag in [inkex.addNS("use", "svg"), "use"]: + + # A element refers to another SVG element via an xlink:href="#blah" + # attribute. We will handle the element by doing an XPath search through + # the document, looking for the element with the matching id="blah" + # attribute. We then recursively process that element after applying + # any necessary (x,y) translation. + # + # Notes: + # 1. We ignore the height and width attributes as they do not apply to + # path-like elements, and + # 2. Even if the use element has visibility="hidden", SVG still calls + # for processing the referenced element. The referenced element is + # hidden only if its visibility is "inherit" or "hidden". + + refid = node.get(inkex.addNS("href", "xlink")) + + # [1:] to ignore leading '#' in reference + path = '//*[@id="{0}"]'.format(refid[1:]) + refnode = node.xpath(path) + if refnode: + x = float(node.get("x", "0")) + y = float(node.get("y", "0")) + # Note: the transform has already been applied + if x != 0 or y != 0: + mat_new2 = Transform(mat_new) @ Transform(Transform("translate({0:f},{1:f})".format(x, y))) + else: + mat_new2 = mat_new + v = node.get("visibility", v) + self.recursivelyTraverseSvg(refnode, mat_new2, parent_visibility=v) + + elif node.tag == inkex.addNS("path", "svg"): + + path_data = node.get("d") + if path_data: + self.addPathVertices(path_data, node, mat_new) + # We now have a path we want to apply a (cross)hatch to + # Apply appropriate functions + b_have_grid = self.makeHatchGrid( + float(self.options.hatchAngle), + float(self.options.hatchSpacing), + True, + ) + if b_have_grid: + if self.options.crossHatch: + self.makeHatchGrid( + float(self.options.hatchAngle + 90.0), + float(self.options.hatchSpacing), + False, + ) + # Now loop over our hatch lines looking for intersections + for h in self.grid: + interstices( + self, + (h[0], h[1]), + (h[2], h[3]), + self.paths, + self.hatches, + self.options.holdBackHatchFromEdges, + self.options.holdBackSteps, + ) + + elif node.tag in [inkex.addNS("rect", "svg"), "rect"]: + + # Manually transform + # + # + # + # into + # + # + # + # I.e., explicitly draw three sides of the rectangle and the + # fourth side implicitly + + # Create a path with the outline of the rectangle + x = float(node.get("x")) + y = float(node.get("y")) + + w = float(node.get("width", "0")) + h = float(node.get("height", "0")) + a = [ + ["M", [x, y]], + ["l", [w, 0]], + ["l", [0, h]], + ["l", [-w, 0]], + ["Z", []], + ] + ret = Path(a) + self.addPathVertices(ret, node, mat_new) + # We now have a path we want to apply a (cross)hatch to + # Apply appropriate functions + b_have_grid = self.makeHatchGrid( + float(self.options.hatchAngle), + float(self.options.hatchSpacing), + True, + ) + if b_have_grid: + if self.options.crossHatch: + self.makeHatchGrid( + float(self.options.hatchAngle + 90.0), + float(self.options.hatchSpacing), + False, + ) + # Now loop over our hatch lines looking for intersections + for h in self.grid: + interstices( + self, + (h[0], h[1]), + (h[2], h[3]), + self.paths, + self.hatches, + self.options.holdBackHatchFromEdges, + self.options.holdBackSteps, + ) + + elif node.tag in [inkex.addNS("line", "svg"), "line"]: + + # Convert + # + # + + x1 = float(node.get("x1")) + y1 = float(node.get("y1")) + x2 = float(node.get("x2")) + y2 = float(node.get("y2")) + + a = [ + ["M ", [x1, y1]], + [" L ", [x2, y2]], + ] + self.addPathVertices(Path(a), node, mat_new) + # We now have a path we want to apply a (cross)hatch to + # Apply appropriate functions + b_have_grid = self.makeHatchGrid( + float(self.options.hatchAngle), + float(self.options.hatchSpacing), + True, + ) + if b_have_grid: + if self.options.crossHatch: + self.makeHatchGrid( + float(self.options.hatchAngle + 90.0), + float(self.options.hatchSpacing), + False, + ) + # Now loop over our hatch lines looking for intersections + for h in self.grid: + interstices( + self, + (h[0], h[1]), + (h[2], h[3]), + self.paths, + self.hatches, + self.options.holdBackHatchFromEdges, + self.options.holdBackSteps, + ) + + elif node.tag in [inkex.addNS("polyline", "svg"), "polyline"]: + + # Convert + # + # + # + # to + # + # + # + # Note: we ignore polylines with no points + + pl = node.get("points", "").strip() + if pl == "": + continue + pa = pl.split() + if not pa: + continue + pathLength = len(pa) + if pathLength < 4: # Minimum of x1,y1 x2,y2 required. + continue + + d = "M " + pa[0] + " " + pa[1] + i = 2 + while i < (pathLength - 1): + d += " L " + pa[i] + " " + pa[i + 1] + i += 2 + + if d: + self.addPathVertices(d, node, mat_new) + + # We now have a path we want to apply a (cross)hatch to + # Apply appropriate functions + b_have_grid = self.makeHatchGrid( + float(self.options.hatchAngle), + float(self.options.hatchSpacing), + True, + ) + if b_have_grid: + if self.options.crossHatch: + self.makeHatchGrid( + float(self.options.hatchAngle + 90.0), + float(self.options.hatchSpacing), + False, + ) + # Now loop over our hatch lines looking for intersections + for h in self.grid: + interstices( + self, + (h[0], h[1]), + (h[2], h[3]), + self.paths, + self.hatches, + self.options.holdBackHatchFromEdges, + self.options.holdBackSteps, + ) + + elif node.tag in [inkex.addNS("polygon", "svg"), "polygon"]: + # Convert + # + # + # + # to + # + # + # + # Note: we ignore polygons with no points + + pl = node.get("points", "").strip() + + pa = pl.split() + d = "".join( + [ + "M " + pa[i] if i == 0 else " L " + pa[i] + for i in range(0, len(pa)) + ] + ) + d += " Z" + self.addPathVertices(d, node, mat_new) + # We now have a path we want to apply a (cross)hatch to + # Apply appropriate functions + b_have_grid = self.makeHatchGrid( + float(self.options.hatchAngle), + float(self.options.hatchSpacing), + True, + ) + if b_have_grid: + if self.options.crossHatch: + self.makeHatchGrid( + float(self.options.hatchAngle + 90.0), + float(self.options.hatchSpacing), + False, + ) + # Now loop over our hatch lines looking for intersections + for h in self.grid: + interstices( + self, + (h[0], h[1]), + (h[2], h[3]), + self.paths, + self.hatches, + self.options.holdBackHatchFromEdges, + self.options.holdBackSteps, + ) + + elif node.tag in [ + inkex.addNS("ellipse", "svg"), + "ellipse", + inkex.addNS("circle", "svg"), + "circle", + ]: + + # Convert circles and ellipses to a path with two 180 degree arcs. + # In general (an ellipse), we convert + # + # + # + # to + # + # + # + # where + # + # X1 = CX - RX + # X2 = CX + RX + # + # Note: ellipses or circles with a radius attribute of value 0 are ignored + + if node.tag in [inkex.addNS("ellipse", "svg"), "ellipse"]: + rx = float(node.get("rx", "0")) + ry = float(node.get("ry", "0")) + else: + rx = float(node.get("r", "0")) + ry = rx + + cx = float(node.get("cx", "0")) + cy = float(node.get("cy", "0")) + x1 = cx - rx + x2 = cx + rx + + d = ( + "M {x1:f},{cy:f} " + "A {rx:f},{ry:f} " + "0 1 0 {x2:f},{cy:f} " + "A {rx:f},{ry:f} " + "0 1 0 {x1:f},{cy:f}".format(x1=x1, x2=x2, rx=rx, ry=ry, cy=cy) + ) + self.addPathVertices(d, node, mat_new) + # We now have a path we want to apply a (cross)hatch to + # Apply appropriate functions + b_have_grid = self.makeHatchGrid( + float(self.options.hatchAngle), + float(self.options.hatchSpacing), + True, + ) + if b_have_grid: + if self.options.crossHatch: + self.makeHatchGrid( + float(self.options.hatchAngle + 90.0), + float(self.options.hatchSpacing), + False, + ) + # Now loop over our hatch lines looking for intersections + for h in self.grid: + interstices( + self, + (h[0], h[1]), + (h[2], h[3]), + self.paths, + self.hatches, + self.options.holdBackHatchFromEdges, + self.options.holdBackSteps, + ) + + elif node.tag in [inkex.addNS("pattern", "svg"), "pattern"]: + pass + elif node.tag in [inkex.addNS("metadata", "svg"), "metadata"]: + pass + elif node.tag in [inkex.addNS("defs", "svg"), "defs"]: + pass + elif node.tag in [inkex.addNS("namedview", "sodipodi"), "namedview"]: + pass + elif node.tag in [inkex.addNS("eggbot", "svg"), "eggbot"]: + pass + elif node.tag in [inkex.addNS("WCB", "svg"), "WCB"]: + pass + elif node.tag in [inkex.addNS("text", "svg"), "text"]: + inkex.errormsg( + "Warning: unable to draw text, please convert it to a path first." + ) + pass + elif not isinstance(node.tag, basestring): + pass + else: + inkex.errormsg( + "Warning: unable to hatch object <{0}>, please convert it to a path first.".format( + node.tag + ) + ) + pass + + def joinFillsWithNode(self, node, stroke_width, path): + + """ + Generate a SVG element containing the path data "path". + Then put this new element into a with the supplied + node. This means making a new element and moving node + under it with the new as a sibling element. + """ + + if not path or len(path) == 0: + return + + # Make a new SVG element whose parent is the parent of node + parent = node.getparent() + if parent is None: + parent = self.document.getroot() + g = etree.SubElement(parent, inkex.addNS("g", "svg")) + # Move node to be a child of this new element + g.append(node) + + # Now make a element which contains the hatches & is a child + # of the new element + stroke_color = "#000000" # default assumption + stroke_width = "1.0" # default value + + try: + style = node.get("style") + if style is not None: + declarations = style.split(";") + for i, declaration in enumerate(declarations): + parts = declaration.split(":", 2) + if len(parts) == 2: + (prop, val) = parts + prop = prop.strip().lower() + if prop == "stroke-width": + stroke_width = val.strip() + elif prop == "stroke": + val = val.strip() + stroke_color = val + finally: + style = { + "stroke": "{0}".format(stroke_color), + "fill": "none", + "stroke-width": "{0}".format(stroke_width), + } + line_attribs = {"style": str(inkex.Style(style)), "d": path} + tran = node.get("transform") + if tran is not None and tran != "": + line_attribs["transform"] = tran + etree.SubElement(g, inkex.addNS("path", "svg"), line_attribs) + + def makeHatchGrid( + self, angle, spacing, init=True + ): # returns True if succeeds in making grid, else False + + """ + Build a grid of hatch lines which encompasses the entire bounding + box of the graphical elements we are to hatch. + + 1. Figure out the bounding box for all of the graphical elements + 2. Pick a rectangle larger than that bounding box so that we can + later rotate the rectangle and still have it cover the bounding + box of the graphical elements. + 3. Center the rectangle of 2 on the origin (0, 0). + 4. Build the hatch line grid in this rectangle. + 5. Rotate the rectangle by the hatch angle. + 6. Translate the center of the rotated rectangle, (0, 0), to be + the center of the bounding box for the graphical elements. + 7. We now have a grid of hatch lines which overlay the graphical + elements and can now be intersected with those graphical elements. + """ + + # If this is the first call, do some one time initializations + # When generating cross hatches, we may be called more than once + if init: + self.getBoundingBox() + self.grid = [] + + # Determine the width and height of the bounding box containing + # all the polygons to be hatched + w = self.xmax - self.xmin + h = self.ymax - self.ymin + + b_bounding_box_exists = (w != (EXTREME_NEG - EXTREME_POS)) and ( + h != (EXTREME_NEG - EXTREME_POS) + ) + ret_value = b_bounding_box_exists + + if b_bounding_box_exists: + # Nice thing about rectangles is that the diameter of the circle + # encompassing them is the length the rectangle's diagonal... + r = math.sqrt(w * w + h * h) / 2.0 + + # Length of a hatch line will be 2r + # Now generate hatch lines within the square + # centered at (0, 0) and with side length at least d + + # While we could generate these lines running back and forth, + # that makes for weird behavior later when applying odd/even + # rules AND there are nested polygons. Instead, when we + # generate the SVG elements with the hatch line + # segments, we can do the back and forth weaving. + + # Rotation information + ca = math.cos(math.radians(90 - angle)) + sa = math.sin(math.radians(90 - angle)) + + # Translation information + cx = self.xmin + (w / 2) + cy = self.ymin + (h / 2) + + # Since the spacing may be fractional (e.g., 6.5), we + # don't try to use range() or other integer iterator + spacing = float(abs(spacing)) + i = -r + while i <= r: + # Line starts at (i, -r) and goes to (i, +r) + x1 = cx + (i * ca) + (r * sa) # i * ca - (-r) * sa + y1 = cy + (i * sa) - (r * ca) # i * sa + (-r) * ca + x2 = cx + (i * ca) - (r * sa) # i * ca - (+r) * sa + y2 = cy + (i * sa) + (r * ca) # i * sa + (+r) * ca + i += spacing + # Remove any potential hatch lines which are entirely + # outside of the bounding box + if (x1 < self.xmin and x2 < self.xmin) or ( + x1 > self.xmax and x2 > self.xmax + ): + continue + if (y1 < self.ymin and y2 < self.ymin) or ( + y1 > self.ymax and y2 > self.ymax + ): + continue + self.grid.append((x1, y1, x2, y2)) + + return ret_value + + def effect(self): + + global ref_count + global pt_last_position_abs + # Viewbox handling + self.handleViewBox() + + if self.options.hatchSpacing == 0: + self.options.hatchSpacing = 0.1 # Hardcode minimum value + + ref_count = 0 + pt_last_position_abs = [0, 0] + + # Build a list of the vertices for the document's graphical elements + if self.options.ids: + # Traverse the selected objects + for id_ in self.options.ids: + self.recursivelyTraverseSvg([self.svg.selected[id_]], self.docTransform) + else: + # Traverse the entire document + self.recursivelyTraverseSvg(self.document.getroot(), self.docTransform) + + # After recursively traversing the svg, we will have a dictionary of transforms and hatches + # Target stroke width will be (doc width + doc height) / 2 / 1000 + # stroke_width_target = ( self.docHeight + self.docWidth ) / 2000 + # stroke_width_target = 1 + stroke_width_target = 1 + # Each hatch line stroke will be within an SVG object which may + # be subject to transforms. So, on an object by object basis, + # we need to transform our target width to a width suitable + # for that object (so that after the object and its hatches are + # transformed, the result has the desired width). + + # To aid in the process, we use a diagonal line segment of length + # stroke_width_target. We then run this segment through an object's + # inverse transform and see what the resulting length of the inversely + # transformed segment is. We could, alternatively, look at the + # x and y scaling factors in the transform and average them. + s = stroke_width_target / math.sqrt(2) + + # Now, dump the hatch fills sorted by which document element + # they correspond to. This is made easy by the fact that we + # saved the information and used each element's lxml.etree node + # pointer as the dictionary key under which to save the hatch + # fills for that node. + + abs_line_segments = {} # Absolute line segments + n_abs_line_segment_total = 0 + n_pen_lifts = 0 + # To implement + for key in self.hatches: + direction = True + if key in self.transforms: + transform = inverseTransform(self.transforms[key]) + # Determine the scaled stroke width for a hatch line + # We produce a line segment of unit length, transform + # its endpoints and then determine the length of the + # resulting line segment. + pt1 = [0, 0] + pt2 = [s, s] + Transform(transform).apply_to_point(pt1) + Transform(transform).apply_to_point(pt2) + dx = pt2[0] - pt1[0] + dy = pt2[1] - pt1[1] + stroke_width = math.sqrt(dx * dx + dy * dy) + else: + transform = None + stroke_width = 1.0 + + # The transform also applies to the hatch spacing we use when searching for end connections + transformed_hatch_spacing = stroke_width * self.options.hatchSpacing + + path = "" # regardless of whether or not we're reducing pen lifts + pt_last_position_abs = [0, 0] + pt_last_position_abs[0] = 0 + pt_last_position_abs[1] = 0 + f_distance_moved_with_pen_up = 0 + if not self.options.reducePenLifts: + for segment in self.hatches[key]: + if len(segment) < 2: + continue + pt1 = segment[0] + pt2 = segment[1] + # Okay, we're going to put these hatch lines into the same + # group as the element they hatch. That element is down + # some chain of SVG elements, some of which may have + # transforms attached. But, our hatch lines have been + # computed assuming that those transforms have already + # been applied (since we had to apply them so as to know + # where this element is on the page relative to other + # elements and their transforms). So, we need to invert + # the transforms for this element and then either apply + # that inverse transform here and now or set it in a + # transform attribute of the element. Having it + # set in the path element seems a bit counterintuitive + # after the fact (i.e., what's this transform here for?). + # So, we compute the inverse transform and apply it here. + if transform is not None: + Transform(transform).apply_to_point(pt1) + Transform(transform).apply_to_point(pt2) + # Now generate the path data for the + if direction: + # Go this direction + path += "M {0:f},{1:f} l {2:f},{3:f} ".format( + pt1[0], pt1[1], pt2[0] - pt1[0], pt2[1] - pt1[1] + ) + else: + # Or go this direction + path += "M {0:f},{1:f} l {2:f},{3:f} ".format( + pt2[0], pt2[1], pt1[0] - pt2[0], pt1[1] - pt2[1] + ) + + direction = not direction + self.joinFillsWithNode(key, stroke_width, path[:-1]) + + else: + for segment in self.hatches[key]: + if ( + len(segment) < 2 + ): # Copied from original, no idea why this is needed [sbm] + continue + if direction: + pt1 = segment[0] + pt2 = segment[1] + else: + pt1 = segment[1] + pt2 = segment[0] + # Okay, we're going to put these hatch lines into the same + # group as the element they hatch. That element is down + # some chain of SVG elements, some of which may have + # transforms attached. But, our hatch lines have been + # computed assuming that those transforms have already + # been applied (since we had to apply them so as to know + # where this element is on the page relative to other + # elements and their transforms). So, we need to invert + # the transforms for this element and then either apply + # that inverse transform here and now or set it in a + # transform attribute of the element. Having it + # set in the path element seems a bit counterintuitive + # after the fact (i.e., what's this transform here for?). + # So, we compute the inverse transform and apply it here. + if transform is not None: + Transform(transform).apply_to_point(pt1) + Transform(transform).apply_to_point(pt2) + + # Now generate the path data for the + # BUT we want to combine as many paths as possible to reduce pen lifts. + # In order to combine paths, we need to know all of the path segments. + # The solution to this conundrum is to generate all path segments, + # but instead of drawing them into the path right away, we put them in + # an array where they'll be available for random access + # by our anti-pen-lift algorithm + abs_line_segments[n_abs_line_segment_total] = [ + pt1, + pt2, + False, + ] # False indicates that segment has not yet been drawn + n_abs_line_segment_total += 1 + direction = not direction + + # Now have a nice juicy buffer full of line segments with absolute coordinates + f_proposed_neighborhood_radius_squared = self.ProposeNeighborhoodRadiusSquared( + transformed_hatch_spacing + ) + # Just fixed and simple for now - may make function of neighborhood later + + for ref_count in range( + n_abs_line_segment_total + ): # This is the entire range of segments, + # Sets global ref_count to segment which has an end closest to current pen position. + # Doesn't need to select which end is closest, as that will happen below, with n_ref_end_index. + # When we have gone thru this whole range, we will be completely done. + # We only get here again, after all _connected_ segments have been "drawn". + if not abs_line_segments[ref_count][ + 2 + ]: # Test whether this segment has been drawn + # Has not been drawn yet + + # Before we do any irrevocable changes to path, let's see if we are going to be able to append any segments. + # The below solution is inelegant, but has the virtue of being relatively simple to implement. + # Pre-qualify this segment on the issue of whether it has any connecting segments. + # If it does not, then just add the path for this one segment, and go on to the next. + # If it does have connecting segments, we need to go through the recursive logic. + # Lazily, again, select the desired direction of line ahead of time. + + b_found_segment_to_add = False # default assumption + n_ref_end_index_at_closest = 0 + f_closest_distance_squared = ( + 123456 # just a random large number + ) + for n_ref_end_index in range(2): + pt_reference = abs_line_segments[ref_count][n_ref_end_index] + pt_reference_other_end = abs_line_segments[ref_count][ + not n_ref_end_index + ] + f_reference_direction_radians = math.atan2( + pt_reference_other_end[1] - pt_reference[1], + pt_reference_other_end[0] - pt_reference[0], + ) # from other end to this end + # The following is just a simple copy from the routine in recursivelyAppendNearbySegments procedure + # Look through all possibilities to choose the closest that fulfills all requirements e.g. direction and colinearity + for innerCount in range( + n_abs_line_segment_total + ): # investigate all segments + if not abs_line_segments[innerCount][2]: + # This segment currently undrawn, so it is a candidate for a path extension + # Need to check both ends of each and every proposed segment so we can find the most appropriate one + # Define pt2 in the reference as the end which we want to extend + for nNewSegmentInitialEndIndex in range(2): + # First try initial end of test segment (aka pt1) vs final end (aka pt2) of reference segment + if ( + innerCount != ref_count + ): # don't investigate self ends + delta_x = ( + abs_line_segments[innerCount][ + nNewSegmentInitialEndIndex + ][0] + - pt_reference[0] + ) # proposed initial pt1 X minus existing final pt1 X + delta_y = ( + abs_line_segments[innerCount][ + nNewSegmentInitialEndIndex + ][1] + - pt_reference[1] + ) # proposed initial pt1 Y minus existing final pt1 Y + if ( + (delta_x * delta_x + delta_y * delta_y) + < f_proposed_neighborhood_radius_squared + ): + f_this_distance_squared = ( + delta_x * delta_x + + delta_y * delta_y + ) + pt_new_segment_this_end = abs_line_segments[ + innerCount + ][ + nNewSegmentInitialEndIndex + ] + pt_new_segment_other_end = abs_line_segments[ + innerCount + ][ + not nNewSegmentInitialEndIndex + ] + f_new_segment_direction_radians = math.atan2( + pt_new_segment_this_end[1] + - pt_new_segment_other_end[1], + pt_new_segment_this_end[0] + - pt_new_segment_other_end[0], + ) # from other end to this end + # If this end would cause an alternating direction, + # then exclude it + if not self.WouldBeAnAlternatingDirection( + f_reference_direction_radians, + f_new_segment_direction_radians, + ): + pass + elif ( + f_this_distance_squared + < f_closest_distance_squared + ): + # One other thing could rule out choosing this segment end: + # Want to screen and remove two segments that, while close enough, + # should be disqualified because they are colinear. The reason for this is that + # if they are colinear, they arose from the same global grid line, which means + # that the gap between them arises from intersections with the boundary. + # The idea here is that, all things being more-or-less equal, + # we would like to give preference to connecting to a segment + # which is the reverse of our current direction. This makes for better + # bezier curve join. + # The criterion for being colinear is that the reference segment angle is effectively + # the same as the line connecting the reference segment to the end of the new segment. + f_joiner_direction_radians = math.atan2( + pt_new_segment_this_end[1] + - pt_reference[1], + pt_new_segment_this_end[0] + - pt_reference[0], + ) + if not self.AreCoLinear( + f_reference_direction_radians, + f_joiner_direction_radians, + ): + # not colinear + f_closest_distance_squared = ( + f_this_distance_squared + ) + b_found_segment_to_add = True + n_ref_end_index_at_closest = ( + n_ref_end_index + ) + + # At last we've looked at all the candidate segment ends, as related to all the reference ends + if not b_found_segment_to_add: + # This segment is solitary. + # Must start a new line, not joined to any previous paths + delta_x = ( + abs_line_segments[ref_count][1][0] + - abs_line_segments[ref_count][0][0] + ) # end minus start, in original direction + delta_y = ( + abs_line_segments[ref_count][1][1] + - abs_line_segments[ref_count][0][1] + ) # end minus start, in original direction + path += "M {0:f},{1:f} l {2:f},{3:f} ".format( + abs_line_segments[ref_count][0][0], + abs_line_segments[ref_count][0][1], + delta_x, + delta_y, + ) # delta is from initial point + f_distance_moved_with_pen_up += math.hypot( + abs_line_segments[ref_count][0][0] + - pt_last_position_abs[0], + abs_line_segments[ref_count][0][1] + - pt_last_position_abs[1], + ) + pt_last_position_abs[0] = ( + abs_line_segments[ref_count][0][0] + delta_x + ) + pt_last_position_abs[1] = ( + abs_line_segments[ref_count][0][1] + delta_y + ) + abs_line_segments[ref_count][ + 2 + ] = True # True flags that this line segment has been + # added to the path to be drawn, so should + # no longer be a candidate for any kind of move. + n_pen_lifts += 1 + else: + # Found segment to add, and we must get to it in absolute terms + delta_x = ( + abs_line_segments[ref_count][ + n_ref_end_index_at_closest + ][0] + - abs_line_segments[ref_count][ + not n_ref_end_index_at_closest + ][0] + ) + # final point (which was closer to the closest continuation segment) minus initial point = delta_x + + delta_y = ( + abs_line_segments[ref_count][ + n_ref_end_index_at_closest + ][1] + - abs_line_segments[ref_count][ + not n_ref_end_index_at_closest + ][1] + ) + # final point (which was closer to the closest continuation segment) minus initial point = delta_y + + path += "M {0:f},{1:f} l ".format( + abs_line_segments[ref_count][ + not n_ref_end_index_at_closest + ][0], + abs_line_segments[ref_count][ + not n_ref_end_index_at_closest + ][1], + ) + f_distance_moved_with_pen_up += math.hypot( + abs_line_segments[ref_count][ + not n_ref_end_index_at_closest + ][0] + - pt_last_position_abs[0], + abs_line_segments[ref_count][ + not n_ref_end_index_at_closest + ][1] + - pt_last_position_abs[1], + ) + pt_last_position_abs[0] = abs_line_segments[ref_count][ + not n_ref_end_index_at_closest + ][0] + pt_last_position_abs[1] = abs_line_segments[ref_count][ + not n_ref_end_index_at_closest + ][1] + # Note that this does not complete the line, as the completion (the delta_x, delta_y part) is being held in abeyance + + # We are coming up on a problem: + # If we add a curve to the end of the line, we have made the curve extend beyond the end of the line, + # and thus beyond the boundaries we should be respecting. + # The solution is to hold in abeyance the actual plotting of the line, + # holding it available for shrinking if a curve is to be added. + # That is + relative_held_line_pos = {0: delta_x, 1: delta_y} + # delta is from initial point + # Will be printed after we know if it must be modified + # to keep the ending join within bounds + pt_last_position_abs[0] += delta_x + pt_last_position_abs[1] += delta_y + + abs_line_segments[ref_count][ + 2 + ] = True # True flags that this line segment has been + # added to the path to be drawn, so should + # no longer be a candidate for any kind of move. + n_pen_lifts += 1 + # Now comes the speedup logic: + # We've just drawn a segment starting at an absolute, not relative, position. + # It was drawn from pt1 to pt2. + # Look for an as-yet-not-drawn segment which has a beginning or ending + # point "near" the end point of this absolute draw, and leave the pen down + # while moving to and then drawing this found line. + # Do this recursively, marking each segment True to show that + # it has been "drawn" already. + # pt2 is the reference point, ie. the point from which the next segment will start + path = self.recursivelyAppendNearbySegments( + transformed_hatch_spacing, + 0, + ref_count, + n_ref_end_index_at_closest, + n_abs_line_segment_total, + abs_line_segments, + path, + relative_held_line_pos, + ) + + self.joinFillsWithNode(key, stroke_width, path[:-1]) + + def recursivelyAppendNearbySegments( + self, + transformed_hatch_spacing, + n_recursion_count, + n_ref_segment_count, + n_ref_end_index, + n_abs_line_segment_total, + abs_line_segments, + cumulative_path, + relative_held_line_pos, + ): + + global pt_last_position_abs + f_proposed_neighborhood_radius_squared = self.ProposeNeighborhoodRadiusSquared( + transformed_hatch_spacing + ) + + # Look through all possibilities to choose the closest + b_found_segment_to_add = False # default assumption + n_new_segment_end1_index_at_closest = 0 + n_outer_count_at_closest = -1 + f_closest_distance_squared = 123456789.0 # just a random large number + + pt_reference = abs_line_segments[n_ref_segment_count][n_ref_end_index] + pt_reference_other_end = abs_line_segments[n_ref_segment_count][ + not n_ref_end_index + ] + f_reference_delta_x = pt_reference_other_end[0] - pt_reference[0] + f_reference_delta_y = pt_reference_other_end[1] - pt_reference[1] + f_reference_direction_radians = math.atan2( + f_reference_delta_y, f_reference_delta_x + ) # from other end to this end + + for outerCount in range(n_abs_line_segment_total): # investigate all segments + if not abs_line_segments[outerCount][2]: + # This segment currently undrawn, so it is a candidate for a path extension + + # Need to check both ends of each and every proposed segment until we find one in the neighborhood + # Defines pt2 in the reference as the end which we want to extend + + for n_new_segment_end1_index in range(2): + # First try initial end of test segment (aka pt1) vs final end (aka pt2) of reference segment + if outerCount != n_ref_segment_count: # don't investigate self ends + delta_x = ( + abs_line_segments[outerCount][n_new_segment_end1_index][0] + - pt_reference[0] + ) # proposed initial pt1 X minus existing final pt1 X + delta_y = ( + abs_line_segments[outerCount][n_new_segment_end1_index][1] + - pt_reference[1] + ) # proposed initial pt1 Y minus existing final pt1 Y + if ( + delta_x * delta_x + delta_y * delta_y + ) < f_proposed_neighborhood_radius_squared: + f_this_distance_squared = ( + delta_x * delta_x + delta_y * delta_y + ) + pt_new_segment_this_end = abs_line_segments[outerCount][ + n_new_segment_end1_index + ] + pt_new_segment_other_end = abs_line_segments[outerCount][ + not n_new_segment_end1_index + ] + f_new_segment_Dx = ( + pt_new_segment_this_end[0] - pt_new_segment_other_end[0] + ) + f_new_segment_Dy = ( + pt_new_segment_this_end[1] - pt_new_segment_other_end[1] + ) + f_new_segment_direction_radians = math.atan2( + f_new_segment_Dy, f_new_segment_Dx + ) # from other end to this end + if not self.WouldBeAnAlternatingDirection( + f_reference_direction_radians, + f_new_segment_direction_radians, + ): + # If this end would cause an alternating direction, + # then exclude it regardless of how close it is + pass + + elif f_this_distance_squared < f_closest_distance_squared: + # One other thing could rule out choosing this segment end: + # Want to screen and remove two segments that, while close enough, + # should be disqualified because they are colinear. The reason for this is that + # if they are colinear, they arose from the same global grid line, which means + # that the gap between them arises from intersections with the boundary. + # The idea here is that, all things being more-or-less equal, + # we would like to give preference to connecting to a segment + # which is the reverse of our current direction. This makes for better + # bezier curve join. + # The criterion for being colinear is that the reference segment angle is effectively + # the same as the line connecting the reference segment to the end of the new segment. + + f_joiner_direction_radians = math.atan2( + pt_new_segment_this_end[1] - pt_reference[1], + pt_new_segment_this_end[0] - pt_reference[0], + ) + if not self.AreCoLinear( + f_reference_direction_radians, + f_joiner_direction_radians, + ): + # not colinear + f_closest_distance_squared = f_this_distance_squared + b_found_segment_to_add = True + n_new_segment_end1_index_at_closest = ( + n_new_segment_end1_index + ) + n_outer_count_at_closest = outerCount + delta_x_at_closest = delta_x + delta_y_at_closest = delta_y + + # At last we've looked at all the candidate segment ends + n_recursion_count += 1 + if not b_found_segment_to_add or n_recursion_count >= RECURSION_LIMIT: + cumulative_path += "{0:f},{1:f} ".format( + relative_held_line_pos[0], relative_held_line_pos[1] + ) # close out this segment + pt_last_position_abs[0] += relative_held_line_pos[0] + pt_last_position_abs[1] += relative_held_line_pos[1] + return cumulative_path # No undrawn segments were suitable for appending, + # or there were so many that we worry about python recursion limit + else: + n_new_segment_end1_index = n_new_segment_end1_index_at_closest + n_new_segment_end2_index = not n_new_segment_end1_index + # n_new_segment_end1_index is 0 for connecting to pt1, + # and is 1 for connecting to pt2 + count = n_outer_count_at_closest # count is the index of the segment to be appended. + delta_x = delta_x_at_closest # delta from final end of incoming segment to initial end of outgoing segment + delta_y = delta_y_at_closest + + # First, move pen to initial end (may be either its pt1 or its pt2) of new segment + + # Insert a bezier curve for this transition element + # To accomplish this, we need information on the incoming and outgoing segments. + # Specifically, we need to know the lengths and angles of the segments in + # order to decide on control points. + f_in_Dx = ( + abs_line_segments[n_ref_segment_count][n_ref_end_index][0] + - abs_line_segments[n_ref_segment_count][not n_ref_end_index][0] + ) + f_in_Dy = ( + abs_line_segments[n_ref_segment_count][n_ref_end_index][1] + - abs_line_segments[n_ref_segment_count][not n_ref_end_index][1] + ) + # The outgoing deltas are based on the reverse direction of the segment, i.e. the segment pointing back to the joiner bezier curve + f_out_Dx = ( + abs_line_segments[count][n_new_segment_end1_index][0] + - abs_line_segments[count][n_new_segment_end2_index][0] + ) # index is [count][start point = 0, final point = 1][0=x, 1=y] + f_out_Dy = ( + abs_line_segments[count][n_new_segment_end1_index][1] + - abs_line_segments[count][n_new_segment_end2_index][1] + ) + + length_of_incoming = math.hypot(f_in_Dx, f_in_Dy) + length_of_outgoing = math.hypot(f_out_Dx, f_out_Dy) + + # We are going to trim-up the ends of the incoming and outgoing segments, + # in order to get a curve which reliably does not extend beyond the boundary. + # Crude readings from inkscape on bezier curve overshoot, using control points extended hatch-spacing distance parallel to segment: + # when end points are in line, overshoot 12/16 in direction of segment + # when at 45 degrees, overshoot 12/16 in direction of segment + # when at 60 degrees, overshoot 12/16 in direction of segment + # Conclusion, at any angle, remove 0.75 * hatch spacing from the length of both lines, + # where 0.75 is, by no coincidence, BEZIER_OVERSHOOT_MULTIPLIER + + # If hatches are getting quite short, we can use a smaller Bezier loop at + # the end to squeeze into smaller spaces. We'll use a normal nice smooth + # curve for non-short hatches + f_desired_shorten_for_smoothest_join = ( + transformed_hatch_spacing * BEZIER_OVERSHOOT_MULTIPLIER + ) # This is what we really want to use for smooth curves + # Separately check incoming vs outgoing lengths to see if bezier distances must be reduced, + # then choose greatest reduction to apply to both - lest we go off-course + # Finally, clip reduction to be no less than 1.0 + f_control_point_divider_incoming = ( + 2.0 * f_desired_shorten_for_smoothest_join / length_of_incoming + ) + f_control_point_divider_outgoing = ( + 2.0 * f_desired_shorten_for_smoothest_join / length_of_outgoing + ) + if f_control_point_divider_incoming > f_control_point_divider_outgoing: + f_largest_desired_control_point_divider = ( + f_control_point_divider_incoming + ) + else: + f_largest_desired_control_point_divider = ( + f_control_point_divider_outgoing + ) + if f_largest_desired_control_point_divider < 1.0: + f_control_point_divider = 1.0 + else: + f_control_point_divider = f_largest_desired_control_point_divider + f_desired_shorten = ( + f_desired_shorten_for_smoothest_join / f_control_point_divider + ) + + pt_delta_to_subtract_from_incoming_end = self.RelativeControlPointPosition( + f_desired_shorten, f_in_Dx, f_in_Dy, 0, 0 + ) + # Note that this will be subtracted from the _point held in abeyance_. + relative_held_line_pos[0] -= pt_delta_to_subtract_from_incoming_end[0] + relative_held_line_pos[1] -= pt_delta_to_subtract_from_incoming_end[1] + + pt_delta_to_add_to_outgoing_start = self.RelativeControlPointPosition( + f_desired_shorten, f_out_Dx, f_out_Dy, 0, 0 + ) + + # We know that when we tack on a curve, we must chop some off the end of the incoming segment, + # and also chop some off the start of the outgoing segment. + # Now, we know we want the control points to be on a projection of each segment, + # in order that there be no abrupt change of plotting angle. The question is, how + # far beyond the endpoint should we place the control point. + pt_relative_control_point_in = self.RelativeControlPointPosition( + transformed_hatch_spacing / f_control_point_divider, + f_in_Dx, + f_in_Dy, + 0, + 0, + ) + pt_relative_control_point_out = self.RelativeControlPointPosition( + transformed_hatch_spacing / f_control_point_divider, + f_out_Dx, + f_out_Dy, + delta_x, + delta_y, + ) + + cumulative_path += "{0:f},{1:f} ".format( + relative_held_line_pos[0], relative_held_line_pos[1] + ) # close out this segment, which has been modified + pt_last_position_abs[0] += relative_held_line_pos[0] + pt_last_position_abs[1] += relative_held_line_pos[1] + # add bezier cubic curve + cumulative_path += "c {0:f},{1:f} {2:f},{3:f} {4:f},{5:f} l ".format( + pt_relative_control_point_in[0], + pt_relative_control_point_in[1], + pt_relative_control_point_out[0], + pt_relative_control_point_out[1], + delta_x, + delta_y, + ) + pt_last_position_abs[0] += delta_x + pt_last_position_abs[1] += delta_y + # Next, move pen in appropriate direction to draw the new segment, given that + # we have just moved to the initial end of the new segment. + # This needs special treatment, as we just did some length changing. + delta_x = ( + abs_line_segments[count][n_new_segment_end2_index][0] + - abs_line_segments[count][n_new_segment_end1_index][0] + + pt_delta_to_add_to_outgoing_start[0] + ) + delta_y = ( + abs_line_segments[count][n_new_segment_end2_index][1] + - abs_line_segments[count][n_new_segment_end1_index][1] + + pt_delta_to_add_to_outgoing_start[1] + ) + relative_held_line_pos[0] = delta_x # delta is from initial point + relative_held_line_pos[ + 1 + ] = delta_y # Will be printed after we know if it must be modified + + # Mark this segment as drawn + abs_line_segments[count][2] = True + + cumulative_path = self.recursivelyAppendNearbySegments( + transformed_hatch_spacing, + n_recursion_count, + count, + n_new_segment_end2_index, + n_abs_line_segment_total, + abs_line_segments, + cumulative_path, + relative_held_line_pos, + ) + return cumulative_path + + def ProposeNeighborhoodRadiusSquared(self, transformed_hatch_spacing): + return ( + transformed_hatch_spacing + * transformed_hatch_spacing + * self.options.hatchScope + * self.options.hatchScope + ) + # The multiplier of x generates a radius of x^0.5 times the hatch spacing. + + @staticmethod + def RelativeControlPointPosition(distance, f_delta_x, f_delta_y, delta_x, delta_y): + + # returns the point, relative to 0, 0 offset by delta_x, delta_y, + # which extends a distance of "distance" at a slope defined by f_delta_x and f_delta_y + pt_return = [0, 0] + + if f_delta_x == 0: + pt_return[0] = delta_x + pt_return[1] = math.copysign(distance, f_delta_y) + delta_y + elif f_delta_y == 0: + pt_return[0] = math.copysign(distance, f_delta_x) + delta_x + pt_return[1] = delta_y + else: + f_slope = math.atan2(f_delta_y, f_delta_x) + pt_return[0] = distance * math.cos(f_slope) + delta_x + pt_return[1] = distance * math.sin(f_slope) + delta_y + + return pt_return + + @staticmethod + def WouldBeAnAlternatingDirection( + f_reference_direction_radians, f_new_segment_direction_radians + ): + # atan2 returns values in the range -pi to +pi, so we must evaluate difference values + # in the range of -2*pi to +2*pi + # f_dir_diff_rad: Direction difference, radians + f_dir_diff_rad = f_reference_direction_radians - f_new_segment_direction_radians + if f_dir_diff_rad < 0: + f_dir_diff_rad += 2 * math.pi + # Without having changed the vector direction of the difference, we have + # now reduced the range to 0 to 2*pi + f_dir_diff_rad -= ( + math.pi + ) # flip opposite direction to coincide with same direction + # Of course they may not be _exactly_ pi different due to osmosis, so allow a tolerance + b_ret_val = abs(f_dir_diff_rad) < RADIAN_TOLERANCE_FOR_ALTERNATING_DIRECTION + + return b_ret_val + + @staticmethod + def AreCoLinear(f_direction_1_radians, f_direction_2_radians): + # allow slight difference in angles, for floating-point indeterminacy + f_abs_delta_radians = abs(f_direction_1_radians - f_direction_2_radians) + if f_abs_delta_radians < RADIAN_TOLERANCE_FOR_COLINEAR: + return True + elif abs(f_abs_delta_radians - math.pi) < RADIAN_TOLERANCE_FOR_COLINEAR: + return True + else: + return False + + +def parseLengthWithUnits(string_to_parse): + """ + Parse an SVG value which may or may not have units attached. + There is a more general routine to consider in scour.py if more + generality is ever needed. + """ + u = "px" + s = string_to_parse.strip() + if s[-2:] == "px": # pixels, at a size of PX_PER_INCH per inch + s = s[:-2] + elif s[-2:] == "in": # inches + s = s[:-2] + u = "in" + elif s[-2:] == "mm": # millimeters + s = s[:-2] + u = "mm" + elif s[-2:] == "cm": # centimeters + s = s[:-2] + u = "cm" + elif s[-2:] == "pt": # points; 1pt = 1/72th of 1in + s = s[:-2] + u = "pt" + elif s[-2:] == "pc": # picas; 1pc = 1/6th of 1in + s = s[:-2] + u = "pc" + elif s[-1:] == "Q" or s[-1:] == "q": # quarter-millimeters. 1q = 1/40th of 1cm + s = s[:-1] + u = "Q" + elif s[-1:] == "%": + u = "%" + s = s[:-1] + + try: + v = float(s) + except: + return None, None + + return v, u + + +def getLength(altself, name, default): + + PX_PER_INCH = 96.0 + """ + Get the attribute with name "name" and default value "default" + Parse the attribute into a value and associated units. Then, accept + no units (''), units of pixels ('px'), and units of percentage ('%'). + Return value in px. + """ + string_to_parse = altself.document.getroot().get(name) + + if string_to_parse: + v, u = parseLengthWithUnits(string_to_parse) + if v is None: + return None + elif u == "" or u == "px": + return float(v) + elif u == "in": + return float(v) * PX_PER_INCH + elif u == "mm": + return float(v) * PX_PER_INCH / 25.4 + elif u == "cm": + return float(v) * PX_PER_INCH / 2.54 + elif u == "Q" or u == "q": + return float(v) * PX_PER_INCH / (40.0 * 2.54) + elif u == "pc": + return float(v) * PX_PER_INCH / 6.0 + elif u == "pt": + return float(v) * PX_PER_INCH / 72.0 + elif u == "%": + return float(default) * v / 100.0 + else: + # Unsupported units + return None + else: + # No width specified; assume the default value + return float(default) + + +def inkscape_run_debug(): + import sys + import os + import datetime + import shutil + import tempfile + + # If we aren't calling this from our own script. + if os.environ.get("DEBUG_RECURSION", "0") == "1": + return + script_path = os.path.abspath(sys.argv[0]) + #debug_dir = os.path.join(os.path.dirname(script_path), "debug") + debug_dir = tempfile.gettempdir() + os.makedirs(debug_dir, exist_ok=True) + base_name, _ = os.path.splitext(os.path.basename(script_path)) + + # Debug Information. + with open(os.path.join(debug_dir, f"{base_name}.debug"), "w") as fid: + fid.write("#" * 20) + fid.write("\n# ") + fid.write(str(datetime.datetime.now())) + fid.write("\n") + fid.write("#" * 20) + + fid.write("\nExecutable: \n\t") + fid.write(sys.executable) + + fid.write("\nCurrentDirectory: \n\t") + fid.write(os.path.abspath(os.path.curdir)) + + fid.write("\nPaths:\n") + for path in sys.path: + fid.write("\t") + fid.write(path) + fid.write("\n") + + fid.write("\nArgs:\n") + for arg in sys.argv: + fid.write("\t") + fid.write(arg) + fid.write("\n") + + # Copy the temporary file to a permanent location for debugging. + in_file = sys.argv[-1] + if not in_file.endswith(".svg"): + out_file = os.path.join(debug_dir, "input_file.svg") + shutil.copy2(sys.argv[-1], out_file) + else: + out_file = in_file + + # Script to run Inkscape Extension as a Python script. + with open(os.path.join(debug_dir, f"{base_name}.sh"), "w") as fid: + fid.write("#!/usr/bin/env bash") + fid.write("\n# ") + fid.write(str(datetime.datetime.now())) + fid.write("\n" * 2) + fid.write("export DEBUG_RECURSION=1\n") + fid.write("export PYTHONPATH=") + fid.write(os.pathsep.join(sys.path)) + fid.write("\n") + fid.write(sys.executable) + fid.write(" ") + fid.write(os.path.abspath(sys.argv[0])) + + # Python program to programmatically call the Inkscape Extension (with the same settings) + # Useful for debugging an extension with VSCode or other debugger. + with open(os.path.join(debug_dir, f"{base_name}_run.py"), "w") as fid: + fid.write("#!" + sys.executable) + fid.write(f"\n# {datetime.datetime.now()}\n") + for module in ["sys", "os"]: + fid.write(f"import {module}\n") + for sys_path in list(sys.path): + if len(sys_path) == 0: + continue + fid.write(f"sys.path.append('{sys_path}')\n") + + fid.write("args = [\n") + for arg in sys.argv[1:-1]: + fid.write(f' "{arg}",\n') + fid.write(f' "{out_file}"\n') + fid.write("]\n") + + fid.write(f"import {base_name}\n") + fid.write("hatchfill = HatchFill.HatchFill()\n") + fid.write("hatchfill.run(args)\n") + + +if __name__ == "__main__": + # Debug script when called from Inkscape + inkscape_run_debug() + HatchFill().run() diff --git a/extensions/fablabchemnitz/hatch_fill/meta.json b/extensions/fablabchemnitz/hatch_fill/meta.json new file mode 100644 index 0000000..dff82be --- /dev/null +++ b/extensions/fablabchemnitz/hatch_fill/meta.json @@ -0,0 +1,26 @@ +[ + { + "name": "Hatch Fill", + "id": "fablabchemnitz.de.hatch_fill", + "path": "hatch_fill", + "dependent_extensions": null, + "original_name": "Hatch Fill 2", + "original_id": "command.dapperfu.inkscape_hatchfill2", + "license": "GNU GPL v3", + "license_url": "https://github.com/dapperfu/inkscape_HatchFill2/blob/master/LICENSE", + "comment": "forked from https://github.com/evil-mad/EggBot/", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/hatch_fill", + "fork_url": "https://github.com/dapperfu/inkscape_HatchFill2", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Hatch+Fill", + "inkscape_gallery_url": null, + "main_authors": [ + "github.com/oskay", + "github.com/EmbeddedMan", + "github.com/Cabalist", + "github.com/mutoo", + "github.com/jrheard", + "github.com/dapperfu", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/hex_tiles/hex_tiles.inx b/extensions/fablabchemnitz/hex_tiles/hex_tiles.inx new file mode 100644 index 0000000..8118bcb --- /dev/null +++ b/extensions/fablabchemnitz/hex_tiles/hex_tiles.inx @@ -0,0 +1,20 @@ + + + Hex Tiles + fablabchemnitz.de.hex_tiles + 19 + 2 + 2 + 15 + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/hex_tiles/hex_tiles.py b/extensions/fablabchemnitz/hex_tiles/hex_tiles.py new file mode 100644 index 0000000..5f55d3b --- /dev/null +++ b/extensions/fablabchemnitz/hex_tiles/hex_tiles.py @@ -0,0 +1,457 @@ +#!/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 + +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/tuckboxextension/' + +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 HexGeneratorBase(object): + def __init__(self, hexDiameter, hexMargin, bleedMargin, pageWidth, pageHeight, pageMargin, unitConverterFunc): + self.UnitConverterFunc = unitConverterFunc + self.HexDiameter = hexDiameter + self.HexMargin = hexMargin + self.BleedMargin = bleedMargin + + self.PageWidth = pageWidth + self.PageHeight = pageHeight + self.PageMargin = pageMargin + + self.ContainerWidth = -1 + self.ContainerHeight = -1 + + 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 DrawAngledGuide(self, xmlParent, xpos, ypos, angle): + # Angles are taken from the horizontal axis, positive angles move clockwise + posString = "{},{}".format(xpos, ypos) + orientationString = "{}, {}".format(math.sin(math.radians(angle)), math.cos(math.radians(angle))) + attribs = {'position': posString, 'orientation': orientationString} + + 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.PageMargin: + 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.PageMargin: + for offset in positions: + curPos += offset + if curPos != lastPos: # don't double draw + self.DrawGuide(xmlParent, 0, curPos) + + lastPos = curPos + + curPos += gap + + def DrawAngledGuides(self, xmlParent, offsetPositions, angle, gap): + + numExtraTopContainers = 0 + numExtraBottomContainers = 0 + if angle > 0: + numExtraTopContainers = math.ceil(self.NumContainersAcross / 2.0) - 1 + if angle < 0: + numExtraBottomContainers = math.ceil(self.NumContainersAcross / 2.0) - 1 + + # draw sets of guides per point avoiding duplicate lines + # NOTE: Effectivly we draw the bottom guides first and then move up (ie y is increasing) + curPos = self.CalcPageBottomMargin() - numExtraBottomContainers * self.ContainerHeight + lastPos = -1 + + while curPos + self.ContainerHeight <= self.PageHeight - self.PageMargin + numExtraTopContainers*self.ContainerHeight: + for offset in offsetPositions: + curPos += offset + if curPos != lastPos: # don't double draw + self.DrawAngledGuide(xmlParent, self.CalcPageLeftMargin() + self.ContainerWidth/2, curPos, angle) + + 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 CalculateBorderVerticalOffset(borderWidth): + if borderWidth == 0: + return 0 + + return borderWidth / math.sin(math.radians(60)) + + @staticmethod + def CreateHexGenerator(hexDiameter, hexMargin, bleedMargin, pageWidth, pageHeight, pageMargin, unitConverterFunc): + return SimpleHexGridLineGenerator(hexDiameter, hexMargin, bleedMargin, pageWidth, pageHeight, pageMargin, unitConverterFunc) + +class SimpleHexGridLineGenerator(HexGeneratorBase): + def __init__(self, hexDiameter, hexMargin, bleedMargin, pageWidth, pageHeight, pageMargin, unitConverterFunc): + super(SimpleHexGridLineGenerator, self).__init__(hexDiameter, hexMargin, bleedMargin, pageWidth, pageHeight, pageMargin, unitConverterFunc) + + self.HexWidth = math.sqrt(3) * (hexDiameter/2) + self.ContainerWidth = self.HexWidth + 2*bleedMargin + self.ContainerHeight = hexDiameter + 2*HexGeneratorBase.CalculateBorderVerticalOffset(bleedMargin) + + # num across + # num down + self.NumContainersAcross = int((self.PageWidth - 2*self.PageMargin) // self.ContainerWidth) # round down division + self.NumContainersDown = int((self.PageHeight - 2*self.PageMargin) // self.ContainerHeight) # round down division + + # content sizes + self.ContentWidth = self.NumContainersAcross * self.ContainerWidth + self.ContentHeight = self.NumContainersDown * self.ContainerHeight + + def GenerateGuides(self, xmlParent): + verticalGuideOffsets = [ + 0, + self.BleedMargin, + self.HexMargin, + self.HexWidth - 2*self.HexMargin, + self.HexMargin, + self.BleedMargin + ] + + bleedVerticalOffset = HexGeneratorBase.CalculateBorderVerticalOffset(self.BleedMargin) + hexMarginVerticalOffset = HexGeneratorBase.CalculateBorderVerticalOffset(self.HexMargin) + horizontalGuideOffsets = [ + 0, + bleedVerticalOffset, + hexMarginVerticalOffset, + self.HexDiameter - 2*hexMarginVerticalOffset, + hexMarginVerticalOffset, + bleedVerticalOffset + ] + + self.DrawVerticleGuides(xmlParent, verticalGuideOffsets, 0) + self.DrawAngledGuides(xmlParent, horizontalGuideOffsets, -30, 0) + self.DrawAngledGuides(xmlParent, horizontalGuideOffsets, 30, 0) + + def GetFoldLinePositions(self): + return [] # no fold lines in simple grid + + def GetCropMarkLines(self): + lines = [] + + leftMargin = self.CalcPageLeftMargin() + bottomMargin = self.CalcPageBottomMargin() + bleedVerticalOffset = HexGeneratorBase.CalculateBorderVerticalOffset(self.BleedMargin) + + def CalcOppositeDeltaGivenAdjacentDelta(xdelta, angle): + return math.tan(math.radians(angle)) * xdelta + + #--------------------------------------------------------------------------------- + #determine all vertical crop marks, duplicates possible + # figure out the xpos' + vertical_xpos = [] + for idx in range(self.NumContainersAcross): + leftX = self.BleedMargin + rightX = leftX + self.HexWidth + containerOffset = leftMargin + idx * self.ContainerWidth + + vertical_xpos.append(containerOffset + leftX) + vertical_xpos.append(containerOffset + rightX) + + vertical_xpos = RoundAndDeduplicatePoints(vertical_xpos) + + # add to list of lines + 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) + ]) + + #--------------------------------------------------------------------------------- + # figure out NW, SW, NE and SE crop marks for both sides of the page + vertical_ypos_nw = [] + vertical_ypos_sw = [] + vertical_ypos_ne = [] + vertical_ypos_se = [] + + yoffset = CalcOppositeDeltaGivenAdjacentDelta(self.ContainerWidth/2 + CROP_GAP, 30) + + lastColumnHasHalfStep = (self.NumContainersAcross % 2) == 0 # an even numbered containers across means that the last column is 1/2 a continer up and has 1 less container + staggeredContainerOffset = 0 + if lastColumnHasHalfStep: + staggeredContainerOffset = self.ContainerHeight/2 + + for idx in range(self.NumContainersDown): + leftContainerOffset = idx * self.ContainerHeight + bottomMargin + rightContainerOffset = leftContainerOffset + staggeredContainerOffset + + bottomY = bleedVerticalOffset + topY = self.ContainerHeight - bleedVerticalOffset + + vertical_ypos_nw.append(leftContainerOffset + topY + yoffset) + vertical_ypos_nw.append(leftContainerOffset + bottomY + yoffset) + + vertical_ypos_sw.append(leftContainerOffset + topY - yoffset) + vertical_ypos_sw.append(leftContainerOffset + bottomY - yoffset) + + vertical_ypos_ne.append(rightContainerOffset + topY + yoffset) + vertical_ypos_ne.append(rightContainerOffset + bottomY + yoffset) + + vertical_ypos_se.append(rightContainerOffset + topY - yoffset) + vertical_ypos_se.append(rightContainerOffset + bottomY - yoffset) + + # sort out a staggered last col + if lastColumnHasHalfStep: # if it's a half step column we need to remove the last container and add acouple of extra lines + vertical_ypos_ne = vertical_ypos_ne[:-2] + vertical_ypos_se = vertical_ypos_se[:-2] + vertical_ypos_ne.append(max(vertical_ypos_ne) + 2*bleedVerticalOffset) + vertical_ypos_se.append(min(vertical_ypos_se) - 2*bleedVerticalOffset) + + # remove duplicate positions + vertical_ypos_nw = RoundAndDeduplicatePoints(vertical_ypos_nw) + vertical_ypos_sw = RoundAndDeduplicatePoints(vertical_ypos_sw) + vertical_ypos_ne = RoundAndDeduplicatePoints(vertical_ypos_ne) + vertical_ypos_se = RoundAndDeduplicatePoints(vertical_ypos_se) + + # add to list of lines + xpos_left = leftMargin - CROP_GAP + xpos_right = self.PageWidth - leftMargin + CROP_GAP + yoffset = CalcOppositeDeltaGivenAdjacentDelta(CROP_LENGTH, 30) + + for ypos in vertical_ypos_nw: + lines.append([ + Point(xpos_left, ypos), + Point(xpos_left - CROP_LENGTH, ypos + yoffset) + ]) + + for ypos in vertical_ypos_sw: + lines.append([ + Point(xpos_left, ypos), + Point(xpos_left - CROP_LENGTH, ypos - yoffset) + ]) + + for ypos in vertical_ypos_ne: + lines.append([ + Point(xpos_right, ypos), + Point(xpos_right + CROP_LENGTH, ypos + yoffset) + ]) + + for ypos in vertical_ypos_se: + lines.append([ + Point(xpos_right, ypos), + Point(xpos_right + CROP_LENGTH, ypos - yoffset) + ]) + + #--------------------------------------------------------------------------------- + # figure out NW, SW, NE and SE crop marks for top and bottom of the page + xpos_nw = [] + xpos_sw = [] + xpos_ne = [] + xpos_se = [] + + xoffset_near = CalcOppositeDeltaGivenAdjacentDelta(bleedVerticalOffset + CROP_GAP, 60) + xoffset_far = CalcOppositeDeltaGivenAdjacentDelta(self.ContainerHeight - bleedVerticalOffset + CROP_GAP, 60) + + for idx in range(0, self.NumContainersAcross, 2): #we only need to do every other container because of the stepping + containerOffset = idx * self.ContainerWidth + leftMargin + topY = self.ContainerHeight - bleedVerticalOffset + bottomY = bleedVerticalOffset + xpos = self.ContainerWidth/2 + + xpos_nw.append(containerOffset + xpos - xoffset_near) + xpos_nw.append(containerOffset + xpos - xoffset_far) + + xpos_sw.append(containerOffset + xpos - xoffset_near) + xpos_sw.append(containerOffset + xpos - xoffset_far) + + xpos_ne.append(containerOffset + xpos + xoffset_near) + xpos_ne.append(containerOffset + xpos + xoffset_far) + + xpos_se.append(containerOffset + xpos + xoffset_near) + xpos_se.append(containerOffset + xpos + xoffset_far) + + + # remove duplicate positions + xpos_nw = RoundAndDeduplicatePoints(xpos_nw) + xpos_sw = RoundAndDeduplicatePoints(xpos_sw) + xpos_ne = RoundAndDeduplicatePoints(xpos_ne) + xpos_se = RoundAndDeduplicatePoints(xpos_se) + + # add to list of lines + ypos_bottom = bottomMargin - CROP_GAP + ypos_top = self.PageHeight - bottomMargin + CROP_GAP + yoffset = CalcOppositeDeltaGivenAdjacentDelta(CROP_LENGTH, 30) + + for xpos in xpos_nw: + if xpos > 0 and xpos < self.PageWidth: + lines.append([ + Point(xpos, ypos_top), + Point(xpos - CROP_LENGTH, ypos_top + yoffset) + ]) + + for xpos in xpos_sw: + if xpos > 0 and xpos < self.PageWidth: + lines.append([ + Point(xpos, ypos_bottom), + Point(xpos - CROP_LENGTH, ypos_bottom - yoffset) + ]) + + for xpos in xpos_ne: + if xpos > 0 and xpos < self.PageWidth: + lines.append([ + Point(xpos, ypos_top), + Point(xpos + CROP_LENGTH, ypos_top + yoffset) + ]) + + for xpos in xpos_se: + if xpos > 0 and xpos < self.PageWidth: + lines.append([ + Point(xpos, ypos_bottom), + Point(xpos + CROP_LENGTH, ypos_bottom - yoffset) + ]) + + #--------------------------------------------------------------------------------- + return lines + +class HexTiles(inkex.EffectExtension): + + def add_arguments(self, pars): + pars.add_argument('-d', '--hex_diameter', type = float, dest = 'HexDiameter') + pars.add_argument('-c', '--hex_margin', type = float, dest = 'HexMargin') + pars.add_argument('-b', '--bleed_margin', type = float, dest = 'BleedMargin') + pars.add_argument('-p', '--page_margin', type = float, dest = 'PageMargin') + + 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 object generator + gen = HexGeneratorBase.CreateHexGenerator(opt.HexDiameter, opt.HexMargin, opt.BleedMargin, pageWidth, pageHeight, opt.PageMargin, self.svg.unittouu) + + gen.GenerateGuides(guideParent) + + ### CROP MARKS + # 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.GenerateCropMarks(layer) + +if __name__ == '__main__': + HexTiles().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/hex_tiles/meta.json b/extensions/fablabchemnitz/hex_tiles/meta.json new file mode 100644 index 0000000..7e636ae --- /dev/null +++ b/extensions/fablabchemnitz/hex_tiles/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Hex Tiles", + "id": "fablabchemnitz.de.hex_tiles", + "path": "hex_tiles", + "dependent_extensions": null, + "original_name": "Hex tiles", + "original_id": "phillips.effect.hexlayoutguides", + "license": "GNU GPL v2", + "license_url": "https://sourceforge.net/p/razorfoss/svn/HEAD/tree/trunk/Inkscape/LayoutGuides/HexLayoutGuides.py", + "comment": "", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/hex_tiles", + "fork_url": "https://sourceforge.net/p/razorfoss/svn/HEAD/tree/trunk/Inkscape/LayoutGuides/", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Hex+Tiles", + "inkscape_gallery_url": null, + "main_authors": [ + "Luke Phillips:lukerazor@hotmail.com", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/inklin/inklin.glade b/extensions/fablabchemnitz/inklin/inklin.glade new file mode 100644 index 0000000..a9e8bdd --- /dev/null +++ b/extensions/fablabchemnitz/inklin/inklin.glade @@ -0,0 +1,421 @@ + + + + + + 200 + 100 + 1 + 10 + + + 3 + 50 + 3 + 1 + 1 + + + Inklin - a collection of things I 'ave an inkling might be useful to someone. + +https://inkscape.org/~inklinea/ + +https://inkscape.org/~bipper/ + + + 500 + 20 + 0.10000000000000001 + 0.10000000000000001 + + + 500 + 20 + 0.10000000000000001 + 0.10000000000000001 + + + 359 + 0.050000000000000003 + 0.10000000000000001 + + + False + False + 600 + 600 + True + + + + + + True + True + + + True + False + + + + True + False + + + Arc Radius 1 + True + True + False + True + + + + + 0 + 0 + + + + + True + True + arc_radius2_adjustment + 1 + + + + 0 + 3 + + + + + True + True + True + arc_radius1_adjustment + 1 + + + + 0 + 1 + + + + + Arc Radius 2 + True + True + False + True + + + + + 0 + 2 + + + + + 1 + 0 + + + + + True + False + + + True + False + + + Test Slider + True + True + True + 0_to_200 + 1 + + + + 0 + 1 + + + + + True + False + Radius + + + 0 + 0 + + + + + 0 + 0 + + + + + True + False + + + True + False + Sectors + + + 0 + 0 + + + + + True + True + True + 1_to_50 + 1 + + + + 0 + 1 + + + + + 0 + 1 + + + + + 0 + 0 + + + + + True + False + + + Sweep Flag + True + True + False + Arc sweep flag + True + + + + + 0 + 0 + + + + + Large Flag + True + True + False + Arc large flag + True + + + + + 0 + 1 + + + + + Point Circles + True + True + False + Draw a small circle at each vertex point + True + + + + + 0 + 4 + + + + + Outer Circle + True + True + False + Show the outer circle the vertices lie on + True + + + + + 0 + 3 + + + + + True + False + + + 0 + 2 + + + + + Numbering + True + True + False + Numbering of vertices + True + + + + + 0 + 5 + + + + + 2 + 0 + + + + + True + False + + + True + False + gtk-ok + + + 0 + 2 + 3 + + + + + True + True + True + deg_rotate + 1 + + + + 0 + 1 + + + + + x rotate + True + True + False + True + + + + + 0 + 0 + + + + + + + + + + + + + + + + + 0 + 1 + 4 + + + + + + + + + + True + False + PolyArcs + + + False + + + + + True + True + 10 + 10 + 10 + 10 + 2 + 3 + 15 + 15 + 15 + 15 + AboutText + + + 1 + + + + + True + False + About + + + 1 + False + + + + + + diff --git a/extensions/fablabchemnitz/inklin/inklin.inx b/extensions/fablabchemnitz/inklin/inklin.inx new file mode 100755 index 0000000..3ca8db5 --- /dev/null +++ b/extensions/fablabchemnitz/inklin/inklin.inx @@ -0,0 +1,16 @@ + + + Inklin + fablabchemnitz.de.inklin + + path + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/inklin/inklin.py b/extensions/fablabchemnitz/inklin/inklin.py new file mode 100644 index 0000000..9a8dee3 --- /dev/null +++ b/extensions/fablabchemnitz/inklin/inklin.py @@ -0,0 +1,251 @@ +#!/usr/bin/env python3 +# +# Copyright (C) [2021] [Matt Cottam], [mpcottam@raincloud.co.uk] +# +# 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. +# + +############################################################################## +# Inklin - a collection of things I 'ave an inkling might be useful to someone. +############################################################################## + + +import inkex +from inkex import Group +import random +from lxml import etree +import math +import gi +import io + +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk +from gi.repository import Gtk, GdkPixbuf, Gdk +from gi.repository.GdkPixbuf import Pixbuf, InterpType + + +def group_wrapper(self, my_objects): + group_id = 'g' + str(random.randrange(100000, 1000000)) + + new_group = self.svg.add(Group.new('#' + group_id)) + # inkex set, takes account of NS attribs etc + new_group.set('inkscape:groupmode', 'layer') + new_group.set('inkscape:label', 'My_Layer_' + group_id) + + for my_object in my_objects: + new_group.append(my_object) + + new_group.attrib['id'] = group_id + + +def svg_arcs(cx, cy, radius, sectors, arc_radius1, arc_radius2, arc_x_rotate, arc_large_flag, arc_sweep_flag, + outer_circle_checkbutton_bool, point_circles_checkbutton_bool, numbering_checkbutton_bool): + + x_start = cx + y_start = cy - radius + + point_circles = '' + point_labels = '' + + angle = 0 + + y_start = cy / 2 + (radius * (math.sin(angle))) + x_start = cx / 2 + (radius * (math.cos(angle))) + + arcs = f'M {x_start} {y_start}' + + for sector in range(1, sectors + 1): + angle = (sector * math.pi) / (sectors / 2) + + y = cy / 2 + (radius * (math.sin(angle))) + x = cx / 2 + (radius * (math.cos(angle))) + + x_start = x + y_start = y + + + # A rx ry x-axis-rotation large-arc-flag sweep-flag x y + + arcs = arcs + f'A {arc_radius1} {arc_radius2} {arc_x_rotate} {arc_large_flag} {arc_sweep_flag} {x} {y} ' + + if not point_circles_checkbutton_bool: + point_circles = '' + else: + point_circles = point_circles + f'' + + if not numbering_checkbutton_bool: + None + else: + point_labels = point_labels + f'{sector}' + + if not outer_circle_checkbutton_bool: + outline_circle = '' + else: + outline_circle = f'' + + # svg = f'' \ + f'{outline_circle}' \ + f'{point_circles}' \ + f'{point_labels}' \ + f'' \ + f'' + + LoadSvg.master_svg = svg + + gtk3_add_svg_image(svg) + + +def gtk3_add_svg_image(svg): + loader = GdkPixbuf.PixbufLoader() + loader.write(svg.encode()) + loader.close() + pixbuf = loader.get_pixbuf() + + # pixbuf = pixbuf.scale_simple(500, 500, InterpType.BILINEAR) + + LoadSvg.preview_image.set_from_pixbuf(pixbuf) + + LoadSvg.preview_image.show_all() + + +def init_arc(): + radius = LoadSvg.builder.get_object('radius_gtk_scale').get_value() + arc_radius1 = LoadSvg.builder.get_object('arc_radius1_gtk_scale').get_value() + arc_radius2 = LoadSvg.builder.get_object('arc_radius2_gtk_scale').get_value() + arc_x_rotate_checkbutton1_bool = LoadSvg.builder.get_object('arc_x_rotate_gtk_checkbutton').get_active() + arc_radius_checkbutton1_bool = LoadSvg.builder.get_object('arc_radius1_gtk_checkbutton').get_active() + arc_radius_checkbutton2_bool = LoadSvg.builder.get_object('arc_radius2_gtk_checkbutton').get_active() + sectors = int(LoadSvg.builder.get_object('sectors_gtk_scale').get_value()) + arc_large_flag = LoadSvg.builder.get_object('arc_large_flag_gtk_checkbutton').get_active() + arc_sweep_flag = LoadSvg.builder.get_object('arc_sweep_flag_gtk_checkbutton').get_active() + + outer_circle_checkbutton_bool = LoadSvg.builder.get_object('outer_circle_gtk_checkbutton').get_active() + point_circles_checkbutton_bool = LoadSvg.builder.get_object('point_circles_gtk_checkbutton').get_active() + numbering_checkbutton_bool = LoadSvg.builder.get_object('numbering_gtk_checkbutton').get_active() + + if not arc_radius_checkbutton1_bool: + arc_radius1 = radius + LoadSvg.builder.get_object('arc_radius1_gtk_scale').set_value(radius) + + if not arc_radius_checkbutton2_bool: + arc_radius2 = radius + LoadSvg.builder.get_object('arc_radius2_gtk_scale').set_value(radius) + + if not arc_x_rotate_checkbutton1_bool: + arc_x_rotate = 0 + else: + arc_x_rotate = LoadSvg.builder.get_object('arc_x_rotate_gtk_scale').get_value() + + if not arc_sweep_flag: + arc_sweep_flag = 0 + else: + arc_sweep_flag = 1 + + if not arc_large_flag: + arc_large_flag = 0 + else: + arc_large_flag = 1 + + + svg_arcs(500, 500, radius, sectors, arc_radius1, arc_radius2, arc_x_rotate, arc_large_flag, arc_sweep_flag, outer_circle_checkbutton_bool, point_circles_checkbutton_bool, numbering_checkbutton_bool) + + # inkex.errormsg(f'arc1 {arc_radius_checkbutton1_bool} arc2 {arc_radius_checkbutton2_bool}') + + +######################################################## +# Gtk Section # +######################################################## + +class Handler: + def onDestroy(self, *args): + Gtk.main_quit() + + def onButtonPressed(self, button): + print("Hello World!") + + # def arcButtonPressed(self, button): + # svg_arcs(500, 500, 50, 8) + # test_print() + + def onScaleChangeRadius(self, scale): + init_arc() + + def onScaleChangeSides(self, scale): + init_arc() + + def onScaleChangeArcRadius(self, scale): + init_arc() + + def arcRadiusCheckbuttonChange(self, scale): + init_arc() + + def onScaleChangeXRotate(self, scale): + init_arc() + + def arcXRotateCheckbuttonChange(self, scale): + init_arc() + + def arcSweepFlagCheckbuttonChange(self, scale): + init_arc() + + def arcLargeFlagCheckbuttonChange(self, scale): + init_arc() + + def outerCircleCheckbuttonChange(self, scale): + init_arc() + + def pointCirclesCheckbuttonChange(self, scale): + init_arc() + + def numberingCheckbuttonChange(self, scale): + init_arc() + + +def run_gtk(): + LoadSvg.builder = Gtk.Builder() + LoadSvg.builder.add_from_file("inklin.glade") + LoadSvg.builder.connect_signals(Handler()) + + LoadSvg.window = LoadSvg.builder.get_object("main_window") + LoadSvg.window.show_all() + LoadSvg.window.set_title('Inklin') + + LoadSvg.preview_image = LoadSvg.builder.get_object('preview_image') + + init_arc() + + Gtk.main() + + +######################################################## +# Inkex effect section # +######################################################## + +class LoadSvg(inkex.EffectExtension): + + def effect(self): + run_gtk() + + svg_etree = etree.fromstring(LoadSvg.master_svg) + + group_wrapper(self, svg_etree) + + +if __name__ == '__main__': + LoadSvg().run() diff --git a/extensions/fablabchemnitz/inklin/meta.json b/extensions/fablabchemnitz/inklin/meta.json new file mode 100644 index 0000000..12ebcfc --- /dev/null +++ b/extensions/fablabchemnitz/inklin/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Inklin", + "id": "fablabchemnitz.de.inklin", + "path": "inklin", + "dependent_extensions": null, + "original_name": "Inklin", + "original_id": "org.inkscape.inklin", + "license": "GNU GPL v3", + "license_url": "https://inkscape.org/de/~inklinea/%E2%98%85inklin", + "comment": "", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/inklin", + "fork_url": "https://inkscape.org/de/~inklinea/%E2%98%85inklin", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Inklin", + "inkscape_gallery_url": null, + "main_authors": [ + "inkscape.org/inklinea", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/inkpacking/inkpacking.inx b/extensions/fablabchemnitz/inkpacking/inkpacking.inx new file mode 100644 index 0000000..97e83f9 --- /dev/null +++ b/extensions/fablabchemnitz/inkpacking/inkpacking.inx @@ -0,0 +1,93 @@ + + + InkPACKING + fablabchemnitz.de.inkpacking + + + 40 + 30 + 80 + 0.5 + + + + + + + + + true + + + + + + + + true + 0.6 + 0 + 14 + 18 + false + false + 5 + + + + + 0 + 2 + 7 + 3 + 3 + 12 + + + true + 0 + 2 + 7 + 3 + 3 + 12 + + + 13 + 12 + false + + + + + + + + + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/inkpacking/inkpacking.py b/extensions/fablabchemnitz/inkpacking/inkpacking.py new file mode 100644 index 0000000..3698dad --- /dev/null +++ b/extensions/fablabchemnitz/inkpacking/inkpacking.py @@ -0,0 +1,776 @@ +#!/usr/bin/env python3 +''' +Copyleft ( ) 2009 Celso Junior celsojr2008 at gmail dot com>, + 2015 Maren Hachmann (updated for Inkscape 0.91) +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 + + +TODO: Mailer Lock; Tuck and Tongle; Gusset Tuck; Arthur Lock +Dust Flaps; Edge Lock; Houghland Snap Lock Bottom +(semi automatic); Quad Lock Bottom; Himes Lock (Automatic Bottom); Security Zipper + +''' + +import inkex +from math import * +from lxml import etree +from inkex.paths import Path + +class inkpacking(inkex.EffectExtension): + + def add_arguments(self, pars): + pars.add_argument("--pages") + pars.add_argument("--dustpages") + pars.add_argument("--width", type=float, default=10.0) + pars.add_argument("--height", type=float, default=15.0) + pars.add_argument("--depth", type=float, default=3.0) + pars.add_argument("--unit", default="mm") + pars.add_argument("--topscheme", default="rwlf") + pars.add_argument("--botscheme", default="rwlf") + pars.add_argument("--paper_thickness", type=float, default=0.5) + pars.add_argument("--tab_proportion", type=float, default=14, help="Inner tab propotion for upper tab") + pars.add_argument("--lockroundradius", type=float, default=18, help="Lock Radius") + pars.add_argument("--clueflapsize", type=float, default=13, help="Clue Flap Size") + pars.add_argument("--clueflapangle", type=float, default=12, help="Clue Flap Angle") + pars.add_argument("--clueflapside", type=inkex.Boolean, default=False) + pars.add_argument("--tfal", type=inkex.Boolean, default=False) + pars.add_argument("--bfal", type=inkex.Boolean, default=False) + pars.add_argument("--hotmeltprop", type=float, default=0.6) + pars.add_argument("--createshapes", type=inkex.Boolean, default=False) + pars.add_argument("--createglueshapes", type=inkex.Boolean, default=False) + pars.add_argument("--fingergrepa", type=inkex.Boolean, default=False) + pars.add_argument("--fingergrepb", type=inkex.Boolean, default=False) + pars.add_argument("--fingergrepr", type=float, default=5) + pars.add_argument("--usetop", type=inkex.Boolean, default=False) + pars.add_argument("--glueflapinoff", type=float, default=0) + pars.add_argument("--glueflapin45", type=float, default=2) + pars.add_argument("--glueflapinang", type=float, default=7) + pars.add_argument("--glueflapouoff", type=float, default=0) + pars.add_argument("--glueflapou45", type=float, default=3) + pars.add_argument("--glueflapouang", type=float, default=12) + pars.add_argument("--bglueflapinoff", type=float, default=0) + pars.add_argument("--bglueflapin45", type=float, default=2) + pars.add_argument("--bglueflapinang", type=float, default=7) + pars.add_argument("--bglueflapouoff", type=float, default=0) + pars.add_argument("--bglueflapou45", type=float, default=3) + pars.add_argument("--bglueflapouang", type=float, default=12) + pars.add_argument("--roto", type=float, default=0) + + def effect(self): + docW = self.svg.unittouu(self.document.getroot().get('width')) + docH = self.svg.unittouu(self.document.getroot().get('height')) + roto = self.svg.unittouu( str(self.options.roto) + self.options.unit ) + boxW = self.svg.unittouu( str(self.options.width) + self.options.unit ) + boxH = self.svg.unittouu( str(self.options.height) + self.options.unit ) + boxD = self.svg.unittouu( str(self.options.depth) + self.options.unit ) + boxL = self.svg.unittouu( str(self.options.tab_proportion) + self.options.unit ) + thck = self.svg.unittouu( str(self.options.paper_thickness) + self.options.unit ) + fingergrepr = self.svg.unittouu( str(self.options.fingergrepr) + self.options.unit ) + gflapsize = self.svg.unittouu( str(self.options.clueflapsize) + self.options.unit ) + gflapangle = self.options.clueflapangle + gfmirror = self.options.clueflapside + fingergrepa = self.options.fingergrepa + fingergrepb = self.options.fingergrepb + gflapangle = 90 - gflapangle + usetop = self.options.usetop + glueflapinoff = self.svg.unittouu( str(self.options.glueflapinoff) + self.options.unit ) + glueflapin45 = self.svg.unittouu( str(self.options.glueflapin45) + self.options.unit ) + lockrr = self.svg.unittouu( str(self.options.lockroundradius) + self.options.unit ) + glueflapinang = self.options.glueflapinang + glueflapindesl = (( (boxD + boxL) / 2 - glueflapinoff - glueflapin45) / sin(radians(90 - glueflapinang)) * sin(radians(glueflapinang))) + glueflapouoff = self.svg.unittouu( str(self.options.glueflapouoff) + self.options.unit ) + glueflapou45 = self.svg.unittouu( str(self.options.glueflapou45) + self.options.unit ) + glueflapouang = self.options.glueflapouang + glueflapoudesl = (( (boxD + boxL) / 2 - glueflapouoff - glueflapou45) / sin(radians(90 - glueflapouang)) * sin(radians(glueflapouang))) + bglueflapinoff = self.svg.unittouu( str(self.options.bglueflapinoff) + self.options.unit ) + bglueflapin45 = self.svg.unittouu( str(self.options.bglueflapin45) + self.options.unit ) + bglueflapinang = self.options.bglueflapinang + bglueflapindesl = (( (boxD + boxL) / 2 - bglueflapinoff - bglueflapin45) / sin(radians(90 - bglueflapinang)) * sin(radians(bglueflapinang))) + bglueflapouoff = self.svg.unittouu( str(self.options.bglueflapouoff) + self.options.unit ) + bglueflapou45 = self.svg.unittouu( str(self.options.bglueflapou45) + self.options.unit ) + bglueflapouang = self.options.bglueflapouang + bglueflapoudesl = (( (boxD + boxL) / 2 - bglueflapouoff - bglueflapou45) / sin(radians(90 - bglueflapouang)) * sin(radians(bglueflapouang))) + tpsc = self.options.topscheme + btsc = self.options.botscheme + tfal = self.options.tfal + bfal = self.options.bfal + hotmeltp = self.options.hotmeltprop + + angx = asin( (boxL - thck) / lockrr ) + angy = (3.141615 / 2) - angx + lockroff = lockrr - (lockrr * sin(angy)) + + box_id = self.svg.get_unique_id('box') + self.box = g = etree.SubElement(self.svg.get_current_layer(), 'g', {'id':box_id}) + cut_line_style = str(inkex.Style(({ 'stroke': '#000000', 'fill': 'none', 'stroke-width':'0.3px' }))) + engrave_line_style = str(inkex.Style(({ 'stroke': '#ff0000', 'fill': 'none', 'stroke-width':'0.3px' }))) + + gflapoffy = (gflapsize / sin( (gflapangle / 360) * 6.28 )) * sin( ((90 - gflapangle) / 360 ) * 6.28) + + # Side Glueflap + if not gfmirror: + line_path = [ + [ 'M', [0, 0]], + [ 'L', [gflapsize * -1, gflapoffy]], + [ 'L', [gflapsize * -1, boxH - gflapoffy]], + [ 'L', [0, boxH]], + [ 'M', [0, 0]] + ] + + if gfmirror: + line_path = [ + [ 'M', [boxW+boxD+boxW+boxD - thck, 0]], + [ 'L', [boxW+boxD+boxW+boxD - thck + gflapsize, gflapoffy]], + [ 'L', [boxW+boxD+boxW+boxD - thck + gflapsize, boxH - gflapoffy]], + [ 'L', [boxW+boxD+boxW+boxD - thck, boxH]], + [ 'M', [boxW+boxD+boxW+boxD - thck, 0]] + ] + + + line_atts = { 'style':cut_line_style, 'id':box_id+'-sideglueflap', 'd':str(Path(line_path)) } + etree.SubElement(g, inkex.addNS('path','svg'), line_atts ) + + + # MainBody + line_path = [ + [ 'M', [0, 0]], + [ 'L', [0, boxH]] + ] + if gfmirror: + style = cut_line_style + else: + style = engrave_line_style + line_atts = { 'style':style, 'id':box_id+'-body-left', 'd':str(Path(line_path)) } + etree.SubElement(g, inkex.addNS('path','svg'), line_atts ) + + line_path = [ + [ 'M', [boxW, 0]], + [ 'L', [boxW, boxH]], + + [ 'M', [boxW + boxD, 0]], + [ 'L', [boxW + boxD, boxH]], + + [ 'M', [boxW + boxD + boxW, 0]], + [ 'L', [boxW + boxD + boxW, boxH]] + ] + line_atts = { 'style':engrave_line_style, 'id':box_id+'-body-mid', 'd':str(Path(line_path)) } + etree.SubElement(g, inkex.addNS('path','svg'), line_atts ) + + if gfmirror: + style = engrave_line_style + else: + style = cut_line_style + line_path = [ + [ 'M', [boxW + boxD + boxW + boxD - thck, 0]], + [ 'L', [boxW + boxD + boxW + boxD - thck, boxH]], + ] + line_atts = { 'style':style, 'id':box_id+'-body-left', 'd':str(Path(line_path)) } + etree.SubElement(g, inkex.addNS('path','svg'), line_atts ) + + # No Top Option + if tpsc == "notp": + if not fingergrepa and not fingergrepb: + line_path = [ + [ 'M', [0, 0]], + [ 'L', [boxW, 0]], + [ 'L', [boxW + boxD, 0]], + [ 'L', [boxW + boxD + boxW, 0]], + [ 'L', [boxW + boxD + boxW + boxD - thck, 0]] + ] + if fingergrepa and not fingergrepb: + line_path = [ + [ 'M', [0, 0]], + [ 'L', [boxW / 2 - fingergrepr, 0]], + [ 'A', [fingergrepr, fingergrepr, 0, 1, 0, boxW - boxW / 2 + fingergrepr, 0]], + [ 'L', [boxW, 0]], + [ 'L', [boxW + boxD, 0]], + [ 'L', [boxW + boxD + boxW / 2 - fingergrepr, 0]], + [ 'A', [fingergrepr, fingergrepr, 0, 1, 0, boxW + boxD + boxW - boxW / 2 + fingergrepr, 0]], + [ 'L', [boxW + boxD + boxW, 0]], + [ 'L', [boxW + boxD + boxW + boxD - thck, 0]] + ] + if fingergrepa and fingergrepb: + line_path = [ + [ 'M', [0, 0]], + [ 'L', [boxW / 2 - fingergrepr, 0]], + [ 'A', [fingergrepr, fingergrepr, 0, 1, 0, boxW - boxW / 2 + fingergrepr, 0]], + [ 'L', [boxW, 0]], + [ 'L', [boxW + boxD / 2 - fingergrepr, 0]], + [ 'A', [fingergrepr, fingergrepr, 0, 1, 0, boxW + boxD - boxD / 2 + fingergrepr, 0]], + [ 'L', [boxW + boxD, 0]], + [ 'L', [boxW + boxD + boxW / 2 - fingergrepr, 0]], + [ 'A', [fingergrepr, fingergrepr, 0, 1, 0, boxW + boxD + boxW - boxW / 2 + fingergrepr, 0]], + [ 'L', [boxW + boxD + boxW, 0]], + [ 'L', [boxW + boxD + boxW + boxD - thck - ( (boxD - thck) / 2 ) - fingergrepr, 0]], + [ 'A', [fingergrepr, fingergrepr, 0, 1, 0, boxW + boxD + boxW - thck + boxD - ( ( boxD - thck ) / 2 ) + fingergrepr , 0]], + [ 'L', [boxW + boxD + boxW + boxD - thck, 0]] + ] + + if not fingergrepa and fingergrepb: + line_path = [ + [ 'M', [0, 0]], + [ 'L', [boxW, 0]], + [ 'L', [boxW + boxD / 2 - fingergrepr, 0]], + [ 'A', [fingergrepr, fingergrepr, 0, 1, 0, boxW + boxD - boxD / 2 + fingergrepr, 0]], + [ 'L', [boxW + boxD, 0]], + [ 'L', [boxW + boxD + boxW, 0]], + [ 'L', [boxW + boxD + boxW + boxD - thck - ( (boxD - thck) / 2 ) - fingergrepr, 0]], + [ 'A', [fingergrepr, fingergrepr, 0, 1, 0, boxW + boxD + boxW - thck + boxD - ( ( boxD - thck ) / 2 ) + fingergrepr , 0]], + [ 'L', [boxW + boxD + boxW + boxD - thck, 0]] + ] + + + line_atts = { 'style':cut_line_style, 'id':box_id+'-topdraw', 'd':str(Path(line_path)) } + etree.SubElement(g, inkex.addNS('path','svg'), line_atts ) + + # No Bottom Option + if btsc == "nobt": + if not fingergrepa and not fingergrepb: + line_path = [ + [ 'M', [0, boxH]], + [ 'L', [boxW, boxH]], + [ 'L', [boxW + boxD, boxH]], + [ 'L', [boxW + boxD + boxW, boxH]], + [ 'L', [boxW + boxD + boxW + boxD - thck, boxH]] + ] + if fingergrepa and not fingergrepb: + line_path = [ + [ 'M', [0, boxH]], + [ 'L', [boxW / 2 - fingergrepr, boxH]], + [ 'A', [fingergrepr, fingergrepr, 0, 1, 1, boxW - boxW / 2 + fingergrepr, boxH]], + [ 'L', [boxW, boxH]], + [ 'L', [boxW + boxD, boxH]], + [ 'L', [boxW + boxD + boxW / 2 - fingergrepr, boxH]], + [ 'A', [fingergrepr, fingergrepr, 0, 1, 1, boxW + boxD + boxW - boxW / 2 + fingergrepr, boxH]], + [ 'L', [boxW + boxD + boxW, boxH]], + [ 'L', [boxW + boxD + boxW + boxD - thck, boxH]], + [ 'M', [0, boxH]] + ] + if fingergrepa and fingergrepb: + line_path = [ + [ 'M', [0, boxH]], + [ 'L', [boxW / 2 - fingergrepr, boxH]], + [ 'A', [fingergrepr, fingergrepr, 0, 1, 1, boxW - boxW / 2 + fingergrepr, boxH]], + [ 'L', [boxW, boxH]], + [ 'L', [boxW + boxD / 2 - fingergrepr, boxH]], + [ 'A', [fingergrepr, fingergrepr, 0, 1, 1, boxW + boxD - boxD / 2 + fingergrepr, boxH]], + [ 'L', [boxW + boxD, boxH]], + [ 'L', [boxW + boxD + boxW / 2 - fingergrepr, boxH]], + [ 'A', [fingergrepr, fingergrepr, 0, 1, 1, boxW + boxD + boxW - boxW / 2 + fingergrepr, boxH]], + [ 'L', [boxW + boxD + boxW, boxH]], + [ 'L', [boxW + boxD + boxW + boxD - thck - ( (boxD - thck) / 2 ) - fingergrepr, boxH]], + [ 'A', [fingergrepr, fingergrepr, 0, 1, 1, boxW + boxD + boxW - thck + boxD - ( ( boxD - thck ) / 2 ) + fingergrepr , boxH]], + [ 'L', [boxW + boxD + boxW + boxD - thck, boxH]], + [ 'M', [0, boxH]] + ] + + if not fingergrepa and fingergrepb: + line_path = [ + [ 'M', [0, boxH]], + [ 'L', [boxW, boxH]], + [ 'L', [boxW + boxD / 2 - fingergrepr, boxH]], + [ 'A', [fingergrepr, fingergrepr, 0, 1, 1, boxW + boxD - boxD / 2 + fingergrepr, boxH]], + [ 'L', [boxW + boxD, boxH]], + [ 'L', [boxW + boxD + boxW, boxH]], + [ 'L', [boxW + boxD + boxW + boxD - thck - ( (boxD - thck) / 2 ) - fingergrepr, boxH]], + [ 'A', [fingergrepr, fingergrepr, 0, 1, 1, boxW + boxD + boxW - thck + boxD - ( ( boxD - thck ) / 2 ) + fingergrepr , boxH]], + [ 'L', [boxW + boxD + boxW + boxD - thck, boxH]], + [ 'M', [0, boxH]] + ] + + line_atts = { 'style':cut_line_style, 'id':box_id+'-botdraw', 'd':str(Path(line_path)) } + etree.SubElement(g, inkex.addNS('path','svg'), line_atts ) + + # Flat Bottom with Lock Flaps + if btsc == "fwlf": + desloc = 0 + inicut = boxW + boxD + if not bfal: + desloc = boxW + boxD + inicut = 0 + line_path = [ + [ 'M', [desloc,boxH]], + [ 'l', [0, boxD]], + [ 'm', [6, 0]], + [ 'l', [0, thck * -2]], + [ 'm', [0, thck]], + [ 'l', [boxW - 12, 0]], + [ 'm', [0, thck * -1]], + [ 'l', [0, thck * 2]], + [ 'm', [6, 0]], + [ 'l', [0, boxD * -1]], + [ 'M', [desloc, boxH]], + [ 'm', [0, boxD *1]], + [ 'm', [thck, 0]], + [ 'l', [lockroff, (boxL - thck)]], + [ 'l', [boxW - lockroff - lockroff - thck - thck, 0]], + [ 'l', [lockroff, (boxL - thck) * -1]] + ] + line_atts = { 'style':cut_line_style, 'id':box_id+'-bothead', 'd':str(Path(line_path)) } + etree.SubElement(g, inkex.addNS('path','svg'), line_atts ) + + line_path = [ + [ 'M', [desloc, boxH + boxD]], + [ 'l', [6, 0]], + [ 'M', [desloc + boxW - 6, boxH + boxD]], + [ 'l', [6, 0]], + [ 'M', [desloc,boxH + thck]], + [ 'l', [boxW,0]] + ] + line_atts = { 'style':engrave_line_style, 'id':box_id+'-bothead', 'd':str(Path(line_path)) } + etree.SubElement(g, inkex.addNS('path','svg'), line_atts ) + + if fingergrepa: + line_path = [ + ["M", [inicut, boxH]], + ["l", [boxW / 2 - fingergrepr, 0]], + ["a", [fingergrepr, fingergrepr, 0, 0, 1, fingergrepr * 2,0]], + ["l", [boxW / 2 - fingergrepr, 0]] + ] + line_atts = { 'style':cut_line_style, 'id':box_id+'-botcut', 'd':str(Path(line_path)) } + etree.SubElement(g, inkex.addNS('path','svg'), line_atts ) + if not fingergrepa: + line_path = [ + ["M", [inicut, boxH]], + ["l", [boxW, 0]] + ] + line_atts = { 'style':cut_line_style, 'id':box_id+'-botcut', 'd':str(Path(line_path)) } + etree.SubElement(g, inkex.addNS('path','svg'), line_atts ) + + + # Flat Top with Lock Flaps + if tpsc == "fwlf": + desloc = 0 + inicut = boxW + boxD + if not tfal: + desloc = boxW + boxD + inicut = 0 + line_path = [ + [ 'M', [desloc,0]], + [ 'l', [0, boxD * -1]], + [ 'm', [6, 0]], + [ 'l', [0, thck * 2]], + [ 'm', [0, thck * -1]], + [ 'l', [boxW - 12, 0]], + [ 'm', [0, thck]], + [ 'l', [0, thck * -2]], + [ 'm', [6, 0]], + [ 'l', [0, boxD]], + [ 'M', [desloc, 0]], + [ 'm', [0, boxD *-1]], + [ 'm', [thck, 0]], + [ 'l', [lockroff, (boxL - thck) * -1]], + [ 'l', [boxW - lockroff - lockroff - thck - thck, 0]], + [ 'l', [lockroff, (boxL - thck)]] + + ] + line_atts = { 'style':cut_line_style, 'id':box_id+'-tophead', 'd':str(Path(line_path)) } + etree.SubElement(g, inkex.addNS('path','svg'), line_atts ) + + line_path = [ + [ 'M', [desloc, boxD * -1]], + [ 'l', [6, 0]], + [ 'M', [desloc + boxW - 6, boxD * -1]], + [ 'l', [6, 0]], + [ 'M', [desloc,thck * -1]], + [ 'l', [boxW,0]] + ] + line_atts = { 'style':engrave_line_style, 'id':box_id+'-tophead', 'd':str(Path(line_path)) } + etree.SubElement(g, inkex.addNS('path','svg'), line_atts ) + + if fingergrepa: + line_path = [ + ["M", [inicut,0]], + ["l", [boxW / 2 - fingergrepr, 0]], + ["a", [fingergrepr, fingergrepr, 0, 0, 0, fingergrepr * 2,0]], + ["l", [boxW / 2 - fingergrepr, 0]] + ] + line_atts = { 'style':cut_line_style, 'id':box_id+'-topcut', 'd':str(Path(line_path)) } + etree.SubElement(g, inkex.addNS('path','svg'), line_atts ) + if not fingergrepa: + line_path = [ + ["M", [inicut,0]], + ["l", [boxW, 0]] + ] + line_atts = { 'style':cut_line_style, 'id':box_id+'-topcut', 'd':str(Path(line_path)) } + etree.SubElement(g, inkex.addNS('path','svg'), line_atts ) + + # Rounded Bottom with Lock Flaps + if btsc == "rwlf": + desloc = 0 + inicut = boxW + boxD + if not bfal: + desloc = boxW + boxD + inicut = 0 + line_path = [ + [ 'M', [desloc,boxH]], + [ 'l', [0, boxD]], + [ 'm', [6, 0]], + [ 'l', [0, thck * -2]], + [ 'm', [0, thck]], + [ 'l', [boxW - 12, 0]], + [ 'm', [0, thck * -1]], + [ 'l', [0, thck * 2]], + [ 'm', [6, 0]], + [ 'l', [0, boxD * -1]], + [ 'M', [desloc, boxH]], + [ 'm', [0, boxD]], + [ 'm', [thck, 0]], + [ 'a', [lockrr , lockrr, 0, 0, 0, lockroff, (boxL - thck)]], + [ 'l', [boxW - lockroff - lockroff - thck - thck, 0]], + [ 'a', [lockrr, lockrr, roto, 0, 0, lockroff, (boxL - thck) * - 1]] + ] + line_atts = { 'style':cut_line_style, 'id':box_id+'-bothead', 'd':str(Path(line_path)) } + etree.SubElement(g, inkex.addNS('path','svg'), line_atts ) + + line_path = [ + [ 'M', [desloc, boxH + boxD]], + [ 'l', [6, 0]], + [ 'M', [desloc + boxW - 6, boxH + boxD]], + [ 'l', [6, 0]], + [ 'M', [desloc,boxH + thck]], + [ 'l', [boxW,0]] + ] + line_atts = { 'style':engrave_line_style, 'id':box_id+'-bothead', 'd':str(Path(line_path)) } + etree.SubElement(g, inkex.addNS('path','svg'), line_atts ) + + if fingergrepa: + line_path = [ + ["M", [inicut,boxH]], + ["l", [boxW / 2 - fingergrepr, 0]], + ["a", [fingergrepr, fingergrepr, 0, 0, 1, fingergrepr * 2,0]], + ["l", [boxW / 2 - fingergrepr, 0]] + ] + line_atts = { 'style':cut_line_style, 'id':box_id+'-botcut', 'd':str(Path(line_path)) } + etree.SubElement(g, inkex.addNS('path','svg'), line_atts ) + if not fingergrepa: + line_path = [ + ["M", [inicut,boxH]], + ["l", [boxW, 0]] + ] + line_atts = { 'style':cut_line_style, 'id':box_id+'-botcut', 'd':str(Path(line_path)) } + etree.SubElement(g, inkex.addNS('path','svg'), line_atts ) + + # Rounded Top with Lock Flaps + if tpsc == "rwlf": + desloc = 0 + inicut = boxW + boxD + if not tfal: + desloc = boxW + boxD + inicut = 0 + line_path = [ + [ 'M', [desloc,0]], + [ 'l', [0, boxD * -1]], + [ 'm', [6, 0]], + [ 'l', [0, thck * 2]], + [ 'm', [0, thck * -1]], + [ 'l', [boxW - 12, 0]], + [ 'm', [0, thck]], + [ 'l', [0, thck * -2]], + [ 'm', [6, 0]], + [ 'l', [0, boxD]], + [ 'M', [desloc, 0]], + [ 'm', [0, boxD *-1]], + [ 'm', [thck, 0]], + [ 'a', [lockrr , lockrr, 0, 0, 1, lockroff, (boxL - thck) * -1]], + [ 'l', [boxW - lockroff - lockroff - thck - thck, 0]], + [ 'a', [lockrr, lockrr, roto, 0, 1, lockroff, (boxL - thck)]] + ] + line_atts = { 'style':cut_line_style, 'id':box_id+'-tophead', 'd':str(Path(line_path)) } + etree.SubElement(g, inkex.addNS('path','svg'), line_atts ) + + line_path = [ + [ 'M', [desloc, boxD * -1]], + [ 'l', [6, 0]], + [ 'M', [desloc + boxW - 6, boxD * -1]], + [ 'l', [6, 0]], + [ 'M', [desloc,thck * -1]], + [ 'l', [boxW,0]] + ] + line_atts = { 'style':engrave_line_style, 'id':box_id+'-tophead', 'd':str(Path(line_path)) } + etree.SubElement(g, inkex.addNS('path','svg'), line_atts ) + if fingergrepa: + line_path = [ + ["M", [inicut,0]], + ["l", [boxW / 2 - fingergrepr, 0]], + ["a", [fingergrepr, fingergrepr, 0, 0, 0, fingergrepr * 2,0]], + ["l", [boxW / 2 - fingergrepr, 0]] + ] + line_atts = { 'style':cut_line_style, 'id':box_id+'-topcut', 'd':str(Path(line_path)) } + etree.SubElement(g, inkex.addNS('path','svg'), line_atts ) + if not fingergrepa: + line_path = [ + ["M", [inicut,0]], + ["l", [boxW, 0]] + ] + line_atts = { 'style':cut_line_style, 'id':box_id+'-topcut', 'd':str(Path(line_path)) } + etree.SubElement(g, inkex.addNS('path','svg'), line_atts ) + + # HotMelt Top + if tpsc == "fwnf": + if tfal: + cut_line_path = [ + [ 'M', [0, 0]], + [ 'L', [0, boxD * -1]], + [ 'L', [boxW, boxD * -1]], + [ 'L', [boxW, 0]], + [ 'M', [boxW, boxD * -1 / 2]], + [ 'M', [boxW + boxD, boxD * -1 / 2]], + [ 'M', [boxW + boxD, 0]], + [ 'L', [boxW + boxD, boxD * -1 * hotmeltp]], + [ 'L', [boxW + boxD + boxW, boxD * -1 * hotmeltp]], + [ 'L', [boxW + boxD + boxW, 0]], + [ 'M', [boxW + boxD + boxW, boxD * -1 /2]], + [ 'M', [boxW + boxD + boxW + boxD - thck, boxD * -1 /2]], + [ 'M', [boxW + boxD + boxW + boxD - thck, 0]] + ] + engrave_line_path = [ + [ 'M', [0, 0 - thck]], + [ 'L', [boxW, 0 - thck]], + [ 'M', [boxW + boxD, 0 - thck]], + [ 'L', [boxW + boxD + boxW, 0 - thck]] + ] + if not tfal: + cut_line_path = [ + [ 'M', [0, 0]], + [ 'L', [0, boxD * -1 * hotmeltp]], + [ 'L', [boxW, boxD * -1 * hotmeltp]], + [ 'L', [boxW, 0]], + [ 'M', [boxW, boxD * -1 / 2]], + [ 'M', [boxW + boxD, boxD * -1 / 2]], + [ 'M', [boxW + boxD, 0]], + [ 'L', [boxW + boxD, boxD * -1]], + [ 'L', [boxW + boxD + boxW, boxD * -1]], + [ 'L', [boxW + boxD + boxW, 0]], + [ 'M', [boxW + boxD + boxW, boxD * -1 /2]], + [ 'M', [boxW + boxD + boxW + boxD - thck, boxD * -1 /2]], + [ 'M', [boxW + boxD + boxW + boxD - thck, 0]] + ] + engrave_line_path = [ + [ 'M', [0, 0 - thck]], + [ 'L', [boxW, 0 - thck]], + [ 'M', [boxW + boxD, 0 - thck]], + [ 'L', [boxW + boxD + boxW, 0 - thck]] + ] + line_atts = { 'style':cut_line_style, 'id':box_id+'-topdraw_cut', 'd':str(Path(cut_line_path)) } + etree.SubElement(g, inkex.addNS('path','svg'), line_atts ) + line_atts = { 'style':engrave_line_style, 'id':box_id+'-topdraw_engrave', 'd':str(Path(engrave_line_path)) } + etree.SubElement(g, inkex.addNS('path','svg'), line_atts ) + # HotMelt Bottom + if btsc == "fwnf": + if bfal: + cut_line_path = [ + [ 'M', [0, boxH]], + [ 'L', [0, boxH + boxD]], + [ 'L', [boxW, boxH + boxD]], + [ 'L', [boxW, boxH]], + [ 'M', [boxW, boxH + boxD / 2]], + [ 'M', [boxW + boxD, boxH + boxD / 2]], + [ 'M', [boxW + boxD, boxH]], + [ 'L', [boxW + boxD, boxH + boxD * hotmeltp]], + [ 'L', [boxW + boxD + boxW, boxH + boxD * hotmeltp]], + [ 'L', [boxW + boxD + boxW, boxH]], + [ 'M', [boxW + boxD + boxW, boxH + boxD /2]], + [ 'M', [boxW + boxD + boxW + boxD - thck, boxH + boxD /2]], + [ 'M', [boxW + boxD + boxW + boxD - thck, boxH]] + + ] + engrave_line_path = [ + [ 'M', [0, boxH + thck]], + [ 'L', [boxW, boxH + thck]], + [ 'M', [boxW + boxD, boxH + thck]], + [ 'L', [boxW + boxD + boxW, boxH + thck]] + ] + if not bfal: + cut_line_path = [ + [ 'M', [0, boxH]], + [ 'L', [0, boxH + boxD * hotmeltp]], + [ 'L', [boxW, boxH + boxD * hotmeltp]], + [ 'L', [boxW, boxH]], + [ 'M', [boxW, boxH + boxD / 2]], + [ 'M', [boxW + boxD, boxH + boxD / 2]], + [ 'M', [boxW + boxD, boxH]], + [ 'L', [boxW + boxD, boxH + boxD]], + [ 'L', [boxW + boxD + boxW, boxH + boxD]], + [ 'L', [boxW + boxD + boxW, boxH]], + [ 'M', [boxW + boxD + boxW, boxH + boxD /2]], + [ 'M', [boxW + boxD + boxW + boxD - thck, boxH + boxD /2]], + [ 'M', [boxW + boxD + boxW + boxD - thck, boxH]] + ] + engrave_line_path = [ + [ 'M', [0, boxH + thck]], + [ 'L', [boxW, boxH + thck]], + [ 'M', [boxW + boxD, boxH + thck]], + [ 'L', [boxW + boxD + boxW, boxH + thck]] + ] + line_atts = { 'style':cut_line_style, 'id':box_id+'-botdraw_cut', 'd':str(Path(cut_line_path)) } + etree.SubElement(g, inkex.addNS('path','svg'), line_atts ) + line_atts = { 'style':engrave_line_style, 'id':box_id+'-botdraw_engrave', 'd':str(Path(engrave_line_path)) } + etree.SubElement(g, inkex.addNS('path','svg'), line_atts ) + thck2 = thck / 2 + + # Top Glue Flaps + if tpsc != "notp": + desclock = thck + if tpsc == "fwnf": + desclock = 0 + if tfal: + cut_line_path = [ + [ 'M', [boxW, 0]], + [ 'l', [0, glueflapinoff * -1]], + [ 'l', [glueflapin45, glueflapin45 * -1]], + [ 'l', [glueflapindesl, ((boxD + boxL) / 2 - glueflapinoff - glueflapin45) * -1 + thck2]], + [ 'M', [boxW + boxD, 0]], + [ 'l', [desclock * -1, 0]], + [ 'l', [0, glueflapouoff * -1]], + [ 'l', [glueflapou45 * -1, glueflapou45 * -1]], + [ 'l', [glueflapoudesl * -1, ((boxD + boxL) / 2 - glueflapouoff - glueflapou45) * -1 + thck2]], + [ 'l', [(boxD - glueflapindesl - glueflapoudesl - glueflapin45 - glueflapou45 - desclock) * -1, 0]], + [ 'M', [boxW + boxW + boxD + boxD - thck, 0]], + [ 'l', [0, glueflapinoff * -1]], + [ 'l', [glueflapin45 * -1, glueflapin45 * -1]], + [ 'l', [glueflapindesl * -1, ((boxD + boxL) / 2 - glueflapinoff - glueflapin45) * -1 + thck2]], + [ 'M', [boxW + boxD + boxW, 0]], + [ 'l', [desclock, 0]], + [ 'l', [0, glueflapouoff * -1]], + [ 'l', [glueflapou45, glueflapou45 * -1]], + [ 'l', [glueflapoudesl, ((boxD + boxL) / 2 - glueflapouoff - glueflapou45) * -1 + thck2]], + [ 'l', [(boxD - glueflapindesl - glueflapoudesl - glueflapin45 - glueflapou45 - desclock -thck ), 0]] + ] + engrave_line_path = [ + [ 'M', [boxW, 0]], + [ 'l', [boxD - desclock, 0]], + [ 'M', [boxW + boxD + boxW + desclock, 0]], + [ 'l', [boxD - thck - desclock, 0]] + ] + + if not tfal: + cut_line_path = [ + [ 'M', [boxW + boxD, 0]], + [ 'l', [0, glueflapinoff * -1]], + [ 'l', [glueflapin45 * -1, glueflapin45 * -1]], + [ 'l', [glueflapindesl * -1, ((boxD + boxL) / 2 - glueflapinoff - glueflapin45) * -1 + thck2]], + [ 'M', [boxW, 0]], + [ 'l', [desclock, 0]], + [ 'l', [0, glueflapouoff * -1]], + [ 'l', [glueflapou45, glueflapou45 * -1]], + [ 'l', [glueflapoudesl, ((boxD + boxL) / 2 - glueflapouoff - glueflapou45) * -1 + thck2]], + [ 'l', [(boxD - glueflapindesl - glueflapoudesl - glueflapin45 - glueflapou45 - desclock), 0]], + [ 'M', [boxW + boxD + boxW, 0]], + [ 'l', [0, glueflapinoff * -1]], + [ 'l', [glueflapin45, glueflapin45 * -1]], + [ 'l', [glueflapindesl, ((boxD + boxL) / 2 - glueflapinoff - glueflapin45) * -1 + thck2]], + [ 'M', [boxW + boxD + boxW + boxD - thck, 0]], + [ 'l', [desclock * -1, 0]], + [ 'l', [0, glueflapouoff * -1]], + [ 'l', [glueflapou45 * -1, glueflapou45 * -1]], + [ 'l', [glueflapoudesl * -1, ((boxD + boxL) / 2 - glueflapouoff - glueflapou45) * -1 + thck2]], + [ 'l', [(boxD - glueflapindesl - glueflapoudesl - glueflapin45 - glueflapou45 - desclock -thck) * -1, 0]] + ] + engrave_line_path = [ + [ 'M', [boxW + desclock, 0]], + [ 'l', [boxD - desclock, 0]], + [ 'M', [boxW + boxD + boxW, 0]], + [ 'l', [boxD -thck - desclock, 0]] + ] + + line_atts = { 'style':cut_line_style, 'id':box_id+'-topglueflap_cut', 'd':str(Path(cut_line_path)) } + etree.SubElement(g, inkex.addNS('path','svg'), line_atts ) + line_atts = { 'style':engrave_line_style, 'id':box_id+'-topglueflap_engrave', 'd':str(Path(engrave_line_path)) } + etree.SubElement(g, inkex.addNS('path','svg'), line_atts ) + + + # Bottom Glue Flaps + if not usetop: + glueflapinoff = bglueflapinoff + glueflapin45 = bglueflapin45 + glueflapindesl = bglueflapindesl + glueflapouoff = bglueflapouoff + glueflapou45 = bglueflapou45 + glueflapoudesl = bglueflapoudesl + + if btsc != "nobt": + desclock = thck + if btsc == "fwnf": + desclock = 0 + if bfal: + cut_line_path = [ + [ 'M', [boxW, boxH]], + [ 'l', [0, glueflapinoff]], + [ 'l', [glueflapin45, glueflapin45]], + [ 'l', [glueflapindesl, ((boxD + boxL) / 2 - glueflapinoff - glueflapin45) -thck2]], + [ 'M', [boxW + boxD, boxH]], + [ 'l', [desclock * -1, 0]], + [ 'l', [0, glueflapouoff]], + [ 'l', [glueflapou45 * -1, glueflapou45]], + [ 'l', [glueflapoudesl * -1, ((boxD + boxL) / 2 - glueflapouoff - glueflapou45) -thck2]], + [ 'l', [(boxD - glueflapindesl - glueflapoudesl - glueflapin45 - glueflapou45 - desclock) * -1, 0]], + [ 'M', [boxW + boxW + boxD + boxD - thck, boxH]], + [ 'l', [0, glueflapinoff]], + [ 'l', [glueflapin45 * -1, glueflapin45]], + [ 'l', [glueflapindesl * -1, ((boxD + boxL) / 2 - glueflapinoff - glueflapin45) - thck2]], + [ 'M', [boxW + boxD + boxW, boxH]], + [ 'l', [desclock, 0]], + [ 'l', [0, glueflapouoff]], + [ 'l', [glueflapou45, glueflapou45]], + [ 'l', [glueflapoudesl, ((boxD + boxL) / 2 - glueflapouoff - glueflapou45) -thck2]], + [ 'l', [(boxD - glueflapindesl - glueflapoudesl - glueflapin45 - glueflapou45 - desclock -thck ), 0]] + ] + engrave_line_path = [ + [ 'M', [boxW, boxH]], + [ 'l', [boxD - desclock, 0]], + [ 'M', [boxW + boxD + boxW + desclock, boxH]], + [ 'l', [boxD - thck - desclock, 0]] + ] + + if not bfal: + cut_line_path = [ + [ 'M', [boxW + boxD, boxH]], + + [ 'l', [0, glueflapinoff]], + [ 'l', [glueflapin45 * -1, glueflapin45]], + [ 'l', [glueflapindesl * -1, ((boxD + boxL) / 2 - glueflapinoff - glueflapin45) - thck2]], + [ 'M', [boxW, boxH]], + [ 'l', [desclock, 0]], + [ 'l', [0, glueflapouoff]], + [ 'l', [glueflapou45, glueflapou45]], + [ 'l', [glueflapoudesl, ((boxD + boxL) / 2 - glueflapouoff - glueflapou45) - thck2]], + [ 'l', [(boxD - glueflapindesl - glueflapoudesl - glueflapin45 - glueflapou45 - desclock), 0]], + [ 'M', [boxW + boxD + boxW, boxH]], + [ 'l', [0, glueflapinoff]], + [ 'l', [glueflapin45, glueflapin45]], + [ 'l', [glueflapindesl, ((boxD + boxL) / 2 - glueflapinoff - glueflapin45) - thck2]], + [ 'M', [boxW + boxD + boxW + boxD - thck, boxH]], + [ 'l', [desclock * -1, 0]], + [ 'l', [0, glueflapouoff]], + [ 'l', [glueflapou45 * -1, glueflapou45]], + [ 'l', [glueflapoudesl * -1, ((boxD + boxL) / 2 - glueflapouoff - glueflapou45) - thck2]], + [ 'l', [(boxD - glueflapindesl - glueflapoudesl - glueflapin45 - glueflapou45 - desclock -thck) * -1, 0]] + ] + engrave_line_path = [ + [ 'M', [boxW + desclock, boxH]], + [ 'l', [boxD - desclock, 0]], + [ 'M', [boxW + boxD + boxW, boxH]], + [ 'l', [boxD -thck - desclock, 0]] + ] + + line_atts = { 'style':cut_line_style, 'id':box_id+'-botglueflap_cut', 'd':str(Path(cut_line_path)) } + etree.SubElement(g, inkex.addNS('path','svg'), line_atts ) + line_atts = { 'style':engrave_line_style, 'id':box_id+'-botglueflap_engrave', 'd':str(Path(engrave_line_path)) } + etree.SubElement(g, inkex.addNS('path','svg'), line_atts ) + + +if __name__ == '__main__': + inkpacking().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/inkpacking/meta.json b/extensions/fablabchemnitz/inkpacking/meta.json new file mode 100644 index 0000000..64576cd --- /dev/null +++ b/extensions/fablabchemnitz/inkpacking/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "InkPACKING", + "id": "fablabchemnitz.de.inkpacking", + "path": "inkpacking", + "dependent_extensions": null, + "original_name": "InkPACKING", + "original_id": "org.inkscape.inkpacking", + "license": "GNU GPL v2", + "license_url": "https://github.com/Moini/inkpacking/blob/master/LICENSE", + "comment": "", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/inkpacking", + "fork_url": "https://github.com/Moini/inkpacking", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/InkPACKING", + "inkscape_gallery_url": null, + "main_authors": [ + "github.com/Moini", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/inkscape.extension.rng b/extensions/fablabchemnitz/inkscape.extension.rng new file mode 100644 index 0000000..c2bcd2f --- /dev/null +++ b/extensions/fablabchemnitz/inkscape.extension.rng @@ -0,0 +1,563 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + file + executable + extension + + + + + + + + + + + + + + + + + + + + python + perl + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + path + extensions + inx + absolute + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + all + g + path + rect + text + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + header + url + + + + + + + default + preserve + + + + + + + + + + + + + + + + + + + + + + + + + + + + expand + + + + + + + + + + + + + + + + + + + + + + + + + + int + + + + + + + + + + + + + + full + + + + + + + + + + float + + + + + + + + + + + + + + + + + + + full + + + + + + + bool + + + + + + color + + + + + colorbutton + + + + + + + + + + + + string + + + + + + + + + + multiline + + + + + + + + + + + path + + + + + file + files + folder + folders + file_new + folder_new + + + + + + + + + + + + + optiongroup + + + + + combo + radio + + + + + + + + + + + + + + + + + + + + + + + + notebook + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 1 + + + + + + + yes + no + + + + diff --git a/extensions/fablabchemnitz/knob_scale/knob_scale.inx b/extensions/fablabchemnitz/knob_scale/knob_scale.inx new file mode 100644 index 0000000..aacdb3f --- /dev/null +++ b/extensions/fablabchemnitz/knob_scale/knob_scale.inx @@ -0,0 +1,51 @@ + + + Knob Scale + fablabchemnitz.de.knob_scale + + + 0 + 0 + 100 + 300 + 1 + true + false + false + + + + + + + 2 + 10 + 1 + 5 + + + + + + + + false + 0 + 10 + 20 + 0 + 10 + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/knob_scale/knob_scale.py b/extensions/fablabchemnitz/knob_scale/knob_scale.py new file mode 100644 index 0000000..7157dd5 --- /dev/null +++ b/extensions/fablabchemnitz/knob_scale/knob_scale.py @@ -0,0 +1,250 @@ +#!/usr/bin/env python3 +''' +Copyright (C) 2017 Artem Synytsyn a.synytsyn@gmail.com + +#TODO: Code cleaning and refactoring + +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. +''' + +import inkex +from lxml import etree +from math import * + +class KnobScale(inkex.EffectExtension): + + def add_arguments(self, pars): + # General settings + pars.add_argument("--x", type=float, default=0.0, help="Center X") + pars.add_argument("--y", type=float, default=0.0, help="Center Y") + pars.add_argument("--radius", type=float, default=100.0, help="Knob radius") + pars.add_argument("--linewidth", type=float, default=1) + pars.add_argument("--angle", type=float, default=260.0, help="Angle of the knob scale in degrees") + pars.add_argument("--draw_arc", type=inkex.Boolean, default='True') + pars.add_argument("--draw_centering_circle", type=inkex.Boolean, default='False') + pars.add_argument("--logarithmic_scale", type=inkex.Boolean, default='False', help="") + pars.add_argument("-u", "--units", default="px", help="units to measure size of knob") + + # Tick settings + pars.add_argument("--n_ticks", type=int, default=5) + pars.add_argument("--ticksize", type=float, default=10) + pars.add_argument("--n_subticks", type=int, default=10) + pars.add_argument("--subticksize", type=float, default=5) + pars.add_argument("--style", default='marks_outwards', help="Style of marks") + + # Label settings + pars.add_argument("--labels_enabled", type=inkex.Boolean, default='False') + pars.add_argument("--rounding_level", type=int, default=0) + pars.add_argument("--text_size", type=float, default=1) + pars.add_argument("--text_offset", type=float, default=20) + pars.add_argument("--start_value", type=float, default=0) + pars.add_argument("--stop_value", type=float, default=10) + # Dummy + pars.add_argument("--tab") + + def draw_text(self, textvalue, radius, angular_position, text_size, parent): + # Create text element + text = etree.Element(inkex.addNS('text','svg')) + text.text = textvalue + + # Set text position to center of document. + text.set('x', str(self.x_offset + radius*cos(angular_position))) + text.set('y', str(self.y_offset + radius*sin(angular_position) + text_size/2)) + + # Center text horizontally with CSS style. + style = { + 'text-align' : 'center', + 'text-anchor': 'middle', + 'alignment-baseline' : 'central', + 'font-size' : str(text_size), + 'vertical-align' : 'middle' + } + + text.set('style', str(inkex.Style(style))) + parent.append(text) + def draw_knob_arc(self, radius, parent, angle, transform='' ): + + start_point_angle = (angle - pi)/2.0 + end_point_angle = pi - start_point_angle + + style = { 'stroke' : '#000000', + 'stroke-width' : str(self.options.linewidth), + 'fill' : 'none' } + ell_attribs = {'style': str(inkex.Style(style)), + inkex.addNS('cx','sodipodi') :str(self.x_offset), + inkex.addNS('cy','sodipodi') :str(self.y_offset), + inkex.addNS('rx','sodipodi') :str(radius), + inkex.addNS('ry','sodipodi') :str(radius), + inkex.addNS('start','sodipodi') :str(end_point_angle), + inkex.addNS('end','sodipodi') :str(start_point_angle), + 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 ) + + def draw_centering_circle(self, radius, parent): + + style = { 'stroke' : '#000000', + 'stroke-width' : '1', + 'fill' : 'none' } + ell_attribs = {'style':str(inkex.Style(style)), + inkex.addNS('cx','sodipodi') :str(self.x_offset), + inkex.addNS('cy','sodipodi') :str(self.y_offset), + inkex.addNS('rx','sodipodi') :str(radius), + inkex.addNS('ry','sodipodi') :str(radius), + inkex.addNS('type','sodipodi') :'arc' + } + ell = etree.SubElement(parent, inkex.addNS('path','svg'), ell_attribs ) + + def draw_circle_mark(self, x_offset, y_offset, radius, mark_angle, mark_length, parent): + + cx = x_offset + radius*cos(mark_angle) + cy = y_offset + radius*sin(mark_angle) + r = mark_length / 2.0 + + style = { + 'stroke': '#000000', + 'stroke-width':'0', + 'fill': '#000000' + } + + circ_attribs = { + 'style':str(inkex.Style(style)), + 'cx':str(cx), + 'cy':str(cy), + 'r':str(r) + } + circle = etree.SubElement(parent, inkex.addNS('circle','svg'), circ_attribs ) + + def draw_knob_line_mark(self, x_offset, y_offset, radius, mark_angle, mark_length, parent): + x1 = x_offset + radius*cos(mark_angle) + y1 = y_offset + radius*sin(mark_angle) + x2 = x_offset + (radius + mark_length)*cos(mark_angle) + y2 = y_offset + (radius + mark_length)*sin(mark_angle) + + line_style = { 'stroke': '#000000', + 'stroke-width': str(self.options.linewidth), + 'fill': 'none' + } + + line_attribs = {'style' : str(inkex.Style(line_style)), + inkex.addNS('label','inkscape') : "none", + 'd' : 'M '+str(x1) +',' + + str(y1) +' L '+str(x2) + +','+str(y2) } + + line = etree.SubElement(parent, inkex.addNS('path','svg'), line_attribs ) + + def draw_tick(self, radius, mark_angle, mark_size, parent): + if (self.options.style == 'marks_inwards') or (self.options.style == 'marks_outwards'): + self.draw_knob_line_mark(self.x_offset, self.y_offset, radius, mark_angle, mark_size, parent) + elif self.options.style == 'marks_circles': + self.draw_circle_mark(self.x_offset, self.y_offset, radius, mark_angle, mark_size, parent) + + def effect(self): + parent = self.svg.get_current_layer() + radius = self.svg.unittouu(str(self.options.radius) + self.options.units) + self.x_offset = self.svg.unittouu(str(self.options.x) + self.options.units) + self.y_offset = self.svg.unittouu(str(self.options.y) + self.options.units) + angle = self.options.angle*pi/180.0 + n_ticks = self.options.n_ticks + n_subticks = self.options.n_subticks + is_outer = True + if self.options.style == 'marks_inwards': + is_outer = False + + tick_length = self.svg.unittouu(str(self.options.ticksize) + self.options.units) + subtick_length = self.svg.unittouu(str(self.options.subticksize) + self.options.units) + arc_radius = radius + + + # Labeling settings + start_num = self.options.start_value + end_num = self.options.stop_value + text_spacing = self.svg.unittouu(str(self.options.text_offset) + self.options.units) + text_size = self.svg.unittouu(str(self.options.text_size) + self.options.units) + + if not is_outer: + subtick_radius = radius + tick_length - subtick_length + arc_radius = radius + tick_length + else: + subtick_radius = radius + arc_radius = radius + + if self.options.draw_arc: + self.draw_knob_arc(arc_radius, parent, angle) + + if self.options.draw_centering_circle: + self.draw_centering_circle(arc_radius + tick_length + text_size + text_spacing, parent) + + if self.options.logarithmic_scale: + start_ticks_angle = 1.5*pi - 0.5*angle + for tick in range(n_ticks): + self.draw_tick(radius, start_ticks_angle + angle*log(tick+1)/log(n_ticks), + tick_length, parent) + + if self.options.labels_enabled: + if self.options.rounding_level > 0: + tick_text = str(round(start_num + + float(tick) * (end_num - start_num) / (n_ticks - 1), + self.options.rounding_level)) + else: + tick_text = str(int(start_num + float(tick) * (end_num - start_num) / (n_ticks - 1))) + + self.draw_text(tick_text, radius + tick_length + text_spacing, + start_ticks_angle + angle*log(tick+1)/log(n_ticks), + text_size, + parent) + + if tick == (n_ticks - 1): + break + + for subtick in range(n_subticks): + self.draw_tick(subtick_radius, start_ticks_angle + angle*log(tick+1+(subtick+1)/(n_subticks+1))/log(n_ticks), + subtick_length, parent) + else: + ticks_delta = angle / (n_ticks - 1) + start_ticks_angle = 1.5*pi - 0.5*angle + + for tick in range(n_ticks): + self.draw_tick(radius, start_ticks_angle + ticks_delta*tick, + tick_length, parent) + + if self.options.labels_enabled: + if self.options.rounding_level > 0: + tick_text = str(round(start_num + + float(tick) * (end_num - start_num) / (n_ticks - 1), + self.options.rounding_level)) + else: + tick_text = str(int(start_num + float(tick) * (end_num - start_num) / (n_ticks - 1))) + + self.draw_text(tick_text, radius + tick_length + text_spacing, + start_ticks_angle + ticks_delta*tick, + text_size, + parent) + + if tick == (n_ticks - 1): + break + + subticks_delta = ticks_delta / (n_subticks + 1) + subtick_start_angle = start_ticks_angle + ticks_delta*tick + subticks_delta + for subtick in range(n_subticks): + self.draw_tick(subtick_radius, subtick_start_angle + subticks_delta*subtick, + subtick_length, parent) + +if __name__ == '__main__': + e = KnobScale().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/knob_scale/meta.json b/extensions/fablabchemnitz/knob_scale/meta.json new file mode 100644 index 0000000..6bee0d0 --- /dev/null +++ b/extensions/fablabchemnitz/knob_scale/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Knob Scale", + "id": "fablabchemnitz.de.knob_scale", + "path": "knob_scale", + "dependent_extensions": null, + "original_name": "Knob Scale", + "original_id": "com.knob_scale", + "license": "GNU GPL v3", + "license_url": "https://github.com/leechwort/knob-scale-generator/blob/master/LICENSE", + "comment": "", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/knob_scale", + "fork_url": "https://github.com/leechwort/knob-scale-generator", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Knob+Scale", + "inkscape_gallery_url": null, + "main_authors": [ + "github.com/leechwort", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/lasercut_polygon/lasercut_polygon.inx b/extensions/fablabchemnitz/lasercut_polygon/lasercut_polygon.inx new file mode 100644 index 0000000..87a4551 --- /dev/null +++ b/extensions/fablabchemnitz/lasercut_polygon/lasercut_polygon.inx @@ -0,0 +1,70 @@ + + + Lasercut Polygon + fablabchemnitz.de.lasercut_polygon + + + + + + + + + + + + + + + 1.0 + 3 + + + + + + 0.0 + + + + + + + 1.0 + 3 + + + + + + 0.0 + + + + + + + + 0.1 + 1 + 1923076095 + 4012452351 + 2500 + 85 + 0 + false + =pass%n:%s:%i:%c= + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/lasercut_polygon/lasercut_polygon.py b/extensions/fablabchemnitz/lasercut_polygon/lasercut_polygon.py new file mode 100644 index 0000000..e6cfbc9 --- /dev/null +++ b/extensions/fablabchemnitz/lasercut_polygon/lasercut_polygon.py @@ -0,0 +1,180 @@ +#! /usr/bin/env python +''' +Generates Inkscape SVG file containing up to two polygons or circle taking kerf and clearance into account + +Copyright (C) 2016 Thore Mehr thore.mehr@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 . +''' +__version__ = "0.8" ### please report bugs, suggestions etc to bugs@twot.eu ### + +import inkex +import math +from lxml import etree +from inkex import Color + + +def drawS(s, XYstring, color): # Draw lines from a list + name='part' + style = { 'stroke': color, 'stroke-width': s.svg.unittouu("1px"), 'fill': 'none' } + drw = {'style':str(inkex.Style(style)),inkex.addNS('label','inkscape'):name,'d':XYstring} + etree.SubElement(parent, inkex.addNS('path','svg'), drw ) + return + +def groupdraw(s, XYstrings, colors) : + if len(XYstrings) == 1: + drawS(s, XYstrings[0], colors[0]) + return + grp_name = 'Group' + grp_attribs = {inkex.addNS('label','inkscape'):grp_name} + grp = etree.SubElement(parent, 'g', grp_attribs)#the group to put everything in + name='part' + for i in range(len(XYstrings)): + style = { 'stroke': colors[i], 'fill': 'none' } + drw = {'style':str(inkex.Style(style)),inkex.addNS('label','inkscape'):name+str(i),'d':XYstrings[i]} + etree.SubElement(grp, inkex.addNS('path','svg'), drw ) + return + +def svg_from_points(points,offset): + s='M'+str(points[0][0]+offset[0])+','+str(points[0][1]+offset[1]) + for i in range(1,len(points)): + s+='L'+str(points[i][0]+offset[0])+','+str(points[i][1]+offset[1]) + s+='Z' + return s + +class LasercutPolygon(inkex.EffectExtension): + + def add_arguments(self, pars): + pars.add_argument('--page') + pars.add_argument('--unit', default='mm', help='Measure Units') + pars.add_argument('--o_type', type=int, default=1, help='Outer type') + pars.add_argument('--o_radius',type=float, default=100, help='Outer Radius') + pars.add_argument('--o_edges', type=int, default=1, help='Outer edges') + pars.add_argument('--o_r_type', type=int, default=1, help='Outer radius type') + pars.add_argument('--o_offset',type=float, default=100, help='Outer Radius') + pars.add_argument('--i_type', type=int, default=1, help='Inner type') + pars.add_argument('--i_radius',type=float, default=100, help='Inner Radius') + pars.add_argument('--i_edges', type=int, default=1, help='Inner edges') + pars.add_argument('--i_r_type', type=int, default=1, help='Inner radius type') + pars.add_argument('--i_offset',type=float, default=100, help='Outer Radius') + pars.add_argument('--kerf',type=float, default=0.5, help='Kerf (width) of cut') + pars.add_argument('--spacing',type=float, default=0.5) + pars.add_argument('--color1', type=Color, default='1923076095') + pars.add_argument('--color2', type=Color, default='4012452351') + pars.add_argument('--intensity', type=int, default=1) + pars.add_argument('--speed', type=int, default=1) + pars.add_argument('--pass_offset', type=int, default=1) + pars.add_argument('--displaylasertag', type=inkex.Boolean, default=False) + pars.add_argument('--lasertag', default="=pass%n:%s:%i:%c=") + + def effect(self): + global parent,nomTab,equalTabs,thickness,kerf,correction + + # Get access to main SVG document element and get its dimensions. + svg = self.document.getroot() + + # Get the attibutes: + widthDoc = self.svg.unittouu(svg.get('width')) + heightDoc = self.svg.unittouu(svg.get('height')) + + parent=self.svg.get_current_layer() + + # Get script's option values. + unit=self.options.unit + kerf = self.svg.unittouu( str(self.options.kerf) + unit ) + spacing = self.svg.unittouu( str(self.options.spacing) + unit ) + + o_type=self.options.o_type + o_edges=self.options.o_edges + o_r_type=self.options.o_r_type + o_radius=self.svg.unittouu(str(self.options.o_radius)+unit)+kerf + o_offset=math.radians(-self.options.o_offset)+math.pi + + i_type=self.options.i_type + i_edges=self.options.i_edges + i_r_type=self.options.i_r_type + i_radius=self.svg.unittouu(str(self.options.i_radius)+unit)+kerf + i_offset=math.radians(-self.options.i_offset)+math.pi + + color1=self.options.color1 + color2=self.options.color2 + intensity=self.options.intensity + speed=self.options.speed + pass_offset=self.options.pass_offset + lasertag=self.options.lasertag + + if (o_r_type==2 and o_type==2): + #a/sin(alpa)=b/sin(beta)=c/sin(gamma) + #b=o_radius, alpha=(2*math.pi/o_edges), gamma=pi/2 , alpha+beta+gamma=pi->beta=pi-alpha+gamma + #search for c + #c=b*(sin(gamma)/sin(betta)) + beta=math.pi/2-(math.pi/o_edges) + o_radius/=math.sin(beta) + if (o_r_type==3 and o_type==2): + beta=math.pi/2-(math.pi/o_edges) + o_radius*=math.sin(beta)/math.sin(math.pi/o_edges) + if (i_r_type==2 and i_type==3): + #a/sin(alpa)=b/sin(beta)=c/sin(gamma) + #b=o_radius, alpha=(2*math.pi/o_edges), gamma=pi/2 , alpha+beta+gamma=pi->beta=pi-alpha+gamma + #search for c + #c=b*(sin(gamma)/sin(betta)) + beta=math.pi/2-(math.pi/i_edges) + i_radius/=math.sin(beta) + if (i_r_type==3 and i_type==3): + beta=math.pi/2-(math.pi/i_edges) + i_radius*=math.sin(beta)/math.sin(math.pi/o_edges) + + + #text = etree.Element(inkex.addNS('text','svg')) + #text.text = "Outside:"+str(self.uutounit(cabinet_width,unit))+"x"+str(self.uutounit(cabinet_depth,unit))+"x"+str(self.uutounit(cabinet_height,unit))+" " + #layer.append(text) + + + if(o_type==1): + s=['M '+str(spacing)+','+str(o_radius+spacing)+'a'+str(o_radius)+','+str(o_radius)+' 0 1,0 '+str(2*o_radius)+',0'+'a'+str(o_radius)+','+str(o_radius)+' 0 1,0 '+str(-2*o_radius)+',0'] + if(o_type==2): + stepsize=2*math.pi/o_edges + points=[] + + for i in range(o_edges): + points+=[(math.sin(o_offset+stepsize*i)*(o_radius+kerf),math.cos(o_offset+stepsize*i)*(o_radius+kerf))] + s=[svg_from_points(points,(o_radius+spacing,o_radius+spacing))] + if(i_type==2): + s+=['M '+str(spacing+o_radius-i_radius)+','+str(o_radius+spacing)+'a'+str(i_radius)+','+str(i_radius)+' 0 1,0 '+str(2*i_radius)+',0'+'a'+str(i_radius)+','+str(i_radius)+' 0 1,0 '+str(-2*i_radius)+',0'] + if(i_type==3): + stepsize=2*math.pi/i_edges + points=[] + + for i in range(i_edges): + points+=[(math.sin(i_offset+stepsize*i)*(i_radius+kerf),math.cos(i_offset+stepsize*i)*(i_radius+kerf))] + s+=[svg_from_points(points,(o_radius+spacing,o_radius+spacing))] + groupdraw(self, s,[color1,color2]) + + if self.options.displaylasertag: + # Create a new layer. + layer = etree.SubElement(svg, 'g') + layer.set(inkex.addNS('label', 'inkscape'), 'lasertag') + layer.set(inkex.addNS('groupmode', 'inkscape'), 'layer') + + tag_1=lasertag + tag_1=tag_1.replace("%n",str(pass_offset+1)).replace("%s",str(speed)).replace("%i",str(intensity)).replace("%c",str(color2)) + tag_2=lasertag + tag_2=tag_2.replace("%n",str(pass_offset+2)).replace("%s",str(speed)).replace("%i",str(intensity)).replace("%c",str(color1)) + text = etree.Element(inkex.addNS('text','svg')) + text.text = tag_1 + if (len(s)>1): + text.text+=" "+tag_2 + layer.append(text) + +if __name__ == '__main__': + LasercutPolygon().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/lasercut_polygon/meta.json b/extensions/fablabchemnitz/lasercut_polygon/meta.json new file mode 100644 index 0000000..4f3407e --- /dev/null +++ b/extensions/fablabchemnitz/lasercut_polygon/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Lasercut Polygon", + "id": "fablabchemnitz.de.lasercut_polygon", + "path": "lasercut_polygon", + "dependent_extensions": null, + "original_name": "Polygon", + "original_id": "eu.flk.laser.polygon", + "license": "GNU GPL v3", + "license_url": "https://github.com/ThoreMehr/inkscape-polygon/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/lasercut_polygon", + "fork_url": "https://github.com/ThoreMehr/inkscape-polygon", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Lasercut+Polygon", + "inkscape_gallery_url": null, + "main_authors": [ + "github.com/ThoreMehr", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/leather_case/leather_case.inx b/extensions/fablabchemnitz/leather_case/leather_case.inx new file mode 100644 index 0000000..4736d1e --- /dev/null +++ b/extensions/fablabchemnitz/leather_case/leather_case.inx @@ -0,0 +1,31 @@ + + + Leather Case + fablabchemnitz.de.leather_case + 80.0 + 165.0 + 10.0 + 10.0 + 10.0 + 2 + + + + + 10.0 + 40.0 + 10.0 + true + false + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/leather_case/leather_case.py b/extensions/fablabchemnitz/leather_case/leather_case.py new file mode 100644 index 0000000..a713fcc --- /dev/null +++ b/extensions/fablabchemnitz/leather_case/leather_case.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python3 +""" +A Inkscape extension to generate the pieces for a leather case that can be laser cut. + +The leather case is intended to be used with up to 5 mobile phones. +""" + +import inkex +import math +from lxml import etree + +class LeatherCase(inkex.EffectExtension): + height = -1.0 + + def add_arguments(self, pars): + pars.add_argument('-w', '--width', type=float, default=80, help='Width (mm)') + pars.add_argument('-x', '--height', type=float, default=165, help='Height (mm)') + pars.add_argument('-d', '--depth', type=float, default=10, help='Depth (mm)') + pars.add_argument('-H', '--heightMargin', type=float, default=10, help='Height Margin (mm)') + pars.add_argument('-r', '--cornerRoundness', type=float, default=10, help='Corner Roundness (mm)') + pars.add_argument('-i', '--divisions', type=int, default=2, help='Divisions') + pars.add_argument('-a', '--claspAmount', default=1, help='Number of Clasps') + pars.add_argument('-p', '--extraTongueLength', type=float, default=10, help='Extra Tongue Length (mm)') + pars.add_argument('-t', '--tipTongueLength', type=float,default=40, help='Tip Tongue Length (mm)') + pars.add_argument('-e', '--extraEdgeWidth', type=float, default=10, help='Extra Edge Width (mm)') + pars.add_argument('-o', '--makeHoles', default="true", help='Make Holes') + pars.add_argument('-g', '--groupObjects', default="false", help='Group Objects') + + def effect(self): + width = self.options.width + height = self.options.height + depth = self.options.depth + heightMargin = self.options.heightMargin + cornerRoundness = self.options.cornerRoundness + divisions = self.options.divisions + oneClasp = self.options.claspAmount == "1" + extraTongueLength = self.options.extraTongueLength + tipTongueLength = self.options.tipTongueLength + extraEdgeWidth = self.options.extraEdgeWidth + makeHoles = self.options.makeHoles == "true" + group = self.options.groupObjects == "true" + + parent = self.svg.get_current_layer() + + if group: + parent = etree.SubElement(parent, 'g') + + line_style = { 'stroke-width': self.svg.unittouu(str(0.1) + "mm"), 'stroke':'#FF0000', 'fill':'none'} + + verticalLine1Size = width - cornerRoundness - 1 + + hole = '' + + if makeHoles: + if oneClasp: + hole = ' m ' + str((height + heightMargin * 2) / 2) + ',' + str(extraTongueLength + 13) + ' c 1,0 2,1 2,2 0,1 -1,2 -2,2 -1,0 -2,-1 -2,-2 0,-1 1,-2 2,-2' + else: + hole = (' m ' + str((height + heightMargin * 2) / 2 - 55) + ',' + str(extraTongueLength + 13) + ' c 1,0 2,1 2,2 0,1 -1,2 -2,2 -1,0 -2,-1 -2,-2 0,-1 1,-2 2,-2' + + ' m 110,0' + ' c 1,0 2,1 2,2 0,1 -1,2 -2,2 -1,0 -2,-1 -2,-2 0,-1 1,-2 2,-2' + ) + + firstPiece_attribs = {'style' : str(inkex.Style(line_style)), + 'd' : 'M 0,0 l 0,' + str(verticalLine1Size) + + ' c 0,' + str(cornerRoundness / 2.0) + ' ' + str(cornerRoundness / 2) + ',' + str(cornerRoundness) + ' ' + str(cornerRoundness) + ',' + str(cornerRoundness) + + ' l ' + str(height + heightMargin * 2 - cornerRoundness * 2) + ',0' + + ' c ' + str(cornerRoundness / 2) + ',0 ' + str(cornerRoundness) + ',' + str(-cornerRoundness / 2) + ' ' + str(cornerRoundness) + ',' + str(-cornerRoundness) + + ' l 0,' + str(-verticalLine1Size) + ' Z' + + hole + } + + + firstPiece = etree.SubElement(parent, inkex.addNS('path','svg'), firstPiece_attribs ) + + + # Intermediate pieces + for x in range(1, divisions): + intermediatePiece_attribs = {'style' : str(inkex.Style(line_style)), + 'd' : 'M ' + str(10 + x*5) + ',' + str(10 + x*5) + ' l 0,' + str(verticalLine1Size) + + ' c 0,' + str(cornerRoundness / 2.0) + ' ' + str(cornerRoundness / 2) + ',' + str(cornerRoundness) + ' ' + str(cornerRoundness) + ',' + str(cornerRoundness) + + ' l ' + str(height + heightMargin * 2 - cornerRoundness * 2) + ',0' + + ' c ' + str(cornerRoundness / 2) + ',0 ' + str(cornerRoundness) + ',' + str(-cornerRoundness / 2) + ' ' + str(cornerRoundness) + ',' + str(-cornerRoundness) + + ' l 0,' + str(-verticalLine1Size) + ' Z' + } + + + intermediatePiece = etree.SubElement(parent, inkex.addNS('path','svg'), intermediatePiece_attribs ) + + + line_style2 = { 'stroke-width': self.svg.unittouu(str(0.1) + "mm"), 'stroke':'#00FF00', 'fill':'none'} + plainTongueLength = depth * divisions + extraTongueLength - 1 + (divisions - 1) + totalWidth = height + heightMargin * 2; + hole = '' + + if makeHoles: + if oneClasp: + hole = ' m 30,' + str(-50 -(plainTongueLength + tipTongueLength - 10)) + ' c 1,0 2,1 2,2 0,1 -1,2 -2,2 -1,0 -2,-1 -2,-2 0,-1 1,-2 2,-2' + else: + hole = (' m -25,' + str(-50 -(plainTongueLength + tipTongueLength - 10)) + ' c 1,0 2,1 2,2 0,1 -1,2 -2,2 -1,0 -2,-1 -2,-2 0,-1 1,-2 2,-2' + + ' m 110,0' + ' c 1,0 2,1 2,2 0,1 -1,2 -2,2 -1,0 -2,-1 -2,-2 0,-1 1,-2 2,-2') + + + if oneClasp: + tongue = (' 0,' + str(-plainTongueLength) + + ' c 0,' + str(-tipTongueLength / 2) + ' ' + str(-totalWidth / 4) + ',' + str(-tipTongueLength) + ' ' + str(-totalWidth / 2) + ',' + str(-tipTongueLength) + + ' ' + str(-totalWidth / 4) + ',0 ' + str(-totalWidth / 2) + ',' + str(tipTongueLength / 2) + ' ' + str(-totalWidth / 2) + ',' + str(tipTongueLength) + + ' l 0,' + str(plainTongueLength) + ) + else: + tongue = (' 0,' + str(-(plainTongueLength + tipTongueLength - cornerRoundness)) + + ' c 0,' + str(-cornerRoundness / 2) + ' ' + str(-cornerRoundness / 2) + ',' + str(-cornerRoundness) + ' ' + str(-cornerRoundness) + ',' + str(-cornerRoundness) + + ' l ' + str(-(height + heightMargin * 2 - cornerRoundness * 2)) + ',0' + + ' c ' + str(-cornerRoundness / 2) + ',0 ' + str(-cornerRoundness) + ',' + str(cornerRoundness / 2) + ' ' + str(-cornerRoundness) + ',' + str(cornerRoundness) + + ' l 0,' + str(plainTongueLength + tipTongueLength - cornerRoundness) + ) + + secondPiece_attribs = {'style' : str(inkex.Style(line_style2)), + 'd' : 'M -5,-4 l 0,' + str(verticalLine1Size - 1) + + ' c 0,' + str(cornerRoundness / 2.0) + ' ' + str(cornerRoundness / 2) + ',' + str(cornerRoundness) + ' ' + str(cornerRoundness) + ',' + str(cornerRoundness) + + ' l ' + str(height + heightMargin * 2 - cornerRoundness * 2) + ',0' + + ' c ' + str(cornerRoundness / 2) + ',0 ' + str(cornerRoundness) + ',' + str(-cornerRoundness / 2) + ' ' + str(cornerRoundness) + ',' + str(-cornerRoundness) + + ' l 0,' + str(-(verticalLine1Size-1)) + + ' -1,-1' + ' 1,-1' + + tongue + + ' 1,1 -1,1 m ' + + str(totalWidth / 2 - 30) +',-1 c 0.25,0 0.5,0.25 0.5,0.5 0,0.25 -0.25,0.5 -0.5,0.5 -0.25,0 -0.5,-0.25 -0.5,-0.5 0,-0.25 0.25,-0.5 0.5,-0.5 ' + + 'm 60,0 c 0.25,0 0.5,0.25 0.5,0.5 0,0.25 -0.25,0.5 -0.5,0.5 -0.25,0 -0.5,-0.25 -0.5,-0.5 0,-0.25 0.25,-0.5 0.5,-0.5' + + 'm 0,50 c 0.25,0 0.5,0.25 0.5,0.5 0,0.25 -0.25,0.5 -0.5,0.5 -0.25,0 -0.5,-0.25 -0.5,-0.5 0,-0.25 0.25,-0.5 0.5,-0.5' + 'm -60,0 c 0.25,0 0.5,0.25 0.5,0.5 0,0.25 -0.25,0.5 -0.5,0.5 -0.25,0 -0.5,-0.25 -0.5,-0.5 0,-0.25 0.25,-0.5 0.5,-0.5' + + hole + } + + secondPiece = etree.SubElement(parent, inkex.addNS('path','svg'), secondPiece_attribs ) + + line_style3 = { 'stroke-width': self.svg.unittouu(str(0.1) + "mm"), 'stroke':'#0000FF', 'fill':'none'} + edgeLength = (width - cornerRoundness) * 2 + height + heightMargin * 2 - cornerRoundness * 2 + 3.14159 * cornerRoundness + edgeWidth = depth * divisions + divisions - 1 + extraEdgeWidth + + thirdPiece_attribs = {'style' : str(inkex.Style(line_style3)), + 'd' : 'M 5,5 l 0,' + str(edgeWidth) + + ' ' + str(edgeLength) + ',0' + + ' 0,' + str(-edgeWidth) + ' Z' + } + + thirdPiece = etree.SubElement(parent, inkex.addNS('path','svg'), thirdPiece_attribs ) + + line_style4 = { 'stroke-width': self.svg.unittouu(str(0.1) + "mm"), 'stroke':'#FF00FF', 'fill':'none'} + edgeLength = 70 + edgeWidth = 60 + + fourthPiece_attribs = {'style' : str(inkex.Style(line_style4)), + 'd' : 'M 10,10 l 0,' + str(edgeWidth) + + ' ' + str(edgeLength) + ',0' + + ' 0,' + str(-edgeWidth) + ' Z' + } + + fourthPiece = etree.SubElement(parent, inkex.addNS('path','svg'), fourthPiece_attribs ) + +if __name__ == '__main__': + LeatherCase().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/leather_case/meta.json b/extensions/fablabchemnitz/leather_case/meta.json new file mode 100644 index 0000000..d39a8b7 --- /dev/null +++ b/extensions/fablabchemnitz/leather_case/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Leather Case", + "id": "fablabchemnitz.de.leather_case", + "path": "leather_case", + "dependent_extensions": null, + "original_name": "Leather Case 1", + "original_id": "com.zerjio.inkscape.lcmp1", + "license": "GNU GPL v3", + "license_url": "https://github.com/zerjillo/laserCutInkscapeExtensions/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/leather_case", + "fork_url": "https://github.com/zerjillo/laserCutInkscapeExtensions", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Leather+Case", + "inkscape_gallery_url": null, + "main_authors": [ + "github.com/zerjillo", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/maze/__pycache__/maze_lib.cpython-310.pyc b/extensions/fablabchemnitz/maze/__pycache__/maze_lib.cpython-310.pyc new file mode 100644 index 0000000..1a4e6c6 Binary files /dev/null and b/extensions/fablabchemnitz/maze/__pycache__/maze_lib.cpython-310.pyc differ diff --git a/extensions/fablabchemnitz/maze/maze.inx b/extensions/fablabchemnitz/maze/maze.inx new file mode 100644 index 0000000..188fe78 --- /dev/null +++ b/extensions/fablabchemnitz/maze/maze.inx @@ -0,0 +1,34 @@ + + + Maze + fablabchemnitz.de.maze + 20 + 20 + 10.0 + + + + + + + + 1.0 + + + + + + + + + path + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/maze/maze.py b/extensions/fablabchemnitz/maze/maze.py new file mode 100644 index 0000000..833010c --- /dev/null +++ b/extensions/fablabchemnitz/maze/maze.py @@ -0,0 +1,65 @@ +#! /usr/bin/env python3 + +# this module is under licence MIT @ Tiemen DUVILLARD 2020 +# for all questions, comments, bugs: duvillard.tiemen@gmail.com + +import inkex +from lxml import etree +from maze_lib import * + +def points_to_svgd(p, close=False): + """ convert list of points (x,y) pairs + into a SVG path list + """ + f = p[0] + p = p[1:] + svgd = 'M%.4f,%.4f' % f + for x in p: + svgd += 'L%.4f,%.4f' % x + if close: + svgd += 'z' + return svgd + + +class Maze(inkex.EffectExtension): + + def add_arguments(self, pars): + pars.add_argument("--verti", type=int, default=20, help="Height (cells)") + pars.add_argument("--horiz", type=int, default=20, help="Length (cells)") + pars.add_argument("--size", type=float, default=10.0, help="Cell size") + pars.add_argument("--cell_units", default="mm", help="Units") + pars.add_argument("--algo", default=1, help="Algorithm") + pars.add_argument("--width", type=float, default=10.0, help="Line width") + + def effect(self): + # basic style + style = { 'stroke': "black", "fill":"none", 'stroke-width': self.options.width } + # my group of paths + topgroup = etree.SubElement(self.svg.get_current_layer(), 'g' ) + + lc = self.svg.unittouu (str(self.options.size) + self.options.cell_units) + X = self.options.verti + Y = self.options.horiz + + L = MazeLib(X,Y,self.options.algo) + + for i,j,door in L.verticalDoors(): + if door: + path = points_to_svgd([(lc*(j+1), lc*(i)), (lc*(j+1), lc*(i+1))]) + mypath_attribs = { 'style': str(inkex.Style(style)), 'd': path } + squiggle = etree.SubElement(topgroup, inkex.addNS('path','svg'), mypath_attribs ) + + for i,j,door in L.horizontalDoors(): + if door: + path = points_to_svgd([(lc*(j), lc*(i+1)), (lc*(j+1), lc*(i+1))]) + mypath_attribs = { 'style': str(inkex.Style(style)), 'd': path } + squiggle = etree.SubElement(topgroup, inkex.addNS('path','svg'), mypath_attribs ) + + + path = points_to_svgd([(0,0),(0,lc*Y),(lc*X,lc*Y),(lc*X,0)], True) + mypath_attribs = { 'style': str(inkex.Style(style)), 'd': path } + squiggle = etree.SubElement(topgroup, inkex.addNS('path','svg'), mypath_attribs ) + + +if __name__ == '__main__': + Maze().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/maze/maze_lib.py b/extensions/fablabchemnitz/maze/maze_lib.py new file mode 100644 index 0000000..45fd928 --- /dev/null +++ b/extensions/fablabchemnitz/maze/maze_lib.py @@ -0,0 +1,641 @@ +#! /usr/bin/env python3 + +# this module is under licence MIT @ Tiemen DUVILLARD 2020 +# for all questions, comments, bugs: duvillard.tiemen@gmail.com + +from random import choice, randrange + +# Representation of maze : +# A labyrinth is a set of 2 door panels. +# For a maze 5*5 : +# Theory : ## Example : +# L = [[[a, b, c, d], # vertical doors ## L = [[[1, 1, 1, 1], # vertical doors +# [e, f, g, h], ## [1, 0, 1, 0], +# [i, j, k, l], ## [0, 1, 0, 1], +# [m, n, o, p], ## [0, 0, 0, 0], +# [q, r, s, t]], ## [0, 1, 0, 0]], +# [[A, B, C, D, E], # horizontal doors ## [[0, 0, 0, 0, 0], # horizontal doors +# [F, G, H, I, J], ## [0, 0, 0, 0, 1], +# [K, L, M, N, O], ## [0, 1, 1, 1, 0], +# [P, Q, R, S, T]]] ## [0, 1, 0, 1, 1]]] +# ## +# ==>> ## ==>> +# X 0 1 2 3 4 ## X 0 1 2 3 4 +# Y ┌───┬───┬───┬───┬───┐ ## Y ┌───┬───┬───┬───┬───┐ +# 0 │ ∙ a ∙ b ∙ c ∙ d ∙ │ ## 0 │ ∙ │ ∙ │ ∙ │ ∙ │ ∙ │ +# ├─A─┼─B─┼─C─┼─D─┼─E─┤ ## │ │ │ │ │ │ +# 1 │ ∙ e ∙ f ∙ g ∙ h ∙ │ ## 1 │ ∙ │ ∙ ∙ │ ∙ ∙ │ +# ├─F─┼─G─┼─H─┼─I─┼─J─┤ ## │ │ │ │ ┌───┤ +# 2 │ ∙ i ∙ j ∙ k ∙ l ∙ │ ## 2 │ ∙ ∙ │ ∙ ∙ │ ∙ │ +# ├─K─┼─L─┼─M─┼─N─┼─O─┤ ## │ ╶───┴───────┘ │ +# 3 │ ∙ m ∙ n ∙ o ∙ p ∙ │ ## 3 │ ∙ ∙ ∙ ∙ ∙ │ +# ├─P─┼─Q─┼─R─┼─S─┼─T─┤ ## │ ╶───┐ ╶───────│ +# 4 │ ∙ q ∙ r ∙ s ∙ t ∙ │ ## 4 │ ∙ ∙ │ ∙ ∙ ∙ │ +# └───┴───┴───┴───┴───┘ ## └───────┴───────────┘ + +def kruskal(x, y): + global ID + ID = 0 + + class case: + """Little class for union-find""" + def __init__(self,x,y): + self.x = x + self.y = y + self.is_repr = True + self.repr = None + global ID + self.ID = ID + ID += 1 + + def represent(self): + if self.is_repr: + return self + else: + L = [self.repr] + while not L[0].is_repr: + L[0] = L[0].repr + return L[0] + + def __eq__(self,other): + return self.represent().ID == other.represent().ID + + def union(self,other): + a = self.represent() + b = other.represent() + if not (a == b): + b.is_repr = False + b.repr = a + + # set of doors + doors = [] + # verticals doors result + verti = [] + # horizontals doors result + horiz = [] + # set of cases + cases = [] + ## initialize vertical doors + for j in range(y): + l = [] + for i in range(x-1): + l.append(1) + doors.append([i,j,'v']) + verti.append(l) + ## initialize horizontal doors + for j in range(y-1): + l = [] + for i in range(x): + l.append(1) + doors.append([i,j,'h']) + horiz.append(l) + + ## initialize cases + for i in range(y): + l = [] + for j in range(x): + l.append(case(x,y)) + cases.append(l) + + cpt = x*y -1 # nb of openings in perfect maze + while cpt > 0: + # I choose a door + idoor = randrange(len(doors)) + dx, dy, dd = doors[idoor] + cx1, cy1 = dx, dy + if dd == 'h' : cx2, cy2 = cx1, cy1+1 + else : cx2, cy2 = cx1+1, cy1 + + C1 = cases[cy1][cx1] + C2 = cases[cy2][cx2] + # if the 2 cases separate by my door are not in same set + if not C1 == C2: + C1.union(C2) + cpt -= 1 + # i modify my result + if dd == "v": verti[dy][dx] = 0 + else: horiz[dy][dx] = 0 + # i delete the door + del doors[idoor] + + return verti,horiz + +def recursive_backtrack(x, y): + # Initialisation of my variables + labyrinthe = [] + for i in range(y): labyrinthe.append([0] * x) + horiz = [] + for i in range(y-1): horiz.append([1] * x) + verti = [] + for i in range(y): verti.append([1] * (x-1)) + + # I choose a random start + X_pos = randrange(x) + Y_pos = randrange(y) + labyrinthe[Y_pos][X_pos] = 1 + historique = [[X_pos,Y_pos]] + + # I explore a tree with deep parcours + while len(historique) != 0: + X = historique[-1][0] + Y = historique[-1][1] + + possibilite = [] + if (Y-1 >= 0) and (labyrinthe[Y-1][X] == 0): possibilite.append(0) + if (X+1 < x) and (labyrinthe[Y][X+1] == 0): possibilite.append(1) + if (Y+1 < y) and (labyrinthe[Y+1][X] == 0): possibilite.append(2) + if (X-1 >= 0) and (labyrinthe[Y][X-1] == 0): possibilite.append(3) + + if len(possibilite) == 0: + del historique[-1] + else: + d = choice(possibilite) + if d == 0: + X1,Y1 = X, Y-1 + horiz[Y-1][X] = 0 + if d == 1: + X1,Y1 = X+1, Y + verti[Y][X] = 0 + if d == 2: + X1,Y1 = X, Y+1 + horiz[Y][X] = 0 + if d == 3: + X1,Y1 = X-1, Y + verti[Y][X-1] = 0 + labyrinthe[Y1][X1] = 1 + historique.append([X1,Y1]) + + return verti, horiz + + +def recursive_chamber(x, y): + # Initialisation of my variables + def recursive_chamber_recu(VERTI,HORIZ,xA,yA,xB,yB): + if (xB - xA <= 1): + return + elif (yB - yA <= 1): + return + else : + dx = xB-xA + dy = yB-yA + v = randrange(0,dx+dy) + if v < dx : + x = randrange(xA,xB-1) + y = randrange(yA,yB) + for i in range(yA,yB): + if i != y: + VERTI[i][x] = 1 + recursive_chamber_recu(VERTI,HORIZ,xA,yA,x+1,yB) + recursive_chamber_recu(VERTI,HORIZ,x+1,yA,xB,yB) + else: + x = randrange(xA,xB) + y = randrange(yA,yB-1) + for i in range(xA,xB): + if i != x: + HORIZ[y][i] = 1 + recursive_chamber_recu(VERTI,HORIZ,xA,yA,xB,y+1) + recursive_chamber_recu(VERTI,HORIZ,xA,y+1,xB,yB) + + + horiz = [] + for i in range(y-1): horiz.append([0] * x) + verti = [] + for i in range(y): verti.append([0] * (x-1)) + + # appel recursif + recursive_chamber_recu(verti,horiz,0,0,x,y) + + return verti, horiz + +# a empty maze +def empty(x, y): + verti = [] + for i in range(y): + verti.append([0] * (x-1)) + + horiz = [] + for i in range(y-1): + horiz.append([0] * x) + return verti, horiz + +# a full maze +def full(x, y): + verti = [] + for i in range(y): + verti.append([1] * (x-1)) + + horiz = [] + for i in range(y-1): + horiz.append([1] * x) + return verti, horiz + + + +class MazeLib: + """This Class define a Maze object""" + def __init__(self, X=5, Y=5, algorithm="empty"): + """Use : m = Maze(X:int,Y:int, algorithm:string)""" + self.X = X # width + self.Y = Y # height + self.verti = [] # table of vertical doors + self.horiz = [] # table of horizontal doors + algorithm = algorithm.lower() + + if algorithm in ("kruskal","k") : + self.verti, self.horiz = kruskal(self.X,self.Y) + self.doors = self.nbDoors() + self.algorithm = "kruskal" + self.perfect = True + + elif algorithm in ("recursive_backtrack","rb") : + self.verti, self.horiz = recursive_backtrack(self.X,self.Y) + self.doors = self.nbDoors() + self.algorithm = "recursive_backtrack" + self.perfect = True + + elif algorithm in ("empty","e") : + self.verti, self.horiz = empty(self.X,self.Y) + self.doors = self.nbDoors() + self.algorithm = "empty" + self.perfect = False + + elif algorithm in ("full","f") : + self.verti, self.horiz = full(self.X,self.Y) + self.doors = self.nbDoors() + self.algorithm = "full" + self.perfect = False + + elif algorithm in ("recursive_chamber","rc") : + self.verti, self.horiz = recursive_chamber(self.X,self.Y) + self.doors = self.nbDoors() + self.algorithm = "recursive_chamber" + self.perfect = False + + else : + raise("Algorithm not recognized. Algorithm must be in ['kruskal','recursive_backtrack','empty','full']") + + def __str__(self): + """Return information about maze""" + s = "Size of maze : {}*{}\nGenerate by : {}\nNb of doors : {}\nPerfect : {}" + s = s.format(self.X,self.Y,self.algorithm,self.doors,self.perfect) + return s + + def nbDoors(self): + """Return number of doors in my maze. If maze is perfect, it equals to (self.X-1)*(self.Y-1)""" + S = 0 + for k in self.verti: S += sum(k) + for k in self.horiz: S += sum(k) + return S + + def verticalDoors(self): + """Iterate on vertical doors""" + for i in range(len(self.verti)): + for j in range(len(self.verti[i])): + yield i,j, bool(self.verti[i][j]) + + def horizontalDoors(self): + """Iterate on horizontal doors""" + for i in range(len(self.horiz)): + for j in range(len(self.horiz[i])): + yield i,j, bool(self.horiz[i][j]) + + def canMove(self, x, y, direction) : + """return if i can move in a direction since (x,y) """ + if direction in ["down","d",0] : # down + if y == self.Y-1 :return False + return not self.horiz[y][x] + if direction in ["up","u",2] : # up + if y == 0 : return False + return not self.horiz[y-1][x] + if direction in ["left","l",3] : # left + if x == 0 : return False + return not self.verti[y][x-1] + if direction in ["right","r",1] : # right + if x == self.X-1 : return False + return not self.verti[y][x] + return False + + def move(self, x, y, direction) : + """return new cord after one move in direction""" + if direction in ["down","d",0] : # down + return x,y+1 + if direction in ["up","u",2] : # up + return x,y-1 + if direction in ["left","l",3] : # left + return x-1,y + if direction in ["right","r",1] : # right + return x+1,y + return x,y + + def liberty(self, x, y): + """return all cases accessible from (x,y)""" + r = [] + for d in ["down","up","left","right"]: + if self.canMove(x,y,d): + r.append(d) + return d + + def toSquare(self): + """return another representation of maze, a List of List of case in {0,1} """ + RET = [] + for i in range(self.Y*2+1): + l = [] + for j in range(self.X*2+1): + if i == 0 or i == self.Y*2 or j == 0 or j == self.X*2 : + l.append(1) + else : + if (i%2==0 and j%2==0) : + l.append(1) + elif (i%2==1 and j%2==1): + l.append(0) + elif (i%2==0 and j%2==1): + l.append(self.horiz[(i-1)//2][(j-1)//2]) + elif (i%2==1 and j%2==0): + l.append(self.verti[(i-1)//2][(j-1)//2]) + RET.append(l) + return RET + + def solve(self, xa, ya, xb, yb): + """return a list of direction for link (xa,ya) -> (xb,yb). Null""" + Sol = [] + Pile = [(xa,ya)] + laby = [] + for i in range(self.Y): + laby.append(['new'] * self.X) + + while len(Pile) != 0 and Pile[-1] != (xb,yb): + pos = Pile[-1] + x,y = pos[0],pos[1] + if laby[y][x] == 'new': + possibilite = [] + for d in ['down',"left","up","right"]: + if self.canMove(pos[0],pos[1],d) : + nx,ny = self.move(pos[0],pos[1],d) + if laby[ny][nx] == 'new' : + possibilite.append(d) + + laby[y][x] = possibilite + elif laby[y][x] == "old": + del Pile[-1] + if len(Sol) != 0: + del Sol[-1] + elif type(laby[y][x]) == list: + if len(laby[y][x]) == 0: + laby[y][x] = "old" + else: + ichoix = -1#randrange(len(laby[y][x])) + choix = laby[y][x][ichoix] + Pile.append(self.move(x,y,choix)) + Sol.append(choix) + del laby[y][x][ichoix] + + return Sol + + def furthestBox(self, xa, ya): + """return the case furtest of the case (xa,ya)""" + Sol = [] + dmax = len(Sol) + pmax = Sol.copy() + cmax = (xa,ya) + + Pile = [(xa,ya)] + laby = [] + for i in range(self.Y): + laby.append(['new'] * self.X) + + while len(Pile) != 0: + pos = Pile[-1] + x,y = pos[0],pos[1] + if laby[y][x] == 'new': + possibilite = [] + for d in ['down',"left","up","right"]: + if self.canMove(pos[0],pos[1],d) : + nx,ny = self.move(pos[0],pos[1],d) + if laby[ny][nx] == 'new' : + possibilite.append(d) + + laby[y][x] = possibilite + elif laby[y][x] == "old": + if len(Sol) > dmax: + dmax = len(Sol) + pmax = Sol.copy() + cmax = (x,y) + del Pile[-1] + if len(Sol) != 0: + del Sol[-1] + elif type(laby[y][x]) == list: + if len(laby[y][x]) == 0: + laby[y][x] = "old" + else: + ichoix = -1#randrange(len(laby[y][x])) + choix = laby[y][x][ichoix] + Pile.append(self.move(x,y,choix)) + Sol.append(choix) + del laby[y][x][ichoix] + + return pmax, cmax + + def longestWay(self): + Xa = randrange(self.X) + Ya = randrange(self.Y) + p,B = self.furthestBox(Xa,Ya) + path,C = self.furthestBox(B[0],B[1]) + return B,path,C + + + + + def save(self): + """Return a string contain's all information. it can be save in file, print, etc..""" + s = "{};{};{};{};{};{};{}".format(self.X, self.Y, self.doors, self.algorithm, self.perfect, self.verti, self.horiz) + return s + + def load(self, st): + """Load a Maze since a string of format save""" + cont = st.split(";") + self.X = int(cont[0]) + self.Y = int(cont[1]) + self.doors = int(cont[2]) + self.algorithm = cont[3] + self.perfect = bool(cont[4]) + self.verti = cont[5].split("], [") + for i in range(len(self.verti)): + self.verti[i] = self.verti[i].split(',') + for j in range(len(self.verti[i])): + self.verti[i][j] = self.verti[i][j].replace("[","") + self.verti[i][j] = self.verti[i][j].replace("]","") + self.verti[i][j] = self.verti[i][j].replace(" ","") + self.verti[i][j] = int(self.verti[i][j]) + + self.horiz = cont[6].split("], [") + for i in range(len(self.horiz)): + self.horiz[i] = self.horiz[i].split(',') + for j in range(len(self.horiz[i])): + self.horiz[i][j] = self.horiz[i][j].replace("[","") + self.horiz[i][j] = self.horiz[i][j].replace("]","") + self.horiz[i][j] = self.horiz[i][j].replace(" ","") + self.horiz[i][j] = int(self.horiz[i][j]) + + def toTxt(self, centre=False, coord=False, basic= False): + """return a txt representation of maze in ASCII art""" + if type(centre) == bool: + if centre : centre = "*" + else : centre = " " + else: + centre = centre[0] + + if basic : + if coord: + R = " X" + for i in range(self.X): + R += " {} ".format(i%10) + R += "\n" + R += "Y +" + r = "0 |" + else: + R = "+" + r = "|" + for j in range(self.X-1): + if self.verti[0][j]: + R += "---+" + r += " {} |".format(centre) + else: + R += "----" + r += " {} ".format(centre) + R += "---+\n" + r += " {} |\n".format(centre) + R += r + for i in range(self.Y-1): + if coord: + if self.horiz[i][0]: + l1 = " +" + else: + l1 = " |" + l2 = "{} |".format((i+1)%10) + else: + if self.horiz[i][0]: + l1 = "+" + else: + l1 = "|" + l2 = "|" + for j in range(self.X-1): + inter = "+" + if self.horiz[i][j] and self.horiz[i][j+1] and not self.verti[i][j] and not self.verti[i+1][j] : inter = "-" + if not self.horiz[i][j] and not self.horiz[i][j+1] and self.verti[i][j] and self.verti[i+1][j] : inter = "|" + if not self.horiz[i][j] and not self.horiz[i][j+1] and not self.verti[i][j] and not self.verti[i+1][j] : inter = " " + + if self.horiz[i][j]: + l1 += "---"+inter + else: + l1 += " "+inter + + if self.verti[i+1][j]: + l2 += " {} |".format(centre) + else: + l2 += " {} ".format(centre) + if self.horiz[i][-1]: + l1 += "---+" + else: + l1 += " |" + l2 += " {} |".format(centre) + + R += l1 + "\n" + R += l2 + "\n" + if coord: + r = " +" + else: + r = "+" + for j in range(self.X-1): + if self.verti[-1][j]: + r += "---+" + else: + r += "----" + r += "---+" + R += r + return R + + else : + if coord: + R = " X" + for i in range(self.X): + R += " {} ".format(i%10) + R += "\n" + R += "Y ┌" + r = "0 │" + else: + R = "┌" + r = "│" + for j in range(self.X-1): + if self.verti[0][j]: + R += "───┬" + r += " {} │".format(centre) + else: + R += "────" + r += " {} ".format(centre) + R += "───┐\n" + r += " {} │\n".format(centre) + R += r + for i in range(self.Y-1): + if coord: + if self.horiz[i][0]: + l1 = " ├" + else: + l1 = " │" + l2 = "{} │".format((i+1)%10) + else: + if self.horiz[i][0]: + l1 = "├" + else: + l1 = "│" + l2 = "│" + for j in range(self.X-1): + # 4 door + if self.horiz[i][j] and self.horiz[i][j+1] and self.verti[i][j] and self.verti[i+1][j] : inter = "┼" + # 3 door + if self.horiz[i][j] and self.horiz[i][j+1] and self.verti[i][j] and not self.verti[i+1][j] : inter = "┴" + if self.horiz[i][j] and self.horiz[i][j+1] and not self.verti[i][j] and self.verti[i+1][j] : inter = "┬" + if self.horiz[i][j] and not self.horiz[i][j+1] and self.verti[i][j] and self.verti[i+1][j] : inter = "┤" + if not self.horiz[i][j] and self.horiz[i][j+1] and self.verti[i][j] and self.verti[i+1][j] : inter = "├" + # 2 door + if self.horiz[i][j] and self.horiz[i][j+1] and not self.verti[i][j] and not self.verti[i+1][j] : inter = "─" + if self.horiz[i][j] and not self.horiz[i][j+1] and self.verti[i][j] and not self.verti[i+1][j] : inter = "┘" + if not self.horiz[i][j] and self.horiz[i][j+1] and self.verti[i][j] and not self.verti[i+1][j] : inter = "└" + if self.horiz[i][j] and not self.horiz[i][j+1] and not self.verti[i][j] and self.verti[i+1][j] : inter = "┐" + if not self.horiz[i][j] and self.horiz[i][j+1] and not self.verti[i][j] and self.verti[i+1][j] : inter = "┌" + if not self.horiz[i][j] and not self.horiz[i][j+1] and self.verti[i][j] and self.verti[i+1][j] : inter = "│" + # 1 door + if not self.horiz[i][j] and not self.horiz[i][j+1] and not self.verti[i][j] and self.verti[i+1][j] : inter = "╷" + if not self.horiz[i][j] and not self.horiz[i][j+1] and self.verti[i][j] and not self.verti[i+1][j] : inter = "╵" + if not self.horiz[i][j] and self.horiz[i][j+1] and not self.verti[i][j] and not self.verti[i+1][j] : inter = "╶" + if self.horiz[i][j] and not self.horiz[i][j+1] and not self.verti[i][j] and not self.verti[i+1][j] : inter = "╴" + # 0 door + if not self.horiz[i][j] and not self.horiz[i][j+1] and not self.verti[i][j] and not self.verti[i+1][j] : inter = " " + if self.horiz[i][j]: + l1 += "───"+inter + else: + l1 += " "+inter + + if self.verti[i+1][j]: + l2 += " {} │".format(centre) + else: + l2 += " {} ".format(centre) + if self.horiz[i][-1]: + l1 += "───┤" + else: + l1 += " │" + l2 += " {} │".format(centre) + + R += l1 + "\n" + R += l2 + "\n" + if coord: + r = " └" + else: + r = "└" + for j in range(self.X-1): + if self.verti[-1][j]: + r += "───┴" + else: + r += "────" + r += "───┘" + R += r + return R \ No newline at end of file diff --git a/extensions/fablabchemnitz/maze/meta.json b/extensions/fablabchemnitz/maze/meta.json new file mode 100644 index 0000000..e1ac2ba --- /dev/null +++ b/extensions/fablabchemnitz/maze/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Maze", + "id": "fablabchemnitz.de.maze", + "path": "maze", + "dependent_extensions": null, + "original_name": "Labyrinthe", + "original_id": "tiemenD.1.laby", + "license": "MIT License", + "license_url": "https://inkscape.org/de/~thjazi/%E2%98%85maze", + "comment": "", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/maze", + "fork_url": "https://inkscape.org/de/~thjazi/%E2%98%85maze", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Maze", + "inkscape_gallery_url": null, + "main_authors": [ + "inkscape.org/thjazi", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/meta.json b/extensions/fablabchemnitz/meta.json new file mode 100644 index 0000000..77aee9a --- /dev/null +++ b/extensions/fablabchemnitz/meta.json @@ -0,0 +1,19 @@ +[ + { + "name": "", + "id": "fablabchemnitz.de.", + "path": "", + "original_name": "", + "original_id": "", + "license": "", + "license_url": "", + "comment": "", + "source_url": "", + "fork_url": null, + "documentation_url": "", + "inkscape_gallery_url": null, + "main_authors": [ + "github.com/eridur-de" + ] + } +] diff --git a/extensions/fablabchemnitz/meta_explanation.json b/extensions/fablabchemnitz/meta_explanation.json new file mode 100644 index 0000000..966b75d --- /dev/null +++ b/extensions/fablabchemnitz/meta_explanation.json @@ -0,0 +1,22 @@ +/*This file contains plugin extra information per folder and is intended to give credits and information about sources, forking and documentation*/ + +[ + { + "name": "The readable name of the extension in menu", + "id": "The id which is used to store paramters in inkscape UI configuration", + "path": "the folder where the extensions is in", + "dependent_extension": "extensions which should be installed to because they are used by THIS extension", + "original_name": "the readble extension name which was assigned before putting it into MightyScape (origin)", + "original_id": "the id which was assigned before putting it into MightyScape (origin)", + "license": "the license, like GPL v2 or MIT", + "license_url": "the original URL where the license can be found", + "comment": "some extra information. Keep empty or set to 'null''", + "source_url": "the URL where the actual MightyScape data is put", + "fork_url": "the URL where the original data comes from, like github.com or gitlab.com", + "documentation_url": "the URL where MightyScape documentation can be found", + "inkscape_gallery_url": "the URL for the gallery location. Set to 'null' if it's not listed in the gallery", + "main_authors": [ + "a comma separated list of authors which work(ed) on the extension. We use either an email address and/or pattern '/:'" + ] + } +] diff --git a/extensions/fablabchemnitz/mirror/meta.json b/extensions/fablabchemnitz/mirror/meta.json new file mode 100644 index 0000000..c9a87be --- /dev/null +++ b/extensions/fablabchemnitz/mirror/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Mirror", + "id": "fablabchemnitz.de.mirror", + "path": "mirror", + "dependent_extensions": null, + "original_name": "Mirror", + "original_id": "test.apex.mirror", + "license": "GNU GPL v2", + "license_url": "https://inkscape.org/~jeko/%E2%98%85mirror", + "comment": "", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/mirror", + "fork_url": "https://inkscape.org/~jeko/%E2%98%85mirror", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Mirror", + "inkscape_gallery_url": null, + "main_authors": [ + "inkscape.org/jeko", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/mirror/mirror.inx b/extensions/fablabchemnitz/mirror/mirror.inx new file mode 100644 index 0000000..4c7ec19 --- /dev/null +++ b/extensions/fablabchemnitz/mirror/mirror.inx @@ -0,0 +1,16 @@ + + + Mirror + fablabchemnitz.de.mirror + + path + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/mirror/mirror.py b/extensions/fablabchemnitz/mirror/mirror.py new file mode 100644 index 0000000..4f3e3e7 --- /dev/null +++ b/extensions/fablabchemnitz/mirror/mirror.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 +""" +Derived from the "envelope" extension by Aaron Spike, aaron@ekips.org +By Apex 2011 +New version Jens Kober 2017, 2020 + +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 inkex.localization import inkex_gettext as _ + +class Mirror(inkex.EffectExtension): + + def effect(self): + if len(self.options.ids) < 2: + raise inkex.AbortExtension(_("This extension requires two selected objects. \nThe second must be a path, exactly two nodes long.")) + + #trafo is selected second + obj = self.svg.selected[self.options.ids[0]] + trafo = self.svg.selected[self.options.ids[1]] + + if isinstance(trafo, inkex.PathElement): + #distil trafo into two node points + trafoPath = trafo.path.transform(trafo.composed_transform()).to_superpath() + if len(trafoPath[0]) != 2: + raise inkex.AbortExtension(_("The second selected object must be exactly two nodes long.")) + + # origin of mirror line + ox = trafoPath[0][0][1][0] + oy = trafoPath[0][0][1][1] + # vector along mirror line + vx = trafoPath[0][1][1][0] - ox + vy = trafoPath[0][1][1][1] - oy + + # the transformation first translates the origin of the mirror line to [0 0], then rotates the mirror line onto the x-axis, + # reflects everything over the x-axis, undoes the rotation, and finally undoes the translation + + # alpha = atan2(vy, vx); + + # [1 0 ox] [cos(alpha) -sin(alpha) 0] [1 0 0] [cos(-alpha) -sin(-alpha) 0] [1 0 -ox] + # Transformation = [0 1 oy]*[sin(alpha) cos(alpha) 0]*[0 -1 0]*[sin(-alpha) cos(-alpha) 0]*[0 1 -oy] + # [0 0 1] [ 0 0 1] [0 0 1] [ 0 0 1] [0 0 1] + + # after some simplifications (or using your favorite symbolic math software): + + # [(vx^2-vy^2)/(vx^2+vy^2) (2 vx vy)/(vx^2+vy^2) (2 vy (ox vy-oy vx))/(vx^2+vy^2)] + # Transformation = [ (2 vx vy)/(vx^2+vy^2) -(vx^2-vy^2)/(vx^2+vy^2) -(2 vx (ox vy-oy vx))/(vx^2+vy^2)] + # [ 0 0 1] + + denom = vx**2 + vy**2 + a00 = (vx**2 - vy**2) / denom + a01 = (2 * vx * vy) / denom + a02 = 2 * (ox * vy - oy * vx) / denom + mat=[[a00, a01, vy * a02], [a01, -a00, -vx * a02]] + obj.transform = inkex.Transform(mat) @ obj.transform + + else: + if isinstance(trafo, inkex.Group): + raise inkex.AbortExtension(_("The second selected object is a group, not a path.\nTry using the procedure Object->Ungroup.")) + else: + raise inkex.AbortExtension(_("The second selected object is not a path.\nTry using the procedure Path->Object to Path.")) + +if __name__ == '__main__': + Mirror().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/move_path_node/meta.json b/extensions/fablabchemnitz/move_path_node/meta.json new file mode 100644 index 0000000..a1a3e05 --- /dev/null +++ b/extensions/fablabchemnitz/move_path_node/meta.json @@ -0,0 +1,20 @@ +[ + { + "name": "Move Path Node", + "id": "fablabchemnitz.de.move_path_node", + "path": "move_path_node", + "dependent_extensions": null, + "original_name": "Move Path Node", + "original_id": "fablabchemnitz.de.move_path_node", + "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/move_path_node", + "fork_url": null, + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Move+Path+Node", + "inkscape_gallery_url": "https://inkscape.org/~MarioVoigt/%E2%98%85move-path-node", + "main_authors": [ + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/move_path_node/move_path_node.inx b/extensions/fablabchemnitz/move_path_node/move_path_node.inx new file mode 100644 index 0000000..bb27882 --- /dev/null +++ b/extensions/fablabchemnitz/move_path_node/move_path_node.inx @@ -0,0 +1,55 @@ + + + Move Path Node + fablabchemnitz.de.move_path_node + + + true + 0 + false + 10px + 10px + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../000_about_fablabchemnitz.svg + + + + path + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/move_path_node/move_path_node.py b/extensions/fablabchemnitz/move_path_node/move_path_node.py new file mode 100644 index 0000000..020345e --- /dev/null +++ b/extensions/fablabchemnitz/move_path_node/move_path_node.py @@ -0,0 +1,184 @@ +#!/usr/bin/env python3 + +''' +Author: Mario Voigt / FabLab Chemnitz +Mail: mario.voigt@stadtfabrikanten.org +Date: 19.05.2021 +Last patch: 19.05.2021 +License: GNU GPL v3 +''' + +import copy +import inkex +from inkex import Circle, TextElement, PathElement +from inkex.paths import CubicSuperPath, Path + +class MovePathNode(inkex.EffectExtension): + + def modify(self, element): + raw = element.path.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:]) + if self.options.debug is True: + if len(subpaths) == 0: + self.msg("{} has no subpaths").format(element.get('id')) + else: + self.msg("{} has {} subpath(s)".format(element.get('id'), len(subpaths))) + + subpathNr = 0 + for path in subpaths: + subpathNr += 1 + newSubpaths = subpaths #we will overwrite them later + + pathIsClosed = False + if path[-1][0] == 'Z' or \ + (path[-1][0] == 'L' and path[0][1] == path[-1][1]) or \ + (path[-1][0] == 'C' and path[0][1] == [path[-1][1][-2], path[-1][1][-1]]) \ + : #if first is last point the path is also closed. The "Z" command is not required + pathIsClosed = True + + if self.options.debug is True: + self.msg("pathIsClosed = " + str(pathIsClosed)) + self.msg("nodes = " + str(len(path))) + + if self.options.closed_only is True and pathIsClosed is False: + if len(subpaths) == 0: + self.msg("{}/subpath {} is not closed! Skipping ...".format(element.get('id'), subpathNr)) + else: + self.msg("{} is not closed! Skipping ...".format(element.get('id'))) + continue #skip this open path + + if len(path) == 2: + continue #skip this open path (special case of straight line segment) + + if path[-1][0] == 'Z': #replace Z with another L command (which moves to the coordinates of the first M command in path) to have better overview + path[-1][0] = 'L' + path[-1][1] = path[0][1] + + #adjust if entered move number is higher than actual node count. We handle as infinite looping + moves = (self.options.movenode) % len(path) + if pathIsClosed is True: #if closed start and end collapse and "duplicate" + moves = (self.options.movenode) % (len(path) - 1) + if self.options.movenode == 0: #special handling for 0 is required + moves = 0 + + if self.options.debug is True: + self.msg("moves to perform = " + str(moves)) + self.msg("root path:") + self.msg(path) + self.msg("-"*25) + + for i in range(moves): + if len(path) > 2: #the path needs at least more than two segments, else we might just get a "pointy path" on an open path + + #we move the first segment to the end of the list + move = path[0] + del path[0] + path.append(move) + oldseg = copy.deepcopy(path[0]) #if we assign like "oldseg = path[0]", it will get overwritten. So we need copy + + if self.options.debug is True: + self.msg("moved path (move no. {}):".format(i+1)) + self.msg(path) + self.msg("-"*25) + + #Now we messed the integrity of the path. It does not begin with 'M' now. But we need an 'M'. + #It now either starts with L or C. H, V, Z cannot occure here. + if path[0][0] == 'C': #and path[-1][0] == 'M': + #self.msg("C to M") + path[0][1] = [path[0][1][-2], path[0][1][-1]] + elif path[0][0] == 'L': #and path[-1][0] == 'M': + #self.msg("L to M") + path[0][1] = [path[0][1][0], path[0][1][1]] + #else: + # self.msg("no idea") + path[0][0] = 'M' #we really need M. Does not matter if 'L' or 'C'. + + if pathIsClosed is True: + if path[-1][0] == 'M' and len(oldseg[1]) == 2: #data of an 'L' command + path[-1][0] = 'L' + path[-1][1] = path[0][1] + elif path[-1][0] == 'M' and len(oldseg[1]) > 2: #data of an 'C' command + path[-1][0] = 'C' + path[-1][1] = oldseg[1] + else: + if path[-1][0] == 'M': #if open path we just drop the dangling 'M' command completely + del path[-1] + + if self.options.debug is True: + self.msg("final path:") + self.msg(path) + self.msg("-"*25) + + newSubpaths[subpathNr - 1] = path + else: + inkex.utils.debug("More moves entered than possible to apply. Path result would be a point, not a line") + #return + + composedPath = inkex.Path() + for newSubpath in newSubpaths: + composedPath.extend(newSubpath) + + if self.options.debug is True: + self.msg("Composed path = " + str(composedPath)) + + element.path = composedPath + + def visualizeFirstTwo(self, element): + """Add a dot label for this path element""" + group = element.getparent().add(inkex.Group(id="visualize-group-" + element.get('id'))) + dot_group = group.add(inkex.Group(id="dot-group-" + element.get('id'))) + num_group = group.add(inkex.Group(id="num-group-" + element.get('id'))) + group.transform = element.transform + radius = self.svg.unittouu(self.options.dotsize) / 2 + + count = 0 + for step, (x, y) in enumerate(element.path.end_points): + count += 1 + circle = Circle(cx=str(x), cy=str(y), r=str(radius), id="circle-" + element.get('id') + "-" + str(count)) + circle.style = inkex.Style({'stroke': 'none', 'fill': '#000'}) + + text = TextElement(x=str(x + radius), y=str(y - radius), id="text-" + element.get('id') + "-" + str(count)) + text.text = str(count) #we start with #1 + text.style = inkex.Style({'font-size': self.svg.unittouu(self.options.fontsize), 'fill-opacity': '1.0', 'stroke': 'none', + 'font-weight': 'normal', 'font-style': 'normal', 'fill': '#999'}) + + dot_group.append(circle) + num_group.append(text) + + if count > 1: #we only display first two points to see the position of the first node and the path direction + break + + def add_arguments(self, pars): + pars.add_argument("--tab") + pars.add_argument("--closed_only", type=inkex.Boolean, default=False, help="If disabled we also apply on open (sub)path. Warning: This REMOVES segments!") + pars.add_argument("--movenode", type=int, default=0, help="Move starting node n nodes further") + pars.add_argument('--visualize_result', type=inkex.Boolean, default=False, help="If enabled each node gets a number and a dot") + pars.add_argument("--dotsize", default="10px", help="Size of the dots on the path nodes") + pars.add_argument("--fontsize", default="20px", help="Size of node labels") + pars.add_argument("--debug", type=inkex.Boolean, default=False, help="Debug Output") + + def effect(self): + if len(self.svg.selected) > 0: + elements = self.svg.selection.filter(PathElement).values() + if len(elements) > 0: + for element in elements: + #move starting element / change direction + self.modify(element) + + #finally apply dots to visualize the result + if self.options.visualize_result is True: + self.visualizeFirstTwo(element) + else: + inkex.errormsg('Selection seems not to contain path elements. Maybe you have selected a group instead?') + return + else: + inkex.errormsg('Please select some objects first.') + return + +if __name__ == '__main__': + MovePathNode().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/mutual_cut_line/meta.json b/extensions/fablabchemnitz/mutual_cut_line/meta.json new file mode 100644 index 0000000..96ebf5e --- /dev/null +++ b/extensions/fablabchemnitz/mutual_cut_line/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Mutual Cut Line", + "id": "fablabchemnitz.de.mutual_cut_line", + "path": "mutual_cut_line", + "dependent_extensions": null, + "original_name": "Mutual Cut Line", + "original_id": "com.kacmarcik.pathmonkey.mutual_cut_line", + "license": "MIT License", + "license_url": "https://github.com/garykac/pathmonkey/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/mutual_cut_line", + "fork_url": "https://github.com/garykac/pathmonkey", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Mutual+Cut+Line", + "inkscape_gallery_url": null, + "main_authors": [ + "github.com/garykac", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/mutual_cut_line/mutual_cut_line.inx b/extensions/fablabchemnitz/mutual_cut_line/mutual_cut_line.inx new file mode 100644 index 0000000..56ed342 --- /dev/null +++ b/extensions/fablabchemnitz/mutual_cut_line/mutual_cut_line.inx @@ -0,0 +1,16 @@ + + + Mutual Cut Line + fablabchemnitz.de.mutual_cut_line + + path + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/mutual_cut_line/mutual_cut_line.py b/extensions/fablabchemnitz/mutual_cut_line/mutual_cut_line.py new file mode 100644 index 0000000..faba348 --- /dev/null +++ b/extensions/fablabchemnitz/mutual_cut_line/mutual_cut_line.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 +''' +Mutual Cut Line +This Inkscape extension will take 2 selected line and cut them both +at their intersection point. +Only the first segment of a multi-segment line will be used. +''' +import inkex +from inkex.paths import Path +import simplepath +from math import * +from lxml import etree + +def error(message): + inkex.errormsg(message) + exit() + +class MutualCutLine(inkex.EffectExtension): + + def effect(self): + if len(self.svg.selected.items()) != 2: + error('Please select 2 lines before running this effect.') + + line = [] + numPaths = 0 + for id, path in self.svg.selected.items(): + if path.tag == inkex.addNS('path','svg'): + numPaths += 1 + style = path.get('style') + np = [style] + p = Path(path.get('d')).to_arrays() + for sp in p: + np.append([sp[1][0], sp[1][1]]) + line.append(np) + path.getparent().remove(path) #after cutting we remove the original lines + + if numPaths != 2: + error('Please select 2 lines before running this effect.') + + # Extract style and points for the first 2 line segments. + astyle = line[0][0] + bstyle = line[1][0] + a1x = line[0][1][0] + a1y = line[0][1][1] + a2x = line[0][2][0] + a2y = line[0][2][1] + b1x = line[1][1][0] + b1y = line[1][1][1] + b2x = line[1][2][0] + b2y = line[1][2][1] + + # Calculate intersection point. + adx = a1x - a2x + ady = a1y - a2y + bdx = b1x - b2x + bdy = b1y - b2y + + denom = adx * bdy - ady * bdx + numa = (a1x * a2y - a1y * a2x) + numb = (b1x * b2y - b1y * b2x) + x_num = numa * bdx - numb * adx + y_num = numa * bdy - numb * ady + + if denom == 0: + error('Lines don\'t intersect in a single point.') + x = x_num / denom + y = y_num / denom + + # TODO: Verify that the 2 segments intersect. + # Current code will connect outside the line segments. + + # Create 4 line segments from the intersection point. + svg_path = inkex.addNS('path','svg') + sega1 = etree.SubElement(self.svg.get_current_layer(), svg_path) + sega1.set('d', 'M '+str(x)+','+str(y)+' L '+str(a1x)+','+str(a1y)) + sega1.set('style', astyle) + + sega2 = etree.SubElement(self.svg.get_current_layer(), svg_path) + sega2.set('d', 'M '+str(x)+','+str(y)+' L '+str(a2x)+','+str(a2y)) + sega2.set('style', astyle) + + segb1 = etree.SubElement(self.svg.get_current_layer(), svg_path) + segb1.set('d', 'M '+str(x)+','+str(y)+' L '+str(b1x)+','+str(b1y)) + segb1.set('style', bstyle) + + segb2 = etree.SubElement(self.svg.get_current_layer(), svg_path) + segb2.set('d', 'M '+str(x)+','+str(y)+' L '+str(b2x)+','+str(b2y)) + segb2.set('style', bstyle) + +if __name__ == '__main__': + MutualCutLine().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/netting/meta.json b/extensions/fablabchemnitz/netting/meta.json new file mode 100644 index 0000000..f16ed1d --- /dev/null +++ b/extensions/fablabchemnitz/netting/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Netting", + "id": "fablabchemnitz.de.netting", + "path": "netting", + "dependent_extensions": null, + "original_name": "", + "original_id": "", + "license": "GNU GPL v2", + "license_url": "http://dp48069596.lolipop.jp/sd/scripts/script_inkscape/netting.zip", + "comment": "", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/netting", + "fork_url": "http://dp48069596.lolipop.jp/sd/scripts/script_inkscape/netting.zip", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Netting", + "inkscape_gallery_url": null, + "main_authors": [ + "Sunabe Kazumichi", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/netting/netting.inx b/extensions/fablabchemnitz/netting/netting.inx new file mode 100644 index 0000000..5b4d1f5 --- /dev/null +++ b/extensions/fablabchemnitz/netting/netting.inx @@ -0,0 +1,53 @@ + + + Netting + fablabchemnitz.de.netting + + + + + + + 0 + 1.000 + 100 + + + + + + + + + + + + + + + + + + + + + + + + + + ../000_about_fablabchemnitz.svg + + + + path + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/netting/netting.py b/extensions/fablabchemnitz/netting/netting.py new file mode 100644 index 0000000..bff16f6 --- /dev/null +++ b/extensions/fablabchemnitz/netting/netting.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 +''' +netting.py +Sunabe kazumichi 2010/3/4 +http://dp48069596.lolipop.jp/ + + +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 program nets in the line. +''' +import random +import math +import inkex +import cubicsuperpath +from lxml import etree +from inkex.paths import Path, CubicSuperPath + +class Netting(inkex.EffectExtension): + + def add_arguments(self, pars): + pars.add_argument("--tab") + pars.add_argument("--netting_type", default="allwithall", help="Netting type") + pars.add_argument("--node_shifting", type=int, default=0, help="Does not apply for 'all with all' type.") + pars.add_argument("--stroke_width", type=float, default=1.0, help="stroke width") + pars.add_argument("--allwithall_limit", type=int, default=100, help="This applies to 'all with all' option only. We limit the nettings loops because 100 path nodes will generate 100x100=10000 netted path nodes already. Might crash Inkscape at higher values easily!") + + def effect(self): + #static + style = {'stroke-width': "{:0.6f}".format(self.svg.unittouu(str(self.options.stroke_width) + "px")), 'stroke': '#000000', 'fill': 'none'} + old_segments = [] + new_segments = [] + + #get complete path data from all selected paths + for element in self.svg.selected.filter(inkex.PathElement).values(): + d = element.get('d') + p = CubicSuperPath(Path(d)) + for subpath in p: + for i, csp in enumerate(subpath): + old_segments.append("%f,%f" % (csp[1][0], csp[1][1])) + + if self.options.netting_type == "allwithall": + count = 0 + allnet_group = inkex.Group(id="g" + element.get('id')) + pathsCollection = [] + self.svg.get_current_layer().append(allnet_group) + for segment1 in range(0, len(old_segments)): + count = count + 1 + for segment2 in range(1, len(old_segments)): + if old_segments[segment1] != old_segments[segment2]: + pathVariant1 = Path('M' + old_segments[segment1] + ' L' + old_segments[segment2]) + pathVariant2 = Path('M' + old_segments[segment2] + ' L' + old_segments[segment1]) #the reversed one + if pathVariant1 not in pathsCollection and pathVariant2 not in pathsCollection: + pathsCollection.append(pathVariant1) + if count >= self.options.allwithall_limit: + break #skip + + for p in pathsCollection: + allnet_path = inkex.PathElement() + allnet_path.style = style + allnet_path.path = p + allnet_group.append(allnet_path) + + + elif self.options.netting_type == "alternatingly": + #build up the net path between the path points alternatingly + first = True + while len(old_segments) > 0: + if first is True: + new_segments.append('M') + first = False + else: + new_segments.append('L') + new_segments.append(old_segments.pop(0)) + if len(old_segments) > 0: + new_segments.append('L') + new_segments.append(old_segments.pop()) + + shift = self.options.node_shifting + if shift < 0: + counter = -1 + else: + counter = +1 + for i in range(0, self.options.node_shifting, counter): + for i in range(4): + shifting = new_segments[0] + if new_segments[0] == 'M': + shifting = 'L' #overwrite possible 'M' with 'L' + del new_segments[0] + new_segments.append(shifting) + new_segments[0] = 'M' #let's begin the path with 'M' again + + #create the path and add it to the current layer + net_path = inkex.PathElement() + net_path.style = style + net_path.path = Path(" ".join(new_segments)) + self.svg.get_current_layer().append(net_path) + +if __name__ == '__main__': + Netting().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/number_subpaths/meta.json b/extensions/fablabchemnitz/number_subpaths/meta.json new file mode 100644 index 0000000..d2a6672 --- /dev/null +++ b/extensions/fablabchemnitz/number_subpaths/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Number Subpaths", + "id": "fablabchemnitz.de.number_subpaths", + "path": "number_subpaths", + "dependent_extensions": null, + "original_name": "Number Subpaths", + "original_id": "org.inkscape.filter.number_subpaths", + "license": "GNU GPL v2", + "license_url": "https://gitlab.com/EllenWasbo/inkscape-extension-number-subpaths/-/blob/master/path_number_subpaths.py", + "comment": "", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/number_subpaths", + "fork_url": "https://gitlab.com/EllenWasbo/inkscape-extension-number-subpaths", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Number+Subpaths", + "inkscape_gallery_url": null, + "main_authors": [ + "gitlab.com/EllenWasbo", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/number_subpaths/number_subpaths.inx b/extensions/fablabchemnitz/number_subpaths/number_subpaths.inx new file mode 100644 index 0000000..d4aadd9 --- /dev/null +++ b/extensions/fablabchemnitz/number_subpaths/number_subpaths.inx @@ -0,0 +1,21 @@ + + + Number Subpaths + fablabchemnitz.de.number_subpaths + + 10px + 10px + false + + + path + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/number_subpaths/number_subpaths.py b/extensions/fablabchemnitz/number_subpaths/number_subpaths.py new file mode 100644 index 0000000..4199714 --- /dev/null +++ b/extensions/fablabchemnitz/number_subpaths/number_subpaths.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2005 Aaron Spike, aaron@ekips.org +# Modified by Ellen Wasbo, ellen@wasbo.net 2021 - number subpaths and mark start/end element with green/red dot +# +# 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. +# +import math +import inkex +from inkex import TextElement, Circle + +class NumberSubpaths(inkex.EffectExtension): + """Mark start and end elements with numbered dots according to the options""" + def add_arguments(self, pars): + pars.add_argument("--dotsize", default="10px", help="Size of the dots on the path elements") + pars.add_argument("--fontsize", default="10px", help="Size of element labels") + pars.add_argument("--showID", type=inkex.Boolean, default=False) + + def effect(self): + if not self.svg.selected: + raise inkex.AbortExtension("Please select an object.") + for element in self.svg.selection.values(): + self.add_dot(element) + + def add_dot(self, element): + """Add a dot label for this path element""" + group = element.getparent().add(inkex.Group()) + dot_group = group.add(inkex.Group()) + num_group = group.add(inkex.Group()) + group.transform = element.transform + + styleStart = inkex.Style({'stroke': 'none', 'fill': '#00ff00'}) + styleEnd = inkex.Style({'stroke': 'none', 'fill': '#ff0000'}) + + idTxt='' + if self.options.showID==True: + idTxt=element.get('id')+', ' + + cc=0 + for sub in element.path.to_superpath(): + x=sub[0][1][0] + y=sub[0][1][1] + circle = dot_group.add(Circle(cx=str(x), cy=str(y), r=str(self.svg.unittouu(self.options.dotsize) / 2))) + circle.style = styleStart + num_group.append(self.add_text( + x + (self.svg.unittouu(self.options.dotsize) / 3), + y - (self.svg.unittouu(self.options.dotsize) / 3), idTxt+str(cc))) + x=sub[-1][1][0] + y=sub[-1][1][1] + circle = dot_group.add(Circle(cx=str(x), cy=str(y), r=str(self.svg.unittouu(self.options.dotsize) *0.9 / 2))) + circle.style = styleEnd + num_group.append(self.add_text( + x + (self.svg.unittouu(self.options.dotsize) / 3), + y - (self.svg.unittouu(self.options.dotsize) / 3), idTxt+str(cc))) + cc+=1 + + def add_text(self, x, y, text): + """Add a text label at the given location""" + elem = TextElement(x=str(x), y=str(y)) + elem.text = str(text) + elem.style = { + 'font-size': self.svg.unittouu(self.options.fontsize), + 'fill-opacity': '1.0', + 'stroke': 'none', + 'font-weight': 'normal', + 'font-style': 'normal', + 'fill': '#999'} + return elem + +if __name__ == '__main__': + NumberSubpaths().run() diff --git a/extensions/fablabchemnitz/parabola/meta.json b/extensions/fablabchemnitz/parabola/meta.json new file mode 100644 index 0000000..0f5e273 --- /dev/null +++ b/extensions/fablabchemnitz/parabola/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Parabola", + "id": "fablabchemnitz.de.parabola", + "path": "parabola", + "dependent_extensions": null, + "original_name": "Parabola", + "original_id": "org.inkscape.render.parabola", + "license": "GNU GPL v3", + "license_url": "https://github.com/opensourcebear/inkscape-extension-parabola/blob/main/LICENSE", + "comment": "", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/parabola", + "fork_url": "https://github.com/opensourcebear/inkscape-extension-parabola", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Parabola", + "inkscape_gallery_url": null, + "main_authors": [ + "github.com/opensourcebear", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/parabola/parabola.inx b/extensions/fablabchemnitz/parabola/parabola.inx new file mode 100644 index 0000000..2708e9a --- /dev/null +++ b/extensions/fablabchemnitz/parabola/parabola.inx @@ -0,0 +1,36 @@ + + + Parabola + fablabchemnitz.de.parabola + + + 120 + 120 + 25 + + + + + + + + + true + true + true + true + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/parabola/parabola.py b/extensions/fablabchemnitz/parabola/parabola.py new file mode 100644 index 0000000..237b6d0 --- /dev/null +++ b/extensions/fablabchemnitz/parabola/parabola.py @@ -0,0 +1,240 @@ +#!/usr/bin/env python3 +# coding=utf-8 +# +# 2/27/2021 - v.1.1.0 +# Copyright (C) 2021 Reginald Waters opensourcebear@nthebare.com +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +""" +This extension renders a wireframe shape and then draws lines to form a parabola +shape. + +The height and width are independently variable. The number of lines will change +the density of the end product. +""" +import inkex + +from inkex import turtle as pturtle + +class Parabola(inkex.GenerateExtension): + + container_label = 'Parabola' + def add_arguments(self, pars): + pars.add_argument("--height", type=int, default=300, help="Shape Height") + pars.add_argument("--width", type=int, default=300, help="Shape Width") + pars.add_argument("--seg_count", type=int, default=10, help="Number of line segments") + pars.add_argument("--shape", default="square") + pars.add_argument("--tab", default="common") + pars.add_argument("--c1", default="true") + pars.add_argument("--c2", default="false") + pars.add_argument("--c3", default="false") + pars.add_argument("--c4", default="false") + + def generate(self): + # Let's simplify the variable names + ht = int(self.options.height) + wt = int(self.options.width) + sc = int(self.options.seg_count) + shape = self.options.shape + c1 = self.options.c1 + c2 = self.options.c2 + c3 = self.options.c3 + c4 = self.options.c4 + + point = self.svg.namedview.center + style = inkex.Style({ + 'stroke-linejoin': 'miter', 'stroke-width': str(self.svg.unittouu('1px')), + 'stroke-opacity': '1.0', 'fill-opacity': '1.0', + 'stroke': '#000000', 'stroke-linecap': 'butt', + 'fill': 'none' + }) + + # Setting the amount to move across the horizontal and vertical + increaseht = (ht / sc) + increasewt = (wt / sc) + + tur = pturtle.pTurtle() + + tur.pu() # Pen up + tur.setpos(point) # start in the center + + if shape == "cross": + # We draw the cross shape and store the 4 points + # Can this be looped? + # Should I store the coordinates in an array/list? + tur.forward((ht / 2)) + toppoint = tur.getpos() + if c3 == 'true' or c4 == 'true': + tur.pd() + tur.backward((ht / 2)) + tur.pu() + if c1 == 'true' or c2 == 'true': + tur.pd() + tur.backward((ht / 2)) + bottompoint = tur.getpos() + tur.pu() + tur.setpos(point) + tur.left(90) + tur.forward((wt / 2)) + rightpoint = tur.getpos() + if c3 == 'true' or c2 == 'true': + tur.pd() + tur.backward((wt / 2)) + tur.pu() + if c1 == 'true' or c4 == 'true': + tur.pd() + tur.backward((wt / 2)) + leftpoint = tur.getpos() + + while sc > 0: + if c1 == 'true': + # Drawing the SE Corner based on SW coordinates + # We always draw this corner + tur.pu() + tur.setpos((bottompoint[0], bottompoint[1] - ( (increaseht / 2) * sc ) )) + tur.pd() + tur.setpos((bottompoint[0] + ( (increasewt / 2) * sc ), bottompoint[1] - (ht / 2) )) + + if c2 == 'true': # Drawing the SW Corner based on SE Coordinates + tur.pu() + tur.setpos((bottompoint[0], bottompoint[1] - ( (increaseht / 2) * sc ) )) + tur.pd() + tur.setpos((bottompoint[0] - ( (increasewt / 2) * sc ), bottompoint[1] - (ht / 2) )) + + if c3 == 'true': # Drawing the NW Corner based on NE Coordinates + tur.pu() + tur.setpos((toppoint[0], toppoint[1] + ( (increaseht / 2) * sc ) )) + tur.pd() + tur.setpos((toppoint[0] - ( (increasewt / 2) * sc ), toppoint[1] + (ht / 2) )) + + if c4 == 'true': # Drawing the NE Corner based on NW Coordinates + tur.pu() + tur.setpos((toppoint[0], toppoint[1] + ( (increaseht / 2) * sc ) )) + tur.pd() + tur.setpos((toppoint[0] + ( (increasewt / 2) * sc ), toppoint[1] + (ht / 2) )) + + sc = sc - 1 + + if shape == "triangle": + # We draw the triangle and store the 3 corner points + # Loopable? + tur.backward((ht / 2)) + tur.left(90) + tur.forward((wt /2)) + cornera = tur.getpos() + if c3 == 'true' or c2 == 'true': + tur.pd() + tur.backward((wt)) + cornerb = tur.getpos() + tur.pu() + if c2 == 'true' or c1 == 'true': + tur.pd() + tur.setpos((point[0], (cornera[1] - ht) )) + cornerc = tur.getpos() + tur.pu() + if c1 == 'true' or c3 == 'true': + tur.pd() + tur.setpos(cornera) + +# So.. The math below took a lot of trial and error to figure out... +# I probably need to take some geography classes... + + while sc > 0: + if c1 == 'true': + tur.pu() + tur.setpos(( (cornerb[0] + ((increasewt / 2) * (sc)) - (wt / 2)), cornerb[1] + (increaseht * sc) - ht )) + tur.pd() + tur.setpos(( (cornera[0] + (increasewt / 2) * (sc)), cornera[1] - (increaseht * sc) )) + + if c2 == 'true': + tur.pu() + tur.setpos((cornerb[0] - (increasewt * sc ) , cornerb[1] )) + tur.pd() + tur.setpos(( (cornerb[0] + ((increasewt / 2) * sc) - (wt / 2)), cornerb[1] + (increaseht * sc) - ht )) + + if c3 == 'true': + tur.pu() + tur.setpos((cornera[0] + (increasewt * sc ) , cornera[1] )) + tur.pd() + tur.setpos(( (cornera[0] - ((increasewt / 2) * sc) + (wt / 2)), cornera[1] + (increaseht * sc) - ht )) + + sc = sc - 1 + + + if shape == "square": + # We draw out the square shape and store the coordinates for each corner + # Can this be looped? + tur.left(90) + tur.forward((wt / 2)) + tur.left(90) + tur.forward((ht / 2)) + swcorner = tur.getpos() + if c4 == 'true' or c3 == 'true': # We only draw the 2 lines that are part of these corners + tur.pd() # Pen Down + tur.left(90) + tur.forward(wt) + secorner = tur.getpos() + tur.pu() + if c3 == 'true' or c2 == 'true': # We only draw the 2 lines that are part of these corners + tur.pd() + tur.left(90) + tur.forward(ht) + necorner = tur.getpos() + tur.pu() + if c1 == 'true' or c2 == 'true': # We only draw the 2 lines that are part of these corners + tur.pd() + tur.left(90) + tur.forward(wt) + nwcorner = tur.getpos() + tur.left(90) + tur.pu() + if c4 == 'true' or c1 == 'true': # We only draw the 2 lines that are part of these corners + tur.pd() + tur.forward(ht) + + while sc > 0: + if c1 == 'true': + # Drawing the NW Corner based on SW coordinates + # We always draw this corner + tur.pu() + tur.setpos((swcorner[0], swcorner[1] - ( increaseht * sc ) )) + tur.pd() + tur.setpos((swcorner[0] + ( increasewt * sc ), swcorner[1] - ht)) + + if c2 == 'true': # Drawing the NE Corner based on SE Coordinates + tur.pu() + tur.setpos((secorner[0], secorner[1] - ( increaseht * sc ) )) + tur.pd() + tur.setpos((secorner[0] - ( increasewt * sc ), secorner[1] - ht)) + + if c3 == 'true': # Drawing the SE Corner based on NE Coordinates + tur.pu() + tur.setpos((necorner[0], necorner[1] + ( increaseht * sc ) )) + tur.pd() + tur.setpos((necorner[0] - ( increasewt * sc ), necorner[1] + ht)) + + if c4 == 'true': # Drawing the SW Corner based on NW Coordinates + tur.pu() + tur.setpos((nwcorner[0], nwcorner[1] + ( increaseht * sc ) )) + tur.pd() + tur.setpos((nwcorner[0] + ( increasewt * sc ), nwcorner[1] + ht)) + + sc = sc - 1 + + return inkex.PathElement(d=tur.getPath(), style=str(style)) + +if __name__ == "__main__": + Parabola().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/parabola_2/meta.json b/extensions/fablabchemnitz/parabola_2/meta.json new file mode 100644 index 0000000..67e5c7f --- /dev/null +++ b/extensions/fablabchemnitz/parabola_2/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Parabola 2", + "id": "fablabchemnitz.de.parabola_2", + "path": "parabola_2", + "dependent_extensions": null, + "original_name": "Parabola", + "original_id": "org.inkscape.render.parabola", + "license": "GNU GPL v3", + "license_url": "https://github.com/opensourcebear/inkscape-extension-parabola/blob/main/LICENSE", + "comment": "", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/parabola_2", + "fork_url": "https://github.com/opensourcebear/inkscape-extension-parabola", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Parabola+2", + "inkscape_gallery_url": null, + "main_authors": [ + "github.com/opensourcebear", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/parabola_2/parabola_2.inx b/extensions/fablabchemnitz/parabola_2/parabola_2.inx new file mode 100644 index 0000000..6b8c193 --- /dev/null +++ b/extensions/fablabchemnitz/parabola_2/parabola_2.inx @@ -0,0 +1,26 @@ + + + Parabola 2 + fablabchemnitz.de.parabola_2 + 200 + 25 + + + + + + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/parabola_2/parabola_2.py b/extensions/fablabchemnitz/parabola_2/parabola_2.py new file mode 100644 index 0000000..efb6b7c --- /dev/null +++ b/extensions/fablabchemnitz/parabola_2/parabola_2.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python3 +# +# 7/9/2021 - v.2.0 +# +# Copyright (C) 2021 Reginald Waters opensourcebear@nthebare.com +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +""" +This extension renders a wireframe shape and then draws lines to form a parabola +shape. + +The height and width are independently variable. The number of lines will change +the density of the end product. + +# Triangle has 3 sides and the sum of the 3 angles is 180 degrees +# (sides - 2) * 180 +# This can be 60/60/60 or 90/45/45 + +# Square has 4 sides and the sum of 4 angles is 360 degrees +# (sides - 2) * 180 +# 90/90/90/90 + +# Pentagon has 5 sides and the sum of 5 angels is 540 degrees +# (sides - 2) * 180 +# 108/108/108/108/108 +... +""" +import inkex + +from inkex import turtle as pturtle + +class Parabola2(inkex.GenerateExtension): + container_label = 'Parabola 2' + def add_arguments(self, pars): + pars.add_argument("--length", type=int, default=300, help="Side Length") + pars.add_argument("--segments", type=int, default=10, help="Number of line segments") + pars.add_argument("--shape", default="square") + + sideopts = [ + (1,2),(1,3),(1,4),(1,5),(1,6),(1,7),(1,8), + (2,3),(2,4),(2,5),(2,6),(2,7),(2,8), + (3,4),(3,5),(3,6),(3,7),(3,8), + (4,5),(4,6),(4,7),(4,8), + (5,6),(5,7),(5,8), + (6,7),(6,8), + (7,8)] + + def generate(self): + sl = self.options.length + sc = self.options.segments + shape = self.options.shape + + cp = self.svg.namedview.center # Center Point + sp = (cp[0] + (sl / 2), cp[1] + (sl / 2)) # Start Point + cords = [] + + def mapshape(sides, sl, sc, sp): + exteriorAngle = 360/sides + movement = sl / sc + tur.setpos(sp) + for i in range(sides): + sidecords = [] + tl = 0 # total length + while tl < sl: + sidecords.append(tur.getpos()) + tur.forward(movement) + tl += movement +# sidecords.append(tur.getpos()) + tur.right(exteriorAngle) + cords.append(sidecords) + return cords + + def mapcross(sl, sc, sp): + movement = sl / sc + tur.setpos(sp) + sidecords = [] + tl = 0 + tur.forward(sl) + while tl < sl: + tur.backward(movement) + sidecords.append(tur.getpos()) + tur.right(90) + + def drawshape(cords): + tur.setpos(cords[0][0]) + for i in range(len(cords)): +# tur.pd() +# tur.setpos(cords[i][-1]) +# tur.pu() + for side in range(len(cords)): + for cord in range(len(cords[0])): + if side == (len(cords) - 1): + tur.setpos(cords[side][cord]) + tur.pd() + if cord != (len(cords[0])): + tur.setpos(cords[0][cord]) + tur.pu() + else: + tur.setpos(cords[side][cord]) + tur.pd() + tur.setpos(cords[side + 1][cord]) + tur.pu() + tur.pu() + + tur = pturtle.pTurtle() + tur.pu() + + if shape == "triangle": + sides = 3 + mapshape(sides, sl, sc, sp) + drawshape(cords) + elif shape == "square": + sides = 4 + mapshape(sides, sl, sc, sp) + drawshape(cords) + elif shape == "pentagon": + sides = 5 + mapshape(sides, sl, sc, sp) + drawshape(cords) + elif shape == "hexagon": + sides = 6 + mapshape(sides, sl, sc, sp) + drawshape(cords) + elif shape == "septagon": + sides = 7 + mapshape(sides, sl, sc, sp) + drawshape(cords) + elif shape == "octagon": + sides = 8 + mapshape(sides, sl, sc, sp) + drawshape(cords) + + style = inkex.Style({ + 'stroke-linejoin': 'miter', 'stroke-width': str(self.svg.unittouu('1px')), + 'stroke-opacity': '1.0', 'fill-opacity': '1.0', + 'stroke': '#000000', 'stroke-linecap': 'butt', + 'fill': 'none' + }) + return inkex.PathElement(d=tur.getPath(), style=str(style)) + +if __name__ == "__main__": + Parabola2().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/piano_scale/meta.json b/extensions/fablabchemnitz/piano_scale/meta.json new file mode 100644 index 0000000..e8a84c7 --- /dev/null +++ b/extensions/fablabchemnitz/piano_scale/meta.json @@ -0,0 +1,22 @@ +[ + { + "name": "Piano Scale", + "id": "fablabchemnitz.de.piano_scale", + "path": "piano_scale", + "dependent_extensions": null, + "original_name": "Piano scale", + "original_id": "Page::Blank", + "license": "GNU GPL v2", + "license_url": "https://github.com/piroxiljin/inkscape-music-scale-generator/blob/master/share/extensions/svgPianoScale.py", + "comment": "See also https://github.com/Neon22/inkscape-music-scale-generator", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/piano_scale", + "fork_url": "https://inkscape.org/~Neon22/%E2%98%85pianoscale", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Piano+Scale", + "inkscape_gallery_url": null, + "main_authors": [ + "github.com/piroxiljin", + "github.com/Neon22", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/piano_scale/piano_scale.inx b/extensions/fablabchemnitz/piano_scale/piano_scale.inx new file mode 100644 index 0000000..931abbc --- /dev/null +++ b/extensions/fablabchemnitz/piano_scale/piano_scale.inx @@ -0,0 +1,46 @@ + + + Piano Scale + fablabchemnitz.de.piano_scale + C1 + B2 + C1 + + + 2212221 + + + + + + + + + + + + + + + + + + + + + + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/piano_scale/piano_scale.py b/extensions/fablabchemnitz/piano_scale/piano_scale.py new file mode 100644 index 0000000..23d4a01 --- /dev/null +++ b/extensions/fablabchemnitz/piano_scale/piano_scale.py @@ -0,0 +1,331 @@ +#!/usr/bin/env python3 + +''' +svgPianoScale.py +Inkscape generator plugin for automatic creation schemes of musical scales and chords. + +Copyright (C) 2011 Iljin Alexender + +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 +''' + +__version__ = "1.0.1" +# Original by Alexander Iljin +# Some mods to 0.91 by Neon22 2016 + +import inkex +import re +import math +from datetime import * +from lxml import etree + +notes = ('C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B') +keys_color = ('W', 'B', 'W', 'B', 'W', 'W', 'B', 'W', 'B', 'W', 'B', 'W') # 12 notes +keys = {'C':'W', 'C#':'B', 'D':'W', 'D#':'B', 'E':'W', 'F':'W', 'F#':'B', 'G':'W', + 'G#':'B', 'A':'W', 'A#':'B', 'B':'W' } +keys_numbers = {'C':'0', 'C#':'0', 'D':'1', 'D#':'1', 'E':'2', 'F':'3', 'F#':'3', 'G':'4', + 'G#':'4', 'A':'5', 'A#':'5', 'B':'6' } +keys_order = {'C':'0', 'C#':'1', 'D':'2', 'D#':'3', 'E':'4', 'F':'5', 'F#':'6', 'G':'7', + 'G#':'8', 'A':'9', 'A#':'10', 'B':'11' } + +intervals = ("2212221", "2122212", "1222122", "2221221", "2212212", "2122122", "1221222") + +# Drawing style +White = '#ffffff' +Black = '#000000' +Marker_color = '#b3b3b3' # Ellipse fill color + +helpSheets = [["Ionian (major) scale", "2212221"], + ["Dorian scale", "2122212"], + ["Phrygian scale", "1222122"], + ["Lydian scale", "2221221"], + ["Mixolydian scale", "2212212"], + ["Aeolian (natural minor) scale", "2122122"], + ["Locrian scale", "1221222"] + ] + +def keyNumberFromNote(note): + """ Given a note such as C1 or C#1 where: + - the 1 defines the octave (starts from 1) + - the # defines note is sharp + return the notes numeric value, from 0 + """ + note = note.upper().strip() + octave = 1 + if '#' in note : # sharp + if (len(note) > 2) and note[2].isdigit(): + octave = int(note[2]) + note = note[0:2] + else: + if (len(note) > 1) and note[1].isdigit(): + octave = int(note[1]) + note = note[0] + return int(keys_order[note])+(octave-1)*12 + +def whiteKeyCountInRange(firstNote, lastNote): + """ Count the White notes between + - used by createPiano + """ + count = 0 + for key in range(firstNote, lastNote+1): + if keys_color[key%12] == "W": + count += 1 + return count + +def colorFromKey(keyNumber): + """ Return B or W based on key. Use octaves + - used by create_markers + """ + return keys_color[keyNumber%12] + +class PianoScale(inkex.EffectExtension): + marker_radius_factor = 0.42 # position marker in X on piano key + marker_y_offset_factor = 0.92 # position marker in Y + + def add_arguments(self, pars): + pars.add_argument("--firstNote", default="C1") + pars.add_argument("--lastNote", default="B2") + pars.add_argument("--tab") + pars.add_argument("--intervals") + pars.add_argument("--keynote") + pars.add_argument("--scale", type=int) + pars.add_argument("--helpSheet", type=int) + + def calculate_size_and_positions(self): + " Determine page size and define key dimensions " + self.doc_width = self.svg.unittouu(self.document.getroot().get('width')) + self.doc_height = self.svg.unittouu(self.document.getroot().get('height')) + # Size of the keys + self.black_key_width = self.svg.unittouu('3.6 mm'); + self.white_key_width = self.svg.unittouu('6 mm'); + self.black_key_height = self.svg.unittouu('18 mm'); + self.white_key_height = self.svg.unittouu('30 mm'); + + def createBlackKey(self, parent, number): + """ Insert Black key into scene + - number times width is X position + """ + key_atts = {'x':str(self.white_key_width * number + self.white_key_width - self.black_key_width/2), + 'y':'0.0', + 'width':str(self.black_key_width), + 'height':str(self.black_key_height), + 'ry':str(self.svg.unittouu('0.7 mm')), + 'style':'fill:%s;stroke:%s;stroke-width:%s;stroke-opacity:1;fill-opacity:1' %(Black, Black, self.svg.unittouu('0.1 mm')) } + white_key = etree.SubElement(parent, 'rect', key_atts) + + def createWhiteKey(self, parent, number): + """ Insert White key into scene + - number times width is X position + """ + key_atts = {'x':str(self.white_key_width * number), + 'y':'0.0', + 'width':str(self.white_key_width), + 'height':str(self.white_key_height), + 'ry':str(self.svg.unittouu('0.7 mm')), + 'style':'fill:%s;stroke:%s;stroke-width:%s;stroke-opacity:1;fill-opacity:1' % (White, Black, self.svg.unittouu('0.25 mm'))} + white_key = etree.SubElement(parent, 'rect', key_atts) + + def createKeyByNumber(self, parent, keyNumber): + """ Use Keynumber to detrmine octave and position within + - draw correct key on basis of note in octave sequence. + """ + octave = math.floor(keyNumber / 12) + 1 + note = keyNumber % 12 + key = int(keys_numbers[notes[note]]) + if keys_color[note] == "W": + self.createWhiteKey(parent, key+7*(octave-1)) + else: + self.createBlackKey(parent, key+7*(octave-1)) + + def createKeyInRange(self, parent, firstKeyNum, lastKeyNum): + """ Draw keys in a range + - do it twice so Black keys are drawn over White ones + """ + for key in range(firstKeyNum, lastKeyNum+1): + if keys_color[key % 12] == 'W': + self.createKeyByNumber(parent, key) + for key in range(firstKeyNum, lastKeyNum+1): + if keys_color[key % 12] == 'B': + self.createKeyByNumber(parent, key) + + def createPiano(self, parent): + """ Draw keys defined by options + - add Piano 'box' above + """ + firstKeyNumber = keyNumberFromNote(self.options.firstNote) + lastKeyNumber = keyNumberFromNote(self.options.lastNote) + self.createKeyInRange(parent, firstKeyNumber, lastKeyNumber) + # Draw the Piano box above keys + rectBump = (self.white_key_width - self.black_key_width/2) + rectBump = self.svg.unittouu('1 mm') + rect_x1 = self.white_key_width * (whiteKeyCountInRange(0, firstKeyNumber)-1)- rectBump + rect_y1 = self.svg.unittouu('-3 mm') + rect_width = self.white_key_width * (whiteKeyCountInRange(firstKeyNumber, lastKeyNumber)) + rectBump*2 + rect_height = self.svg.unittouu('4 mm') + rect_atts = {'x':str(rect_x1), + 'y':str(rect_y1), + 'width':str(rect_width), + 'height':str(rect_height), + 'ry':str(0), + 'style':'fill:%s;stroke:none;fill-opacity:1' %(White) } + rect = etree.SubElement(parent, 'rect', rect_atts) + path_atts = {'style':'fill:%s;stroke:%s;stroke-width:%s;stroke-opacity:1' %(White, Black, self.svg.unittouu('0.25 mm')), + 'd':'m %s,%s l 0,%s %s,0 0, %s' % (rect_x1, rect_y1, rect_height, rect_width, -rect_height) } + path = etree.SubElement(parent, 'path', path_atts) + + def createMarkerAt(self, parent, x, y, radius, markerText): + " Draw a Marker at position x,y " + markerGroup = etree.SubElement(parent, 'g') + # should replace with svg:circle + ellipce_atts = { + inkex.addNS('cx','sodipodi'):str(x), + inkex.addNS('cy','sodipodi'):str(y), + inkex.addNS('rx','sodipodi'):str(radius), + inkex.addNS('ry','sodipodi'):str(radius), + inkex.addNS('type','sodipodi'):'arc', + 'd':'m %s,%s a %s,%s 0 1 1 %s,0 %s,%s 0 1 1 %s,0 z' %(x+radius, y, x, y, -radius*2, x, y, radius*2), + 'style':'fill:%s;stroke:%s;stroke-width:%s;stroke-opacity:1;fill-opacity:1' %(Marker_color, Black, self.svg.unittouu('0.125 mm'))} + ellipse = etree.SubElement(markerGroup, 'path', ellipce_atts) + # draw the text + textstyle = {'font-size': '4px', + 'font-family': 'arial', + 'text-anchor': 'middle', + 'text-align': 'center', + 'fill': Black } + text_atts = {'style':str(inkex.Style(textstyle)), + 'x': str(x), + 'y': str(y + radius*0.5) } + text = etree.SubElement(markerGroup, 'text', text_atts) + text.text = str(markerText) + + def createMarkerOnWhite(self, parent, whiteNumber, markerText): + " Position Marker on White key " + radius = self.white_key_width * self.marker_radius_factor + center_x = self.white_key_width * (whiteNumber + 0.5) + center_y = self.white_key_height * self.marker_y_offset_factor - radius + self.createMarkerAt(parent, center_x, center_y, radius, markerText) + + def createMarkerOnBlack(self, parent, whiteNumber, markerText): + " Position Marker on Black key " + radius = self.white_key_width * self.marker_radius_factor + center_x = self.white_key_width * (whiteNumber + 1) + center_y = self.black_key_height * self.marker_y_offset_factor - radius + self.createMarkerAt(parent, center_x, center_y, radius, markerText) + + def createMarkers(self, parent, keyNumberList, markerTextList): + current=0 + for key in keyNumberList: + octave = math.floor(key/12) + if colorFromKey(key) == "W": + self.createMarkerOnWhite(parent, int(keys_numbers[notes[key%12]])+(octave)*7, markerTextList[current]) + else: + self.createMarkerOnBlack(parent, int(keys_numbers[notes[key%12]])+(octave)*7, markerTextList[current]) + current += 1; + + def createMarkersFromIntervals(self, parent, intervals): + """ Check intervals. + Then gather keys which need markers + and the text for each one. + Make markers. + """ + # Check intervals are well defined and markers are legit. + intervalSumm = sum([int(i) for i in intervals]) + if intervalSumm != 12: + inkex.debug("Warning! Scale must have 12 half-tones. But %d defined."%(intervalSumm)) + + firstKeyNum = keyNumberFromNote(self.options.firstNote) + lastKeyNum = keyNumberFromNote(self.options.lastNote) + + markedKeys = () + markerText = () + if keyNumberFromNote(self.options.keynote) in range(firstKeyNum, lastKeyNum+1): + currentKey = keyNumberFromNote(self.options.keynote) + markedKeys = (currentKey,) + markerText = ('1',) + currentInterval = 0 + for key in range(keyNumberFromNote(self.options.keynote), lastKeyNum+1): + if key - currentKey == int(intervals[currentInterval]): + markedKeys += (key,) + currentInterval += 1 + markerText += (str(currentInterval+1),) + if currentInterval == len(intervals): + currentInterval = 0 + currentKey = key + # + currentKey = keyNumberFromNote(self.options.keynote) + currentInterval = len(intervals)-1 + for key in range(keyNumberFromNote(self.options.keynote), firstKeyNum-1, -1): + if currentKey - key == int(intervals[currentInterval]): + markedKeys += (key,) + markerText += (str(currentInterval+1),) + currentInterval -= 1 + if currentInterval == -1: + currentInterval = len(intervals)-1 + currentKey = key + # make the markers + self.createMarkers(parent, markedKeys, markerText) + + def createHelpSheet(self, parent, title, intervals): + """ Draw big text Label and draw 12 different scales + """ + textstyle = {'font-size': '22px', + 'font-family': 'arial', + 'text-anchor': 'middle', + 'text-align': 'center', + 'fill': Black } + text_atts = {'style':str(inkex.Style(textstyle)), + 'x': str( self.doc_width/2 ), + 'y': str( self.black_key_height) } + text = etree.SubElement(parent, 'text', text_atts) + text.text = title + # + for i in range(0, 12): + # override the ui input value for each note in the scale + self.options.keynote = notes[i] + # calculate the piano position on the page + if keys_color[i] == "W": + t = 'translate(%s,%s)' % (self.doc_width/2, + self.doc_height - self.white_key_height*1.5 + - (self.white_key_height + self.svg.unittouu('7 mm')) * int(keys_numbers[self.options.keynote]) ) + else: # Black key + t = 'translate(%s,%s)' % (self.svg.unittouu('7 mm'), + self.doc_height- self.white_key_height*1.5 + - (self.white_key_height+self.svg.unittouu('7 mm')) * int(keys_numbers[self.options.keynote]) - self.white_key_height*0.5 ) + group = etree.SubElement(parent, 'g', { 'transform':t}) + # Create a piano using that keynote in the Scale (defined in intervals) + self.createPiano(group) + self.createMarkersFromIntervals(group, intervals) + + def effect(self): + self.calculate_size_and_positions() + parent = self.document.getroot() + if str(self.options.tab) == "scale": + t = 'translate(%s,%s)' % (self.svg.namedview.center[0], self.svg.namedview.center[1]) + group = etree.SubElement(parent, 'g', { 'transform':t}) + self.createPiano(group) + self.createMarkersFromIntervals(group, intervals[self.options.scale]) + elif str(self.options.tab) == "helpSheet": + t = 'translate(%s,%s)' % (self.svg.unittouu('5 mm'), self.svg.unittouu('5 mm')) + group = etree.SubElement(parent, 'g', { 'transform':t}) + scale_index = self.options.helpSheet + self.createHelpSheet(group, helpSheets[scale_index][0], helpSheets[scale_index][1]) + else: # direct intervals + t = 'translate(%s,%s)' % (self.svg.namedview.center[0], self.svg.namedview.center[1]) + group = etree.SubElement(parent, 'g', { 'transform':t}) + self.createPiano(group) + self.createMarkersFromIntervals(group, self.options.intervals) + +if __name__ == '__main__': + PianoScale().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/playing_cards/meta.json b/extensions/fablabchemnitz/playing_cards/meta.json new file mode 100644 index 0000000..c769673 --- /dev/null +++ b/extensions/fablabchemnitz/playing_cards/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Playing Cards", + "id": "fablabchemnitz.de.playing_cards", + "path": "playing_cards", + "dependent_extensions": null, + "original_name": "Playing Cards", + "original_id": "elam.playingcards", + "license": "GNU GPL v3", + "license_url": "https://github.com/DerElam/inkscape-extension-playing-cards/blob/master/LICENSE", + "comment": "", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/playing_cards", + "fork_url": "https://github.com/DerElam/inkscape-extension-playing-cards", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Playing+Cards", + "inkscape_gallery_url": null, + "main_authors": [ + "github.com/DerElam", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/playing_cards/playing_cards.inx b/extensions/fablabchemnitz/playing_cards/playing_cards.inx new file mode 100755 index 0000000..f95e728 --- /dev/null +++ b/extensions/fablabchemnitz/playing_cards/playing_cards.inx @@ -0,0 +1,131 @@ + + + Playing Cards + fablabchemnitz.de.playing_cards + + + + + + 2.5 + + + + + + + + + + 3.5 + + + + + + + + + + 1 + + + + + + + + + + + + 0 + + + + + + + + + + 5 + + + + + + + + + + 1 + + + + + + + + + + 5 + + + + + + + + + + 0 + + + + + + + + + + + + + + + + true + + + + 5 + + + + + + + + + true + true + true + true + true + false + true + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/playing_cards/playing_cards.py b/extensions/fablabchemnitz/playing_cards/playing_cards.py new file mode 100755 index 0000000..2642442 --- /dev/null +++ b/extensions/fablabchemnitz/playing_cards/playing_cards.py @@ -0,0 +1,862 @@ +from math import ceil, floor +from lxml import etree +import re +import inkex + + +# Constants +# ========= + + +# A unit is represented as a conversion factor relative to the pixel unit. The +# keys must be identical to the optiongroup options defined in the .inx file. +UNITS = { + "px": 1.0, + "pt": 96.0 / 72.0, + "in": 96.0, + "cm": 96.0 / 2.54, + "mm": 96.0 / 25.4 +} + +# EPSILON is used as a threshold by the rounding functions +EPSILON = 1e-3 + +# FOLD_LINE_TYPES defines the accepted values for horizontal and vertical +# fold lines that can be set on the command line. +NO_FOLD_LINE = "NoFoldLine" +HORIZONTAL_FOLD_LINE = "HorizontalFoldLine" +VERTICAL_FOLD_LINE = "VerticalFoldLine" +FOLD_LINE_TYPES = [NO_FOLD_LINE, HORIZONTAL_FOLD_LINE, VERTICAL_FOLD_LINE] + + +# Functions that change positions in some way +# =========================================== + + +def round_up(value, grid_size): + """ + Return the smallest grid point that is greater or equal to the value. + + :type value: float + :type grid_size: float + :rtype: float + """ + try: + return ceil(value / grid_size - EPSILON) * grid_size + except ZeroDivisionError: + return value + + +def round_down(value, grid_size): + """ + Return the greatest grid point that is less or equal to the value. + + :type value: float + :type grid_size: float + :rtype: float + """ + try: + return floor(value / grid_size + EPSILON) * grid_size + except ZeroDivisionError: + return value + + +def mirror_at(value, at): + """ + Reflect the value at a given point. + + :type value: float + :type at: float + :rtype: float + """ + return 2.0 * at - value + + +# Functions related to quantities and units +# ========================================= + + +def convert_unit(source_unit, target_unit): + """ + Returns a factor that converts from one unit to another. + + :type source_unit: str | float + :type target_unit: str | float + :rtype: float + """ + + # If the units are the same the conversion factor is obviously 1 + if source_unit == target_unit: + return 1.0 + + # If the unit is given as a float nothing needs to be done. Otherwise we + # try to find the unit and its float value in the dictionary of valid + # units. + if not isinstance(source_unit, float): + if source_unit not in UNITS.keys(): + raise ValueError("unexpected unit \"" + source_unit + "\"") + source_unit = UNITS[source_unit] + if not isinstance(target_unit, float): + if target_unit not in UNITS.keys(): + raise ValueError("unexpected unit \"" + target_unit + "\"") + target_unit = UNITS[target_unit] + + return source_unit / target_unit + + +def make_quantity(magnitude, unit): + """ + Create a quantity from a magnitude and a unit. + + :type magnitude: float + :type unit: str + """ + return "{0}{1}".format(magnitude, unit) + + +def split_quantity(quantity): + """ + Split a quantity into its magnitude and unit and return them as a tuple. + + :type quantity: str + :rtype: (float, str) | (float, NoneType) + """ + + # Matches a floating point number optionally followed by letters. The + # floating point number is the magnitude and the letters are the unit. + pattern = re.compile(r'(?P[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?)' + r'(?P[a-zA-Z]+)?') + match = re.match(pattern, quantity) + if match: + return (float(match.group("magnitude")), match.group("unit")) + else: + return (0, None) + + +def convert_quantity(quantity, target_unit): + """ + Convert the unit of a quantity to another unit. + + :type quantity: str + :type taget_unit: str | float + :rtype: str + """ + return "{0}{1}".format(convert_magnitude(quantity, target_unit), target_unit) + + +def convert_magnitude(quantity, target_unit): + """ + Convert the unit of a quantity to another unit and return only the new magnitude. + + :type quantity: str + :type target_unit: str | float + :rtype: float + """ + magnitude, source_unit = split_quantity(quantity) + new_magnitude = magnitude * convert_unit(source_unit, target_unit) + return new_magnitude + + +# Functions related to the placement of cards +# =========================================== + + +def calculate_positions_without_fold_line(page_size, margin_size, card_size, + bleed_size, grid_size, min_spacing, + grid_aligned): + """ + Position cards along one direction of the page without a fold line. + + The calculated positions are the positions of the right edges or bottom + edges of the cards. The other edges and the positions of the bleeds can be + easily derived by adding card_size, -bleed_size, and card_size+bleed_size. + + All sizes and spacings must be given as magnitudes, i.e. without units. + Their units are assumed to be identical but can be arbitrary. + + :param page_size: The width or height of the page. + :type page_size: float + :param margin_size: The empty margin of the page. Nothing will be placed in + the margin except for the frame. + :type margin_size: float + :param card_size: The width or height of each card. + :type card_size: float + :param bleed_size: The bleed around each card. This can be zero. + :type bleed_size: float + :param grid_size: The size of the alignment grid. The value is ignored if + grid_aligned is False. + :type grid_size: float + :param min_spacing: The minimum distance between two cards. + :type min_spacing: float + :param grid_aligned: Whether or not the beginning of a card should be on a + grid point. + :type grid_aligned: bool + :return: A list containing the beginnings of each card + :rtype: [float] + """ + # The bleed of the first card begins where the page margin ends. The card + # is then moved to the next grid point if grid_aligned is True. + card_begin = margin_size + bleed_size + if grid_aligned: + card_begin = round_up(card_begin, grid_size) + card_end = card_begin + card_size + + # There are to bleeds between the end of the first card and the beginning + # of the next. The spacing between two cards is two bleeds or min_spacing, + # whichever is greater. If grid_aligned is True the next card is moved even + # farther away so that it begins at the next grid point. + spacing = max(min_spacing, 2.0 * bleed_size) + if grid_aligned: + spacing = round_up(card_end + spacing, grid_size) - card_end + + # We add cards and spacings until we run out of enough empty space. + cards = [] + remaining = 0 + while True: + card_end = card_begin + card_size + next_remaining = page_size - margin_size - card_end - bleed_size + if next_remaining < 0: + break + remaining = next_remaining + cards.append(card_begin) + card_begin = card_end + spacing + + # Shift everything towards the center of the page. + shift = remaining / 2.0 + if grid_aligned: + shift = round_down(shift, grid_size) + cards = [card + shift for card in cards] + + return cards + + +def calculate_positions_with_fold_line(page_size, margin_size, card_size, + bleed_size, grid_size, min_spacing, + min_fold_line_spacing, grid_aligned): + """ + Position the cards along one direction of the page with a central fold line. + + The calculated positions are the positions of the right edges or bottom + edges of the cards. The other edges and the positions of the bleeds can be + easily derived by adding card_size, -bleed_size, and card_size+bleed_size. + + All sizes and spacings must be given as magnitudes, i.e. without units. + Their units are assumed to be identical but can be arbitrary. + + :param page_size: The width or height of the page. + :type page_size: float + :param margin_size: The empty margin of the page. Nothing will be placed in + the margin. + :type margin_size: float + :param card_size: The width or height of each card. + :type card_size: float + :param bleed_size: The bleed around each card. This can be zero. + :type bleed_size: float + :param grid_size: The size of the alignment grid. The value is ignored if + grid_aligned is False. + :type grid_size: float + :param min_spacing: The minimum distance between two cards. + :type min_spacing: float + :param min_fold_line_spacing: The minimum distance between a card and the + fold line. + :type min_fold_line_spacing: float + :param grid_aligned: Whether or not the beginning of a card should be on a + grid point. + :type grid_aligned: bool + :return: A tuple with a list containing the beginnings of each card and the + position of the fold line. + :rtype: ([float], float) + """ + # The spacing between the two central cards at the fold line must be at + # at least 2*bleed_size or 2*min_fold_line_spacing or min_spacing, + # whichever is the greatest. + central_spacing = max(2.0 * min_fold_line_spacing, + max(min_spacing, 2.0 * bleed_size)) + + # First we assume that the fold line is at the center of the page. This + # might change a bit later if we want grid alignment. We then place the + # first card before the fold line so that there is an empty space of + # central_spacing/2 between the card and the fold line. + card_begin = (page_size - central_spacing) / 2.0 - card_size + if grid_aligned: + card_begin = round_down(card_begin, grid_size) + card_end = card_begin + card_size + + # The card on the other side can be placed by mirroring the first card at + # the fold line. But this card is not neccessarily grid aligned. We fix that + # by increasing the central spacing so that the first card on the other side + # of the fold line is also grid aligned. + if grid_aligned: + central_spacing = round_up( + card_end + central_spacing, grid_size) - card_end + + # The fold line should not be at the center of the page but in the middle + # between the two central cards. If we don't use grid alignment then this + # is also the center of the page. + fold_line = card_end + central_spacing / 2.0 + + # The spacing between all remaining cards might be different because we + # don't use min_fold_line_spacing. But the calculation remains the same as + # for the two central cards. + spacing = max(min_spacing, 2.0 * bleed_size) + if grid_aligned: + spacing = round_up(card_end + spacing, grid_size) - card_end + + # Now that we have calculated all spacings we start adding cards to both + # sides of the fold line beginning at the center and moving outwards. + cards = [] + while True: + if card_begin < margin_size: + break + cards.append(card_begin) + cards.append(mirror_at(card_end, fold_line)) + card_begin -= card_size + spacing + card_end = card_begin + card_size + + # We sort the positions of the cards so that the positions start with the + # lowest and end with the highest value. + cards.sort() + + return (cards, fold_line) + + +class PlayingCardsExtension(inkex.EffectExtension): + """ + Implements the interface for Inkscape addons. + + An instance of this class is created in main(). __init__() sets up the + OptionParser provided by the base class to recognize all needed command + line parameters. Then in main() inkex.Effect.run() is called which then + parses the command line and calls effect(). This is where we do our work. + """ + + # Constants passed from the command line + PAGE_WIDTH = None + PAGE_HEIGHT = None + CARD_WIDTH = None + CARD_HEIGHT = None + BLEED_SIZE = None + MIN_CARD_SPACING = None + CROP_MARK_SPACING = None + MIN_FOLD_LINE_SPACING = None + PAGE_MARGIN = None + GRID_SIZE = None + ALIGN_TO_GRID = None + FOLD_LINE_TYPE = None + FRAME_SPACING = None + DRAW_GUIDES = None + DRAW_CARDS = None + DRAW_BLEEDS = None + DRAW_CROP_LINES = None + DRAW_FOLD_LINE = None + DRAW_PAGE_MARGIN = None + DRAW_FRAME = None + + USER_UNIT = None # The unit used in the document + horizontal_card_positions = None # Calculated horizontal positions + vertical_card_positions = None # Calculated vertical positions + fold_line_position = None # Calculated position of the fold line + + def add_arguments(self, pars): + """ + Initialize the OptionParser with recognized parameters. + + The option names must be identical to those defined in the .inx file. + The option values are later used to initialize the class constants. + """ + pars.add_argument("--pageName", type=str) + + pars.add_argument("--cardWidth", type=float) + pars.add_argument("--cardWidthUnit", choices=UNITS.keys()) + + pars.add_argument("--cardHeight", type=float) + pars.add_argument("--cardHeightUnit", choices=UNITS.keys()) + + pars.add_argument("--bleedSize", type=float, action="store") + pars.add_argument("--bleedSizeUnit", choices=UNITS.keys()) + + pars.add_argument("--minCardSpacing", type=float) + pars.add_argument("--minCardSpacingUnit", choices=UNITS.keys()) + + pars.add_argument("--cropMarkSpacing", type=float) + pars.add_argument("--cropMarkSpacingUnit", choices=UNITS.keys()) + + pars.add_argument("--minFoldLineSpacing", type=float) + pars.add_argument("--minFoldLineSpacingUnit", choices=UNITS.keys()) + + pars.add_argument("--pageMargin", type=float) + pars.add_argument("--pageMarginUnit", choices=UNITS.keys()) + + pars.add_argument("--frameSpacing", type=float) + pars.add_argument("--frameSpacingUnit", choices=UNITS.keys()) + + pars.add_argument("--gridSize", type=float) + pars.add_argument("--gridSizeUnit", choices=UNITS.keys()) + + pars.add_argument("--gridAligned", type=inkex.Boolean) + pars.add_argument("--foldLineType", choices=FOLD_LINE_TYPES) + pars.add_argument("--drawGuides", type=inkex.Boolean) + pars.add_argument("--drawCards", type=inkex.Boolean) + pars.add_argument("--drawBleeds", type=inkex.Boolean) + pars.add_argument("--drawCropLines", type=inkex.Boolean) + pars.add_argument("--drawFoldLine", type=inkex.Boolean) + pars.add_argument("--drawPageMargin", type=inkex.Boolean) + pars.add_argument("--drawFrame", type=inkex.Boolean) + + def init_user_unit(self): + """ + Determine the user unit from the document contents. + """ + root = self.document.getroot() + view_box = root.get("viewBox") + + # If the document has a valid viewBox we try to derive the user unit + # from that. + valid_view_box = view_box and len(view_box.split()) == 4 + if valid_view_box: + view_box = root.get("viewBox").split() + view_box_width, view_box_width_unit = split_quantity(view_box[2]) + # If the viewBox has a unit use that. + if view_box_width_unit: + self.USER_UNIT = view_box_width_unit + # If the viewBox has no unit derive the unit from the ratio between + # the document width and the viewBox width. + else: + document_width, document_width_unit = split_quantity( + self.document_width()) + self.USER_UNIT = document_width / view_box_width + if document_width_unit: + self.USER_UNIT *= UNITS[document_width_unit] + # If the document has no valid viewBox we try to derive the user unit + # from the document width. + else: + document_width, document_width_unit = split_quantity( + self.document_width()) + if document_width_unit: + self.USER_UNIT = UNITS[document_width_unit] + else: + # This might be problematic because v0.91 uses 90dpi and v0.92 + # uses 96dpi + self.USER_UNIT = UNITS["px"] + + def init_constants(self): + """ + Initialize the class constants from the OptionParser values and the + document contents. + + This converts all quantities from the unit given on the command line to + the user unit. + """ + + self.PAGE_WIDTH = self.to_user_unit(self.document_width()) + self.PAGE_HEIGHT = self.to_user_unit(self.document_height()) + self.CARD_WIDTH = self.to_user_unit( + make_quantity(self.options.cardWidth, + self.options.cardWidthUnit)) + self.CARD_HEIGHT = self.to_user_unit( + make_quantity(self.options.cardHeight, + self.options.cardHeightUnit)) + self.BLEED_SIZE = self.to_user_unit( + make_quantity(self.options.bleedSize, + self.options.bleedSizeUnit)) + self.GRID_SIZE = self.to_user_unit( + make_quantity(self.options.gridSize, + self.options.gridSizeUnit)) + self.MIN_CARD_SPACING = self.to_user_unit( + make_quantity(self.options.minCardSpacing, + self.options.minCardSpacingUnit)) + self.CROP_MARK_SPACING = self.to_user_unit( + make_quantity(self.options.cropMarkSpacing, + self.options.cropMarkSpacingUnit)) + self.MIN_FOLD_LINE_SPACING = self.to_user_unit( + make_quantity(self.options.minFoldLineSpacing, + self.options.minFoldLineSpacingUnit)) + self.PAGE_MARGIN = self.to_user_unit( + make_quantity(self.options.pageMargin, + self.options.pageMarginUnit)) + self.FRAME_SPACING = self.to_user_unit( + make_quantity(self.options.frameSpacing, + self.options.frameSpacingUnit)) + self.ALIGN_TO_GRID = self.options.gridAligned + self.FOLD_LINE_TYPE = self.options.foldLineType + self.DRAW_GUIDES = self.options.drawGuides + self.DRAW_CARDS = self.options.drawCards + self.DRAW_BLEEDS = self.options.drawBleeds + self.DRAW_CROP_LINES = self.options.drawCropLines + self.DRAW_FOLD_LINE = self.options.drawFoldLine + self.DRAW_PAGE_MARGIN = self.options.drawPageMargin + self.DRAW_FRAME = self.options.drawFrame + + def effect(self): + self.init_user_unit() + self.init_constants() + + self.calculate_positions() + + # Create one layer for the things that we want to print and another + # layer for things that we don't want to print but are useful while + # working on the cards. + non_printing_layer = self.create_layer("(template) non printing") + printing_layer = self.create_layer("(template) printing") + + if self.DRAW_GUIDES: + self.create_guides() + + if self.DRAW_CARDS: + self.create_cards(non_printing_layer) + + if self.DRAW_BLEEDS: + self.create_bleeds(non_printing_layer) + + if self.DRAW_CROP_LINES: + self.create_crop_lines(printing_layer) + + if self.DRAW_FOLD_LINE: + self.create_fold_line(printing_layer) + + if self.DRAW_PAGE_MARGIN: + self.create_margin(non_printing_layer) + + if self.DRAW_FRAME: + self.create_frame(printing_layer) + + def to_user_unit(self, quantity): + """ + Convert a quantity to the user unit and return its magnitude. + + :type quantity: str + :rtype: float + """ + return convert_magnitude(quantity, self.USER_UNIT) + + def document_width(self): + """ + Return the document width. + + The width is read from the document. It may or may not contain a unit. + """ + return self.document.getroot().get("width") + + def document_height(self): + """ + Return the document height. + + The height is read from the document. It may or may not contain a unit. + """ + return self.document.getroot().get("height") + + def calculate_positions(self): + """ + Calculate the horizontal and vertical positions of all cards. + + The results are stored in self.horizontal_card_positions, + self.vertical_card_positions, and self.fold_line_position. + """ + if self.FOLD_LINE_TYPE == VERTICAL_FOLD_LINE: + self.horizontal_card_positions, self.fold_line_position = \ + calculate_positions_with_fold_line( + self.PAGE_WIDTH, + self.PAGE_MARGIN, + self.CARD_WIDTH, + self.BLEED_SIZE, + self.GRID_SIZE, + self.MIN_CARD_SPACING, + self.MIN_FOLD_LINE_SPACING, + self.ALIGN_TO_GRID) + else: + self.horizontal_card_positions = \ + calculate_positions_without_fold_line( + self.PAGE_WIDTH, + self.PAGE_MARGIN, + self.CARD_WIDTH, + self.BLEED_SIZE, + self.GRID_SIZE, + self.MIN_CARD_SPACING, + self.ALIGN_TO_GRID) + + if self.FOLD_LINE_TYPE == HORIZONTAL_FOLD_LINE: + self.vertical_card_positions, self.fold_line_position = \ + calculate_positions_with_fold_line( + self.PAGE_HEIGHT, + self.PAGE_MARGIN, + self.CARD_HEIGHT, + self.BLEED_SIZE, + self.GRID_SIZE, + self.MIN_CARD_SPACING, + self.MIN_FOLD_LINE_SPACING, + self.ALIGN_TO_GRID) + else: + self.vertical_card_positions = \ + calculate_positions_without_fold_line( + self.PAGE_HEIGHT, + self.PAGE_MARGIN, + self.CARD_HEIGHT, + self.BLEED_SIZE, + self.GRID_SIZE, + self.MIN_CARD_SPACING, + self.ALIGN_TO_GRID) + + # Functions related to the structure of the document + # ================================================== + + def create_group(self, parent, label): + """ + Create a new group in the svg document. + + :type parent: lxml.etree._Element + :type label: str + :rtype: lxml.etree._Element + """ + group = etree.SubElement(parent, "g") + group.set(inkex.addNS("label", "inkscape"), label) + return group + + def create_layer(self, label, is_visible=True, is_locked=True): + """ + Create a new layer in the svg document. + + :type label: str + :rtype: lxml.etree._Element + """ + layer = self.create_group(self.document.getroot(), label) + layer.set(inkex.addNS("groupmode", "inkscape"), "layer") + # The Inkscape y-axis runs from bottom to top, the SVG y-axis runs from + # top to bottom. Therefore we need to transform all y coordinates. + layer.set( + "transform", "matrix(1 0 0 -1 0 {0})".format(self.PAGE_HEIGHT)) + + # Don't show the layer contents + if not is_visible: + layer.set("style", "display:none") + + # Lock the layer + if is_locked: + layer.set(inkex.addNS("insensitive", "sodipodi"), "true") + + return layer + + # Functions related to the contents of the document + # ================================================= + + def create_guide(self, x, y, orientation): + """ + Create an arbitrary guide. + + :type x: float + :type y: float + :type orientation: str + """ + view = self.document.getroot().find(inkex.addNS("namedview", "sodipodi")) + guide = etree.SubElement(view, inkex.addNS("guide", "sodipodi")) + guide.set("orientation", orientation) + guide.set("position", "{0},{1}".format(x, y)) + + def create_horizontal_guide(self, y): + """ + Create a horizontal guide. + """ + self.create_guide(0, y, "0,1") + + def create_vertical_guide(self, x): + """ + Create a vertical guide. + """ + self.create_guide(x, 0, "1,0") + + def create_guides(self): + """ + Create guides at all sides of all cards and bleeds. + """ + for x in self.horizontal_card_positions: + self.create_vertical_guide(x) + self.create_vertical_guide(x + self.CARD_WIDTH) + if self.BLEED_SIZE > 0: + self.create_vertical_guide(x - self.BLEED_SIZE) + self.create_vertical_guide(x + self.CARD_WIDTH + self.BLEED_SIZE) + + for y in self.vertical_card_positions: + self.create_horizontal_guide(y) + self.create_horizontal_guide(y + self.CARD_HEIGHT) + if self.BLEED_SIZE > 0: + self.create_horizontal_guide(y - self.BLEED_SIZE) + self.create_horizontal_guide(y + self.CARD_HEIGHT + self.BLEED_SIZE) + + def create_bleeds(self, parent): + """ + Creates a rectangle for each bleed. + + :type parent: lxml.etree._Element + """ + if self.BLEED_SIZE <= 0: + return + + attributes = {"x": str(-self.BLEED_SIZE), + "y": str(-self.BLEED_SIZE), + "width": str(self.CARD_WIDTH + 2.0 * self.BLEED_SIZE), + "height": str(self.CARD_HEIGHT + 2.0 * self.BLEED_SIZE), + "stroke": "black", + "stroke-width": str(self.to_user_unit("0.25pt")), + "fill": "none"} + + for y in self.vertical_card_positions: + for x in self.horizontal_card_positions: + attributes["transform"] = "translate({0},{1})".format(x, y) + etree.SubElement(parent, "rect", attributes) + + def create_cards(self, parent): + """ + Create a rectangle for each card. + + :type parent: lxml.etree._Element + """ + attributes = {"x": str(0), + "y": str(0), + "width": str(self.CARD_WIDTH), + "height": str(self.CARD_HEIGHT), + "stroke": "black", + "stroke-width": str(self.to_user_unit("0.25pt")), + "fill": "none"} + + for y in self.vertical_card_positions: + for x in self.horizontal_card_positions: + attributes["transform"] = "translate({0},{1})".format(x, y) + etree.SubElement(parent, "rect", attributes) + + def create_fold_line(self, parent): + """ + Create a horizontal or vertical fold line. + + :type parent: lxml.etree._Element + """ + attributes = {"stroke": "black", + "stroke-width": str(self.to_user_unit("0.25pt")), + "fill": "none"} + + if self.FOLD_LINE_TYPE == HORIZONTAL_FOLD_LINE: + attributes["d"] = "M 0,{0} H {1}".format( + self.fold_line_position, self.PAGE_WIDTH) + elif self.FOLD_LINE_TYPE == VERTICAL_FOLD_LINE: + attributes["d"] = "M {0},0 V {1}".format( + self.fold_line_position, self.PAGE_HEIGHT) + else: + return + + etree.SubElement(parent, "path", attributes) + + def create_crop_lines(self, parent): + """ + Create horizontal and vertical crop lines. + + :type parent: lxml.etree._Element + """ + attributes = {"stroke": "black", + "stroke-width": str(self.to_user_unit("0.25pt")), + "fill": "none"} + + # (begin, end) pairs for vertical crop line between bleeds + pairs = [] + begin = 0 + for y in self.vertical_card_positions: + end = y - self.BLEED_SIZE - self.CROP_MARK_SPACING + # Only add lines if they fit between two bleeds + if end - begin >= EPSILON: + pairs.append((begin, end)) + begin = end + self.CARD_HEIGHT \ + + 2.0 * self.BLEED_SIZE \ + + 2.0 * self.CROP_MARK_SPACING + pairs.append((begin, self.PAGE_HEIGHT)) + + # One crop line consists of many short strokes + attributes["d"] = " ".join(["M 0,{0} 0,{1}".format(begin, end) + for (begin, end) in pairs]) + + # Shifted copies of the crop line + for x in self.horizontal_card_positions: + attributes["transform"] = "translate({0},0)".format(x) + etree.SubElement(parent, "path", attributes) + attributes["transform"] = "translate({0},0)".format( + x + self.CARD_WIDTH) + etree.SubElement(parent, "path", attributes) + + # (begin, end) pairs for horizontal crop line between bleeds + pairs = [] + begin = 0 + for x in self.horizontal_card_positions: + end = x - self.BLEED_SIZE - self.CROP_MARK_SPACING + # Only add lines if they fit between two bleeds + if end - begin >= EPSILON: + pairs.append((begin, end)) + begin = end + self.CARD_WIDTH \ + + 2.0 * self.BLEED_SIZE \ + + 2.0 * self.CROP_MARK_SPACING + pairs.append((begin, self.PAGE_WIDTH)) + + # One crop line consists of many short strokes + attributes["d"] = " ".join(["M {0},0 {1},0".format(begin, end) + for (begin, end) in pairs]) + + # Shifted copies of the crop line + for y in self.vertical_card_positions: + attributes["transform"] = "translate(0,{0})".format(y) + etree.SubElement(parent, "path", attributes) + attributes["transform"] = "translate(0,{0})".format( + y + self.CARD_HEIGHT) + etree.SubElement(parent, "path", attributes) + + def create_margin(self, parent): + """ + Create a rectangle for the page margin. + + :type parent: lxml.etree._Element + """ + if self.PAGE_MARGIN <= 0: + return + + attributes = {"x": str(self.PAGE_MARGIN), + "y": str(self.PAGE_MARGIN), + "width": str(self.PAGE_WIDTH - 2.0 * self.PAGE_MARGIN), + "height": str(self.PAGE_HEIGHT - 2.0 * self.PAGE_MARGIN), + "stroke": "black", + "stroke-width": str(self.to_user_unit("0.25pt")), + "stroke-dasharray": "0.5,0.5", + "fill": "none"} + + etree.SubElement(parent, "rect", attributes) + + def create_frame(self, parent): + """ + Create a frame around the cards. + """ + + # If we don't have any cards we can't draw a frame around them + if len(self.horizontal_card_positions) == 0 or \ + len(self.vertical_card_positions) == 0: + return + + XMIN = min(self.horizontal_card_positions) + XMAX = max(self.horizontal_card_positions) + YMIN = min(self.vertical_card_positions) + YMAX = max(self.vertical_card_positions) + + LEFT = XMIN - self.FRAME_SPACING + BOTTOM = YMIN - self.FRAME_SPACING + WIDTH = XMAX - XMIN + self.CARD_WIDTH + 2 * self.FRAME_SPACING + HEIGHT = YMAX - YMIN + self.CARD_HEIGHT + 2 * self.FRAME_SPACING + + attributes = {"x": str(LEFT), + "y": str(BOTTOM), + "width": str(WIDTH), + "height": str(HEIGHT), + "stroke": "black", + "stroke-width": str(self.to_user_unit("0.25pt")), + "fill": "none"} + + etree.SubElement(parent, "rect", attributes) + + +def main(): + PlayingCardsExtension().run() + + +if __name__ == '__main__': + main() diff --git a/extensions/fablabchemnitz/polyhedra/meta.json b/extensions/fablabchemnitz/polyhedra/meta.json new file mode 100644 index 0000000..3915e08 --- /dev/null +++ b/extensions/fablabchemnitz/polyhedra/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Polyhedra", + "id": "fablabchemnitz.de.polyhedra", + "path": "polyhedra", + "dependent_extensions": null, + "original_name": "", + "original_id": "net.fabhub.polyhedra.", + "license": "GNU GPL v2", + "license_url": "https://storage.googleapis.com/google-code-archive-source/v2/code.google.com/jmantonfablab/source-archive.zip", + "comment": "ported to Inkscape v1 by Mario Voigt", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/polyhedra", + "fork_url": "http://www.fabhub.net/entry.php?27-Polyhedra-addiction", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Polyhedra", + "inkscape_gallery_url": null, + "main_authors": [ + "jmanton@illinois.edu", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/polyhedra/polyhedra.inx b/extensions/fablabchemnitz/polyhedra/polyhedra.inx new file mode 100644 index 0000000..09002b3 --- /dev/null +++ b/extensions/fablabchemnitz/polyhedra/polyhedra.inx @@ -0,0 +1,68 @@ + + + Polyhedra + fablabchemnitz.de.polyhedra + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 100.0 + + + + + + + + + + + + + + + 1.0 + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/polyhedra/polyhedra.py b/extensions/fablabchemnitz/polyhedra/polyhedra.py new file mode 100644 index 0000000..742cb52 --- /dev/null +++ b/extensions/fablabchemnitz/polyhedra/polyhedra.py @@ -0,0 +1,233 @@ +#!/usr/bin/env python3 +''' +Copyright (C) 2010 Jonathan Manton (jmanton @ illinois.edu) +Copyright (C) 2007 Aaron Spike (aaron @ ekips.org) +Copyright (C) 2007 Tavmjong Bah (tavmjong @ free.fr) + +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 program is loosely based on the gear extension for inkscape + +import inkex +from polyhedrondata import angleOverride, polyhedronData +from math import * +from lxml import etree + +def points_to_svgd(p): + f = p[0] + p = p[1:] + svgd = 'M%.3f,%.3f' % f + for x in p: + svgd += 'L%.3f,%.3f' % x + return svgd + +class Polyhedra(inkex.EffectExtension): + + no_tab = { 'cut' : ['m 0,0 100,0'], 'perf' : []} + + def add_arguments(self, pars): + pars.add_argument("-p", "--poly", default="Cube", help="Polygon net to render") + pars.add_argument("-s", "--size", type=float, default=100.0, help="Size of first edge") + pars.add_argument("-u", "--unit", default="mm", help="Units") + pars.add_argument("-m", "--material_thickness", type=float, default=1.0, help="Material thickness") + pars.add_argument("-t", "--tabs", type=int, default=0, help="Tab style") + + def get_tab(self, limitAngle): + return(self.get_connector('tab', limitAngle)) + + def get_slot(self, limitAngle): + return(self.get_connector('slot', limitAngle)) + + def get_slot_tab_style(self): + # these are designed to be 100px wide - they have to be scaled + + notab = {0 : self.no_tab} + simpletab = {0: {'cut': ['m 0,0 20,19 60,0 l 20,-19'], 'perf' : ['m 0,0 100,0']}} + # tab array - 0 = slot/tab, 1 = two tabs, 2 = single tab (alternate tab and nothing), 3 = none + mt = self.options.material_thickness + tabStyle = [{ \ + 50 : {'cut': ['m 0,0 35,0 0,{} -6,0 c 0,0 1,8 6,12 8,6 22,6 30,0 5,-4 6,-12 6,-12 l -6,0 0,{} 35,0'.format(mt,-mt)], 'perf' : ['M 42,0 58,0']}, \ + 24 : {'cut': ['m 0,0 55,0 0,{} -6,0 c 0,0 1,3 6,6 8,3 19,6 27,8 5,-4 9,-14 9,-14 l -6,0 0,{} 15,0'.format(mt,-mt)], 'perf' : ['M 62,0 78,0']}, \ + 18 : {'cut': ['m 0,0 65,0 0,{} -6,0 c 0,0 1,3 6,6 8,2 11,2 19,3 5,-4 9,-10 7,-9 l -6,0 0,{} 15,0'.format(mt,-mt)], 'perf' : ['m 72,0 6,0']}, \ + 6 : {'cut': ['m 0,0 70,0 {},7 10,1 5,-6 -2,0 0,{} 15,0'.format(mt,-mt)], 'perf' : ['m 74,0 7,0']}, + 0 : {'cut': ['m 0,0 100,0'], 'perf' : []}}, simpletab, simpletab, notab] + slotStyle = [{\ + 50 : {'cut': ['m 0,0 20,19 60,0 l 20,-19', 'm 28,{} 4,{} 36,0 4,{}'.format(-mt, mt, -(mt))], 'perf' : ['M 0,0 28,0','M 100,0 72,0']}, \ + 24 : {'cut': ['M 100,0 90,18 30,11 20,0 0,0', 'm 92,{} -4,{} -36,0 -4,{}'.format(-mt, mt, -(mt))], 'perf' : ['M 100,0 92,0', 'M 20,0 48,0']}, \ + 18 : {'cut': ['M 100,0 90,16 40,9 35,0 0,0', 'm 92,{} -4,{} -26,0 -4,{}'.format(-mt, mt, -(mt))], 'perf' : ['M 100,0 92,0', 'M 35,0 58,0']}, \ + 6 : {'cut': ['M 100,0 98,10 88,9 84,2 86,2 86,0 0,0'], 'perf' : ['m 96,0 -6,0']}, + 0 : {'cut': ['m 0,0 100,0'], 'perf' : [] }}, simpletab, notab, notab] + return [tabStyle, slotStyle] + + def get_connector(self, type, limitAngle): + if(self.options.tabs == 1): # two tabs + return(self.gen_tab(limitAngle/2)) + + if(self.options.tabs == 2): # one tab + if(type == 'tab'): + return(self.gen_tab(limitAngle)) + else: + return(self.no_tab) + + if(self.options.tabs == 3): # no tabs or slots + return(self.no_tab) + + # otherwise, get stuff from the array of specially modified tab/slots + + if(type == 'tab'): + source = self.get_slot_tab_style()[0] + else: + source = self.get_slot_tab_style()[1] + + cuttable = source[self.options.tabs].keys() + sorted(cuttable) # sorts in-place. Ugh. + reversed(cuttable) # in-place. Ugh. + for angle in cuttable: + if(limitAngle >= angle): + return(source[self.options.tabs][angle]) + + def gen_tab(self, theta_degrees): + theta = radians(min(theta_degrees,90)) + minOffset = 8 + tabAngle = radians(40) + minTabLength = 40 + tabHeight = 19 # must be >= minOffset + tabWidth = 100 + + # determine if we need to sqash down side of tab by limit angle + bX = minOffset * cos(theta) / sin(theta) + aX = bX - (minOffset * cos(tabAngle) / sin(tabAngle)) + if(theta >= tabAngle): + tab_cut = [(0,0), (tabHeight/sin(tabAngle) * cos(tabAngle), tabHeight)] + tab_perf = [(0,0), (tabWidth,0)] + else: + if (aX > (tabWidth - minTabLength)): # too stubby, don't put a tab + return(no_tab) + tab_cut = [(0,0), (aX,0), (bX, minOffset)] + tab_perf = [(aX,0), (tabWidth,0)] + + # now see if we also have to squash the other side of the tab - this happens + # for very small angles where the tab height on the far side would still be + # too high and would intersect with the other tab + + # first, find where the tab would intersect the limit angle if we just put + # out a line of infinite length at the tab angle. + bmult = tabWidth / (cos(theta) * sin(tabAngle) / sin(theta) + cos(tabAngle)) + tiY = bmult * sin(tabAngle) + + # now see if that intersection is too low. If so, limit the tab's height + tiY = min(tiY, tabHeight) + tab_cut.append((tabWidth - tiY / sin(tabAngle) * cos(tabAngle), tiY)) + tab_cut.append((tabWidth, 0)) + + return({'cut' : [points_to_svgd(tab_cut)], 'perf' : [points_to_svgd(tab_perf)]}) + + def effect(self): + + poly = self.options.poly + size = self.svg.unittouu(str(self.options.size) + self.options.unit) + + eC = polyhedronData[poly]['edgeCoordinates'] + iEI = polyhedronData[poly]['insideEdgeIndices'] + oEI = polyhedronData[poly]['outsideEdgeIndices'] + oED = polyhedronData[poly]['outsideEdgeDegrees'] + sidelen = sqrt((eC[oEI[0][0]-1][0] - eC[oEI[0][1]-1][0])**2 + (eC[oEI[0][0]-1][1] - eC[oEI[0][1]-1][1])**2) + scale = size / sidelen + + # Translate group, Rotate path. + t = 'translate(' + str( self.svg.namedview.center[0] ) + ',' + str( self.svg.namedview.center[1] ) + ')' + g_attribs = {inkex.addNS('label','inkscape'):'Polygon ' + str( poly ), 'transform':t } + g = etree.SubElement(self.svg.get_current_layer(), 'g', g_attribs) + + gsub_attribs = {inkex.addNS('label','inkscape'):'Polygon ' + str( poly ) + 'border' } + gsub = etree.SubElement(g, 'g', gsub_attribs) + + # Create SVG Path + cutStyle = { 'stroke': '#0000FF', 'stroke-width': self.svg.unittouu("1px"), 'fill': 'none' } + perfStyle = { 'stroke': '#FF0000', 'stroke-width': self.svg.unittouu("1px"), 'fill': 'none' } + textStyle = { + 'font-size': str( size/4 ), + 'font-family': 'arial', + 'text-anchor': 'middle', + 'text-align': 'center', + 'fill': '#222' + } + + numOEI = len(oEI) + for edgeIndex in range(numOEI): + eX1 = eC[oEI[edgeIndex][0]-1][0] + eY1 = eC[oEI[edgeIndex][0]-1][1] + eX2 = eC[oEI[edgeIndex][1]-1][0] + eY2 = eC[oEI[edgeIndex][1]-1][1] + + origin = (eX1 * scale, eY1 * scale) + edgelen = sqrt((eX1 - eX2)**2 + (eY1 - eY2)**2) + edgesize = size * (edgelen / sidelen) + + + prevAngle = (oED[(edgeIndex - 1) % numOEI] + 180 - oED[edgeIndex]) % 360 + nextAngle = (oED[edgeIndex] + 180 - oED[(edgeIndex + 1) % numOEI]) % 360 + if str(angleOverride) in poly: + if angleOverride[poly].has_key('prev'): + if angleOverride[poly]['prev'].has_key(edgeIndex): + prevAngle = angleOverride[poly]['prev'][edgeIndex] + if angleOverride[poly].has_key('next'): + if angleOverride[poly]['next'].has_key(edgeIndex): + nextAngle = angleOverride[poly]['next'][edgeIndex] + trans = 'translate(' + str(origin[0]) + ',' + str(origin[1]) + ') rotate(' + str(oED[edgeIndex] ) + ') scale(' + str(edgesize/100.0) + ')' + + limitAngle = min(prevAngle, nextAngle) + tab = self.get_tab(limitAngle) + slot = self.get_slot(limitAngle) + + # the "special" tabs are all skewed one way or the other, to + # make room for tight turns. This determines if it is the previous + # or next angle that is the tight one, and flips the tab or slot if + # needed. + + if(nextAngle == limitAngle): + trans += ' translate(100.0, 0) scale(-1,1)' + + tab_group_attribs = {inkex.addNS('label','inkscape'):'tab', 'transform': trans } + tab_group = etree.SubElement(gsub, 'g', tab_group_attribs) + midX = (eX2 - eX1)/2 + eX1 + midY = (eY2 - eY1)/2 + eY1 + text_attrib = {'style': str(inkex.Style(textStyle)), 'x' : str(midX * scale), 'y' : str(midY * scale) } + # etree.SubElement(gsub, 'text', text_attrib).text = str(edgeIndex) + if ((edgeIndex % 2) == 0): + edgetype = tab + else: + edgetype = slot + + for path in edgetype['cut']: + gear_attribs = {'style':str(inkex.Style(cutStyle)), 'd':path} + gear = etree.SubElement(tab_group, inkex.addNS('path','svg'), gear_attribs ) + + for path in edgetype['perf']: + gear_attribs = {'style':str(inkex.Style(perfStyle)), 'd':path} + gear = etree.SubElement(tab_group, inkex.addNS('path','svg'), gear_attribs ) + + gsub_attribs = {inkex.addNS('label','inkscape'):'Polygon ' + str( poly ) + 'inside' } + gsub = etree.SubElement(g, 'g', gsub_attribs) + + for edge in iEI: + points = [(eC[edge[0]-1][0] * scale, eC[edge[0]-1][1] * scale), (eC[edge[1]-1][0] * scale, eC[edge[1]-1][1] * scale)] + path = points_to_svgd( points ) + perf_attribs = {'style':str(inkex.Style(perfStyle)), 'd':path} + gear = etree.SubElement(gsub, inkex.addNS('path','svg'), perf_attribs ) + +if __name__ == '__main__': + Polyhedra().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/polyhedra/polyhedrondata.py b/extensions/fablabchemnitz/polyhedra/polyhedrondata.py new file mode 100644 index 0000000..248cdd8 --- /dev/null +++ b/extensions/fablabchemnitz/polyhedra/polyhedrondata.py @@ -0,0 +1,1429 @@ +#!/usr/bin/env python3 +# Copyright (C) 2010 Jonathan Manton (jmanton @ illinois.edu) + +# 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 + + +# net data produced with Mathematica using the notebook at https://github.com/vmario89/fablab-polyhedra/tree/master/autonets +# angle overrides manually produced, with help from debugging mode on net generation that also outputs the vertex number of the outer edges of the nets. + +angleOverride = {\ + 'SnubCube' : {'prev' : {19 : 24., 38 : 24.}, 'next' : {16 : 24., 35 : 24.}}, \ + 'SnubDodecahedron' : {'prev' : {10 : 24., 17 : 24., 21 : 24. , 28 : 24., 32: 24., 39: 24. , 43 : 24., 50 : 24., 54 : 24., 69 : 24., 76 : 24., 80 : 24., 87 : 24., 91 : 24., 98 : 24., 102 : 24., 109 : 24., 113 : 24.}, \ + 'next' : {7 : 24., 14 : 24., 18 : 24., 25 : 24. , 29 : 24., 36 : 24. , 40 : 24., 47 : 24., 51 : 24., 66 : 24., 73 : 24., 77 : 24., 84 : 24., 88 : 24., 95 : 24., 99 : 24., 106 : 24., 110 : 24.}},\ + 'GreatRhombicosidodecahedron' : {'prev' : {16: 18., 25: 18., 46: 18., 55: 18., 68: 18., 77: 18., 90: 18., 99: 18., 112: 18., 136: 18., 145: 18., 166: 18., 175: 18., 188: 18., 197: 18., 210: 18., 219: 18., 232: 18. }, \ + 'next' : {13 : 18., 22: 18., 43: 18., 52: 18., 65: 18., 74: 18., 87: 18., 96: 18., 109: 18., 133: 18., 142: 18., 163: 18., 172: 18., 185: 18., 194: 18., 207: 18., 216: 18., 229: 18.}}, \ + 'SmallRhombicosidodecahedron' : {'prev' : {10 : 24., 11 : 24., 24 : 24., 25 : 24., 35 : 24., 36 : 24., 46 : 24., 47: 24., 59 : 24., 73: 24., 74: 24., 84: 24., 85: 24., 95: 24., 96: 24., 106: 24., 107: 24. }, \ + 'next' : {6: 24. , 7 : 24., 20 : 24. , 21 : 24., 31 : 24., 32 : 24., 42 : 24., 43 : 24., 56 : 24., 69 : 24., 70: 24., 80: 24., 81: 24., 91:24., 92: 24., 102: 24., 103: 24. }}, \ + 'TruncatedIcosahedron' : {'prev' : {8 : 24., 12 : 24., 22: 24., 26: 24., 33: 24., 37: 24., 44: 24., 48: 24., 55: 24., 67: 24., 71: 24., 78: 24., 82: 24., 89: 24., 93: 24., 100: 24., 104: 24., 114: 24.}, \ + 'next' : {5 : 24., 9 : 24., 19: 24., 23: 24., 30: 24., 34: 24., 41: 24., 45: 24., 52: 24., 64: 24., 68: 24., 75: 24., 79: 24., 86: 24., 90: 24., 97: 24., 101: 24., 111: 24. }}, \ + 'DeltoidalHexecontahedron' : {'prev' : {6: 24., 44: 24., 56: 24., 68: 24., 80: 24., 92: 24., 104: 24., 116: 24.}, \ + 'next' : {3: 24., 41: 24., 53: 24., 65: 24., 77: 24., 89: 24., 101: 24., 113: 24.}}, \ + 'PentagonalIcositetrahedron' : {'prev' : {16: 24., 22: 24.}, \ + 'next' : {13: 24., 19: 24.}}, \ + 'PentagonalHexecontahedron' : {'prev' : {10: 12., 28: 12., 65: 12., 83: 12., 101: 12., 119: 12., 156: 12., 174: 12.}, \ + 'next' : {7: 12., 25: 12., 62: 12., 80: 12., 98: 12., 116: 12., 153: 12., 171: 12.}}, \ + 'PentakisDodecahedron' : {'prev' : {4: 18., 25: 18., 31: 18., 37: 18., 43: 18., 48: 18., 54: 18., 60: 18.}, \ + 'next' : {1: 18., 22: 18., 28: 18., 34: 18., 40: 18., 45: 18., 51: 18., 57: 18. }} \ +} + + +polyhedronData = \ +{'Cube' : {'edgeCoordinates' : [[0., 1.], [0., 2.], [1., 0.], [1., \ +1.], [1., 2.], [1., 3.], [2., 0.], [2., 1.], [2., 2.], [2., 3.], [3., \ +1.], [3., 2.], [4., 1.], [4., 2.]], 'outsideEdgeIndices' : [[1, 2], \ +[2, 5], [5, 6], [6, 10], [10, 9], [9, 12], [12, 14], [14, 13], [13, \ +11], [11, 8], [8, 7], [7, 3], [3, 4], [4, 1]], 'outsideEdgeDegrees' : \ +[90., 0., 90., 0., -90., 0., 0., -90., 180., 180., -90., 180., 90., \ +180.], 'insideEdgeIndices' : [[4, 5], [4, 8], [5, 9], [8, 9], [11, \ +12]]}, 'Dodecahedron' : {'edgeCoordinates' : [[2.11803, 0.], \ +[4.73607, 0.], [5.73607, 0.], [6.3541, 0.], [7.3541, 0.], [1.30902, \ +0.587785], [2.92705, 0.587785], [0.809017, 0.951057], [3.42705, \ +0.951057], [4.42705, 0.951057], [6.04508, 0.951057], [7.66312, \ +0.951057], [0., 1.53884], [1.61803, 1.53884], [2.61803, 1.53884], \ +[4.23607, 1.53884], [5.23607, 1.53884], [6.8541, 1.53884], [7.8541, \ +1.53884], [0.309017, 2.4899], [1.30902, 2.4899], [2.92705, 2.4899], \ +[3.92705, 2.4899], [5.54508, 2.4899], [6.54508, 2.4899], [8.16312, \ +2.4899], [0.5, 3.07768], [2.11803, 3.07768], [3.73607, 3.07768], \ +[4.73607, 3.07768], [7.3541, 3.07768], [5.23607, 3.44095], [6.8541, \ +3.44095], [0.809017, 4.02874], [1.80902, 4.02874], [2.42705, \ +4.02874], [3.42705, 4.02874], [6.04508, 4.02874]], \ +'outsideEdgeIndices' : [[13, 20], [20, 21], [21, 27], [27, 34], [34, \ +35], [35, 28], [28, 36], [36, 37], [37, 29], [29, 22], [22, 23], [23, \ +30], [30, 24], [24, 32], [32, 38], [38, 33], [33, 25], [25, 31], [31, \ +26], [26, 19], [19, 18], [18, 12], [12, 5], [5, 4], [4, 11], [11, 3], \ +[3, 2], [2, 10], [10, 17], [17, 16], [16, 9], [9, 15], [15, 7], [7, \ +1], [1, 6], [6, 14], [14, 8], [8, 13]], 'outsideEdgeDegrees' : [72., \ +0., 144., 72., 0., -72., 72., 0., -72., -144., 0., 36., -36., 108., \ +36., -36., -108., 36., -36., -108., 180., -36., -108., 180., 108., \ +-108., 180., 108., 36., 180., -144., 144., -72., -144., 144., 72., \ +-144., 144.], 'insideEdgeIndices' : [[11, 17], [11, 18], [14, 15], \ +[14, 21], [15, 22], [16, 23], [17, 24], [18, 25], [21, 28], [22, 28], \ +[24, 25]]}, 'Icosahedron' : {'edgeCoordinates' : [[1., 0.], [2., 0.], \ +[3., 0.], [4., 0.], [5., 0.], [0.5, 0.866025], [1.5, 0.866025], [2.5, \ +0.866025], [3.5, 0.866025], [4.5, 0.866025], [5.5, 0.866025], [0., \ +1.73205], [1., 1.73205], [2., 1.73205], [3., 1.73205], [4., 1.73205], \ +[5., 1.73205], [0.5, 2.59808], [1.5, 2.59808], [2.5, 2.59808], [3.5, \ +2.59808], [4.5, 2.59808]], 'outsideEdgeIndices' : [[12, 18], [18, \ +13], [13, 19], [19, 14], [14, 20], [20, 15], [15, 21], [21, 16], [16, \ +22], [22, 17], [17, 11], [11, 5], [5, 10], [10, 4], [4, 9], [9, 3], \ +[3, 8], [8, 2], [2, 7], [7, 1], [1, 6], [6, 12]], \ +'outsideEdgeDegrees' : [60., -60., 60., -60., 60., -60., 60., -60., \ +60., -60., -60., -120., 120., -120., 120., -120., 120., -120., 120., \ +-120., 120., 120.], 'insideEdgeIndices' : [[6, 7], [6, 13], [7, 8], \ +[7, 13], [7, 14], [8, 9], [8, 14], [8, 15], [9, 10], [9, 15], [9, \ +16], [10, 11], [10, 16], [10, 17], [12, 13], [13, 14], [14, 15], [15, \ +16], [16, 17]]}, 'Octahedron' : {'edgeCoordinates' : [[2., 0.], [0.5, \ +0.866025], [1.5, 0.866025], [2.5, 0.866025], [3.5, 0.866025], [0., \ +1.73205], [1., 1.73205], [2., 1.73205], [3., 1.73205], [1.5, \ +2.59808]], 'outsideEdgeIndices' : [[6, 7], [7, 10], [10, 8], [8, 9], \ +[9, 5], [5, 4], [4, 1], [1, 3], [3, 2], [2, 6]], 'outsideEdgeDegrees' \ +: [0., 60., -60., 0., -60., 180., -120., 120., 180., 120.], \ +'insideEdgeIndices' : [[2, 7], [3, 4], [3, 7], [3, 8], [4, 8], [4, \ +9], [7, 8]]}, 'Tetrahedron' : {'edgeCoordinates' : [[0., 0.], [0.5, \ +0.866025], [1., 0.], [1., 1.73205], [1.5, 0.866025], [2., 0.]], \ +'outsideEdgeIndices' : [[1, 2], [2, 4], [4, 5], [5, 6], [6, 3], [3, \ +1]], 'outsideEdgeDegrees' : [60., 60., -60., -60., 180., 180.], \ +'insideEdgeIndices' : [[2, 3], [2, 5], [3, 5]]}, 'Cuboctahedron' : \ +{'edgeCoordinates' : [[0., 1.86603], [0., 2.86603], [0.5, 1.], \ +[0.866025, 2.36603], [0.866025, 3.36603], [1.36603, 0.5], [1.36603, \ +1.5], [1.86603, 2.36603], [1.86603, 3.36603], [2.23205, 0.], \ +[2.23205, 1.], [2.73205, 1.86603], [2.73205, 2.86603], [3.23205, 0.], \ +[3.23205, 1.], [3.59808, 2.36603], [4.09808, 0.5], [4.09808, 1.5], \ +[4.59808, 2.36603], [4.9641, 1.], [5.4641, 1.86603], [5.9641, 1.]], \ +'outsideEdgeIndices' : [[1, 4], [4, 2], [2, 5], [5, 9], [9, 13], [13, \ +8], [8, 12], [12, 16], [16, 19], [19, 21], [21, 22], [22, 20], [20, \ +18], [18, 15], [15, 17], [17, 14], [14, 10], [10, 6], [6, 11], [11, \ +7], [7, 3], [3, 1]], 'outsideEdgeDegrees' : [30., 150., 30., 0., \ +-30., -150., -30., 30., 0., -30., -60., 180., 150., -150., -30., \ +-150., 180., 150., 30., 150., -150., 120.], 'insideEdgeIndices' : \ +[[4, 5], [4, 7], [4, 8], [7, 8], [8, 9], [10, 11], [11, 12], [11, \ +15], [12, 15], [14, 15], [16, 18], [18, 19], [20, 21]]}, \ +'GreatRhombicosidodecahedron' : {'edgeCoordinates' : [[0., 5.63692], \ +[0.309017, 4.68586], [0.309017, 6.58797], [0.618034, 8.04179], \ +[0.618034, 10.7738], [1.11803, 4.09808], [1.11803, 7.17576], \ +[1.11803, 8.90781], [1.11803, 9.90781], [1.11803, 11.6399], [2.11803, \ +3.44371], [2.11803, 4.09808], [2.11803, 7.17576], [2.11803, 8.90781], \ +[2.11803, 9.90781], [2.11803, 11.6399], [2.36603, 9.08063], [2.61803, \ +2.57768], [2.61803, 8.04179], [2.61803, 10.7738], [2.67504, 8.12957], \ +[2.67504, 10.0317], [2.92705, 4.68586], [2.92705, 6.58797], [2.98406, \ +3.94371], [2.98406, 6.67576], [2.98406, 12.1399], [3.23607, 5.63692], \ +[3.48406, 3.07768], [3.48406, 4.80973], [3.48406, 5.80973], [3.48406, \ +7.54179], [3.48406, 10.6195], [3.48406, 11.2738], [4.48406, 3.07768], \ +[4.48406, 4.80973], [4.48406, 5.80973], [4.48406, 7.54179], [4.48406, \ +10.6195], [4.73205, 5.63692], [4.73205, 13.1787], [4.98406, 3.94371], \ +[4.98406, 6.67576], [5.04107, 4.68586], [5.04107, 6.58797], [5.04107, \ +12.2276], [5.04107, 14.1298], [5.29308, 8.12957], [5.29308, 10.0317], \ +[5.35008, 8.04179], [5.35008, 10.7738], [5.60209, 9.08063], [5.85008, \ +4.09808], [5.85008, 7.17576], [5.85008, 8.90781], [5.85008, 9.90781], \ +[5.85008, 11.6399], [5.85008, 14.7175], [6.85008, 3.44371], [6.85008, \ +4.09808], [6.85008, 7.17576], [6.85008, 8.90781], [6.85008, 9.90781], \ +[6.85008, 11.6399], [6.85008, 14.7175], [7.09808, 9.08063], [7.35008, \ +2.57768], [7.35008, 8.04179], [7.35008, 10.7738], [7.40709, 8.12957], \ +[7.40709, 10.0317], [7.6591, 4.68586], [7.6591, 6.58797], [7.6591, \ +12.2276], [7.6591, 14.1298], [7.71611, 3.94371], [7.71611, 6.67576], \ +[7.71611, 12.1399], [7.96812, 5.63692], [7.96812, 13.1787], [8.21611, \ +3.07768], [8.21611, 4.80973], [8.21611, 5.80973], [8.21611, 7.54179], \ +[8.21611, 10.6195], [8.21611, 11.2738], [9.21611, 3.07768], [9.21611, \ +4.80973], [9.21611, 5.80973], [9.21611, 7.54179], [9.21611, 10.6195], \ +[9.4641, 5.63692], [9.71611, 3.94371], [9.71611, 6.67576], [9.77312, \ +4.68586], [9.77312, 6.58797], [10.0251, 8.12957], [10.0251, 10.0317], \ +[10.0821, 8.04179], [10.0821, 10.7738], [10.3341, 9.08063], [10.5821, \ +4.09808], [10.5821, 7.17576], [10.5821, 8.90781], [10.5821, 9.90781], \ +[10.5821, 11.6399], [11.5821, 3.44371], [11.5821, 4.09808], [11.5821, \ +7.17576], [11.5821, 8.90781], [11.5821, 9.90781], [11.5821, 11.6399], \ +[11.8301, 9.08063], [12.0821, 2.57768], [12.0821, 8.04179], [12.0821, \ +10.7738], [12.1391, 8.12957], [12.1391, 10.0317], [12.3912, 4.68586], \ +[12.3912, 6.58797], [12.4482, 3.94371], [12.4482, 6.67576], [12.4482, \ +12.1399], [12.7002, 5.63692], [12.9482, 3.07768], [12.9482, 4.80973], \ +[12.9482, 5.80973], [12.9482, 7.54179], [12.9482, 10.6195], [12.9482, \ +11.2738], [13.9482, 3.07768], [13.9482, 4.80973], [13.9482, 5.80973], \ +[13.9482, 7.54179], [13.9482, 10.6195], [14.1962, 5.63692], [14.4482, \ +3.94371], [14.4482, 6.67576], [14.5052, 4.68586], [14.5052, 6.58797], \ +[14.7572, 8.12957], [14.7572, 10.0317], [14.8142, 8.04179], [14.8142, \ +10.7738], [15.0662, 9.08063], [15.3142, 4.09808], [15.3142, 7.17576], \ +[15.3142, 8.90781], [15.3142, 9.90781], [15.3142, 11.6399], [16.3142, \ +3.44371], [16.3142, 4.09808], [16.3142, 7.17576], [16.3142, 8.90781], \ +[16.3142, 9.90781], [16.3142, 11.6399], [16.5622, 1.53884], [16.5622, \ +9.08063], [16.8142, 2.57768], [16.8142, 8.04179], [16.8142, 10.7738], \ +[16.8712, 0.587785], [16.8712, 2.4899], [16.8712, 8.12957], [16.8712, \ +10.0317], [17.1232, 4.68586], [17.1232, 6.58797], [17.1802, 3.94371], \ +[17.1802, 6.67576], [17.1802, 12.1399], [17.4322, 5.63692], [17.6802, \ +0.], [17.6802, 3.07768], [17.6802, 4.80973], [17.6802, 5.80973], \ +[17.6802, 7.54179], [17.6802, 10.6195], [17.6802, 11.2738], [18.6802, \ +0.], [18.6802, 3.07768], [18.6802, 4.80973], [18.6802, 5.80973], \ +[18.6802, 7.54179], [18.6802, 10.6195], [18.9282, 5.63692], [19.1802, \ +3.94371], [19.1802, 6.67576], [19.2372, 4.68586], [19.2372, 6.58797], \ +[19.4892, 0.587785], [19.4892, 2.4899], [19.4892, 8.12957], [19.4892, \ +10.0317], [19.5462, 8.04179], [19.5462, 10.7738], [19.7982, 1.53884], \ +[19.7982, 9.08063], [20.0462, 4.09808], [20.0462, 7.17576], [20.0462, \ +8.90781], [20.0462, 9.90781], [20.0462, 11.6399], [21.0462, 3.44371], \ +[21.0462, 4.09808], [21.0462, 7.17576], [21.0462, 8.90781], [21.0462, \ +9.90781], [21.0462, 11.6399], [21.2942, 9.08063], [21.5462, 2.57768], \ +[21.5462, 8.04179], [21.5462, 10.7738], [21.6032, 8.12957], [21.6032, \ +10.0317], [21.8553, 4.68586], [21.8553, 6.58797], [21.9123, 3.94371], \ +[21.9123, 6.67576], [21.9123, 12.1399], [22.1643, 5.63692], [22.4123, \ +3.07768], [22.4123, 4.80973], [22.4123, 5.80973], [22.4123, 7.54179], \ +[22.4123, 10.6195], [22.4123, 11.2738], [23.4123, 3.07768], [23.4123, \ +4.80973], [23.4123, 5.80973], [23.4123, 7.54179], [23.4123, 10.6195], \ +[23.9123, 3.94371], [23.9123, 6.67576], [24.2213, 8.12957], [24.2213, \ +10.0317], [24.2783, 8.04179], [24.5303, 9.08063], [24.7783, \ +7.17576]], 'outsideEdgeIndices' : [[1, 3], [3, 7], [7, 4], [4, 8], \ +[8, 9], [9, 5], [5, 10], [10, 16], [16, 27], [27, 34], [34, 20], [20, \ +15], [15, 14], [14, 19], [19, 32], [32, 21], [21, 17], [17, 22], [22, \ +33], [33, 39], [39, 49], [49, 52], [52, 48], [48, 38], [38, 50], [50, \ +55], [55, 56], [56, 51], [51, 57], [57, 46], [46, 41], [41, 47], [47, \ +58], [58, 65], [65, 75], [75, 80], [80, 74], [74, 64], [64, 78], [78, \ +86], [86, 69], [69, 63], [63, 62], [62, 68], [68, 84], [84, 70], [70, \ +66], [66, 71], [71, 85], [85, 91], [91, 98], [98, 101], [101, 97], \ +[97, 90], [90, 99], [99, 104], [104, 105], [105, 100], [100, 106], \ +[106, 112], [112, 123], [123, 130], [130, 116], [116, 111], [111, \ +110], [110, 115], [115, 128], [128, 117], [117, 113], [113, 118], \ +[118, 129], [129, 135], [135, 142], [142, 145], [145, 141], [141, \ +134], [134, 143], [143, 148], [148, 149], [149, 144], [144, 150], \ +[150, 156], [156, 170], [170, 178], [178, 161], [161, 155], [155, \ +154], [154, 160], [160, 176], [176, 164], [164, 158], [158, 165], \ +[165, 177], [177, 184], [184, 193], [193, 197], [197, 192], [192, \ +183], [183, 194], [194, 200], [200, 201], [201, 195], [195, 202], \ +[202, 208], [208, 219], [219, 226], [226, 212], [212, 207], [207, \ +206], [206, 211], [211, 224], [224, 213], [213, 209], [209, 214], \ +[214, 225], [225, 231], [231, 235], [235, 237], [237, 234], [234, \ +230], [230, 236], [236, 238], [238, 233], [233, 229], [229, 228], \ +[228, 232], [232, 227], [227, 221], [221, 210], [210, 203], [203, \ +217], [217, 222], [222, 223], [223, 218], [218, 205], [205, 216], \ +[216, 220], [220, 215], [215, 204], [204, 198], [198, 188], [188, \ +185], [185, 189], [189, 199], [199, 187], [187, 182], [182, 181], \ +[181, 186], [186, 180], [180, 191], [191, 196], [196, 190], [190, \ +179], [179, 172], [172, 162], [162, 157], [157, 163], [163, 173], \ +[173, 159], [159, 151], [151, 168], [168, 174], [174, 175], [175, \ +169], [169, 153], [153, 167], [167, 171], [171, 166], [166, 152], \ +[152, 146], [146, 139], [139, 136], [136, 140], [140, 147], [147, \ +138], [138, 133], [133, 132], [132, 137], [137, 131], [131, 125], \ +[125, 114], [114, 107], [107, 121], [121, 126], [126, 127], [127, \ +122], [122, 109], [109, 120], [120, 124], [124, 119], [119, 108], \ +[108, 102], [102, 95], [95, 92], [92, 96], [96, 103], [103, 94], [94, \ +89], [89, 88], [88, 93], [93, 87], [87, 81], [81, 67], [67, 59], [59, \ +76], [76, 82], [82, 83], [83, 77], [77, 61], [61, 73], [73, 79], [79, \ +72], [72, 60], [60, 53], [53, 44], [44, 40], [40, 45], [45, 54], [54, \ +43], [43, 37], [37, 36], [36, 42], [42, 35], [35, 29], [29, 18], [18, \ +11], [11, 25], [25, 30], [30, 31], [31, 26], [26, 13], [13, 24], [24, \ +28], [28, 23], [23, 12], [12, 6], [6, 2], [2, 1]], \ +'outsideEdgeDegrees' : [72., 36., 120., 60., 90., 120., 60., 0., 30., \ +-60., -150., -120., -90., -60., -30., 144., 108., 72., 36., 0., -36., \ +-72., -108., -144., 30., 60., 90., 120., 60., 144., 108., 72., 36., \ +0., -36., -72., -108., -144., 30., -60., -150., -120., -90., -60., \ +-30., 144., 108., 72., 36., 0., -36., -72., -108., -144., 30., 60., \ +90., 120., 60., 0., 30., -60., -150., -120., -90., -60., -30., 144., \ +108., 72., 36., 0., -36., -72., -108., -144., 30., 60., 90., 120., \ +60., 0., 30., -60., -150., -120., -90., -60., -30., 144., 108., 72., \ +36., 0., -36., -72., -108., -144., 30., 60., 90., 120., 60., 0., 30., \ +-60., -150., -120., -90., -60., -30., 144., 108., 72., 36., 0., -36., \ +-72., -108., -144., 30., -60., -150., -120., -90., -60., -120., 180., \ +-150., 120., 30., 60., 90., 120., 150., -36., -72., -108., -144., \ +180., 144., 108., 72., 36., -150., -120., -90., -60., -120., -36., \ +-72., -108., -144., 180., 144., 108., 72., 36., -150., 120., 30., \ +60., 90., 120., 150., -36., -72., -108., -144., 180., 144., 108., \ +72., 36., -150., -120., -90., -60., -120., 180., -150., 120., 30., \ +60., 90., 120., 150., -36., -72., -108., -144., 180., 144., 108., \ +72., 36., -150., -120., -90., -60., -120., 180., -150., 120., 30., \ +60., 90., 120., 150., -36., -72., -108., -144., 180., 144., 108., \ +72., 36., -150., -120., -90., -60., -120., 180., -150., 120., 30., \ +60., 90., 120., 150., -36., -72., -108., -144., 180., 144., 108.], \ +'insideEdgeIndices' : [[7, 13], [8, 14], [9, 15], [13, 19], [16, 20], \ +[25, 29], [26, 32], [30, 36], [31, 37], [32, 38], [38, 43], [50, 54], \ +[54, 61], [55, 62], [56, 63], [57, 64], [61, 68], [64, 69], [76, 81], \ +[77, 84], [82, 88], [83, 89], [84, 90], [90, 94], [99, 103], [103, \ +109], [104, 110], [105, 111], [109, 115], [112, 116], [121, 125], \ +[122, 128], [126, 132], [127, 133], [128, 134], [134, 138], [143, \ +147], [147, 153], [148, 154], [149, 155], [153, 160], [156, 161], \ +[168, 173], [169, 176], [173, 180], [174, 181], [175, 182], [176, \ +183], [183, 187], [194, 199], [199, 205], [200, 206], [201, 207], \ +[205, 211], [208, 212], [217, 221], [218, 224], [222, 228], [223, \ +229], [224, 230], [230, 233]]}, 'GreatRhombicuboctahedron' : \ +{'edgeCoordinates' : [[0., 4.12132], [0., 5.12132], [0.707107, \ +2.41421], [0.707107, 3.41421], [0.707107, 5.82843], [0.707107, \ +6.82843], [1.70711, 2.41421], [1.70711, 3.41421], [1.70711, 5.82843], \ +[1.70711, 6.82843], [1.91421, 3.25529], [1.91421, 5.98735], [2.41421, \ +2.38927], [2.41421, 4.12132], [2.41421, 5.12132], [2.41421, 6.85337], \ +[3.41421, 0.707107], [3.41421, 1.70711], [3.41421, 2.38927], \ +[3.41421, 4.12132], [3.41421, 5.12132], [3.41421, 6.85337], [3.41421, \ +7.53553], [3.41421, 8.53553], [3.91421, 3.25529], [3.91421, 5.98735], \ +[4.12132, 0.], [4.12132, 2.41421], [4.12132, 3.41421], [4.12132, \ +5.82843], [4.12132, 6.82843], [4.12132, 9.24264], [5.12132, 0.], \ +[5.12132, 2.41421], [5.12132, 3.41421], [5.12132, 5.82843], [5.12132, \ +6.82843], [5.12132, 9.24264], [5.32843, 3.25529], [5.32843, 5.98735], \ +[5.82843, 0.707107], [5.82843, 1.70711], [5.82843, 2.38927], \ +[5.82843, 4.12132], [5.82843, 5.12132], [5.82843, 6.85337], [5.82843, \ +7.53553], [5.82843, 8.53553], [6.82843, 2.38927], [6.82843, 4.12132], \ +[6.82843, 5.12132], [6.82843, 6.85337], [7.32843, 3.25529], [7.32843, \ +5.98735], [7.53553, 2.41421], [7.53553, 3.41421], [7.53553, 5.82843], \ +[7.53553, 6.82843], [8.53553, 2.41421], [8.53553, 3.41421], [8.53553, \ +5.82843], [8.53553, 6.82843], [8.74264, 3.25529], [8.74264, 5.98735], \ +[9.24264, 2.38927], [9.24264, 4.12132], [9.24264, 5.12132], [9.24264, \ +6.85337], [10.2426, 2.38927], [10.2426, 4.12132], [10.2426, 5.12132], \ +[10.2426, 6.85337], [10.7426, 3.25529], [10.7426, 5.98735], [10.9497, \ +2.41421], [10.9497, 3.41421], [10.9497, 5.82843], [10.9497, 6.82843], \ +[11.9497, 2.41421], [11.9497, 3.41421], [11.9497, 5.82843], [11.9497, \ +6.82843], [12.1569, 3.25529], [12.1569, 5.98735], [12.6569, 2.38927], \ +[12.6569, 4.12132], [12.6569, 5.12132], [12.6569, 6.85337], [13.6569, \ +2.38927], [13.6569, 4.12132], [13.6569, 5.12132], [13.6569, 6.85337], \ +[14.1569, 3.25529], [14.1569, 5.98735]], 'outsideEdgeIndices' : [[1, \ +2], [2, 5], [5, 6], [6, 10], [10, 9], [9, 15], [15, 12], [12, 16], \ +[16, 22], [22, 26], [26, 21], [21, 30], [30, 31], [31, 23], [23, 24], \ +[24, 32], [32, 38], [38, 48], [48, 47], [47, 37], [37, 36], [36, 45], \ +[45, 40], [40, 46], [46, 52], [52, 54], [54, 51], [51, 57], [57, 58], \ +[58, 62], [62, 61], [61, 67], [67, 64], [64, 68], [68, 72], [72, 74], \ +[74, 71], [71, 77], [77, 78], [78, 82], [82, 81], [81, 87], [87, 84], \ +[84, 88], [88, 92], [92, 94], [94, 91], [91, 90], [90, 93], [93, 89], \ +[89, 85], [85, 83], [83, 86], [86, 80], [80, 79], [79, 75], [75, 76], \ +[76, 70], [70, 73], [73, 69], [69, 65], [65, 63], [63, 66], [66, 60], \ +[60, 59], [59, 55], [55, 56], [56, 50], [50, 53], [53, 49], [49, 43], \ +[43, 39], [39, 44], [44, 35], [35, 34], [34, 42], [42, 41], [41, 33], \ +[33, 27], [27, 17], [17, 18], [18, 28], [28, 29], [29, 20], [20, 25], \ +[25, 19], [19, 13], [13, 11], [11, 14], [14, 8], [8, 7], [7, 3], [3, \ +4], [4, 1]], 'outsideEdgeDegrees' : [90., 45., 90., 0., -90., -45., \ +120., 60., 0., -60., -120., 45., 90., 135., 90., 45., 0., -45., -90., \ +-135., -90., -45., 120., 60., 0., -60., -120., 45., 90., 0., -90., \ +-45., 120., 60., 0., -60., -120., 45., 90., 0., -90., -45., 120., \ +60., 0., -60., -120., -90., -60., -120., 180., 120., 60., -135., \ +-90., 180., 90., 135., -60., -120., 180., 120., 60., -135., -90., \ +180., 90., 135., -60., -120., 180., 120., 60., -135., -90., -45., \ +-90., -135., 180., 135., 90., 45., 90., 135., -60., -120., 180., \ +120., 60., -135., -90., 180., 90., 135.], 'insideEdgeIndices' : [[4, \ +8], [5, 9], [14, 15], [14, 20], [15, 21], [20, 21], [28, 34], [29, \ +35], [30, 36], [31, 37], [44, 45], [44, 50], [45, 51], [50, 51], [56, \ +60], [57, 61], [66, 67], [66, 70], [67, 71], [70, 71], [76, 80], [77, \ +81], [86, 87], [86, 90], [87, 91]]}, 'Icosidodecahedron' : \ +{'edgeCoordinates' : [[0., 2.20071], [0.379874, 3.36984], [0.587785, \ +1.39169], [0.78661, 4.28339], [0.994522, 2.30524], [1.12302, \ +2.70071], [1.33093, 0.722562], [1.3744, 5.0924], [1.78113, 4.17886], \ +[1.98904, 2.20071], [1.98904, 3.20071], [2.19696, 1.22256], [2.19696, \ +4.17886], [2.32545, 0.], [2.53336, 0.978148], [2.60369, 5.0924], \ +[2.9401, 1.89169], [2.9401, 3.50973], [3.27651, 0.309017], [3.52789, \ +2.70071], [3.59821, 4.98787], [3.80613, 4.00973], [3.93462, 1.78716], \ +[3.93462, 3.61426], [4.14253, 0.809017], [4.27103, 2.03158], \ +[4.54927, 4.67886], [4.92914, 3.50973], [5.13706, 1.53158], [5.13706, \ +2.53158], [5.54379, 1.61803], [5.7517, 4.42327], [5.8802, 3.20071], \ +[6.15844, 0.553432], [6.15844, 3.50973], [6.49485, 5.0924], [6.53831, \ +1.72256], [6.74623, 1.36245], [6.74623, 2.70071], [7.15296, \ +0.448903], [7.15296, 3.61426], [7.36087, 4.5924], [7.36087, 5.5924], \ +[7.48937, 2.03158], [7.74075, 4.42327], [8.14748, 0.553432], \ +[8.14748, 3.50973], [8.3554, 1.53158], [8.3554, 2.53158], [8.48389, \ +5.0924], [8.76213, 1.61803], [9.09854, 3.20071], [9.14201, 3.61426], \ +[9.34992, 0.809017], [9.34992, 4.5924], [9.75665, 1.72256], [9.96457, \ +2.70071], [10.0931, 3.92327]], 'outsideEdgeIndices' : [[1, 5], [5, \ +10], [10, 6], [6, 2], [2, 4], [4, 8], [8, 9], [9, 11], [11, 13], [13, \ +16], [16, 21], [21, 27], [27, 22], [22, 18], [18, 24], [24, 28], [28, \ +33], [33, 39], [39, 35], [35, 32], [32, 36], [36, 43], [43, 42], [42, \ +41], [41, 45], [45, 50], [50, 55], [55, 58], [58, 53], [53, 47], [47, \ +52], [52, 57], [57, 56], [56, 54], [54, 51], [51, 49], [49, 48], [48, \ +46], [46, 40], [40, 34], [34, 38], [38, 44], [44, 37], [37, 31], [31, \ +30], [30, 29], [29, 26], [26, 20], [20, 23], [23, 25], [25, 19], [19, \ +14], [14, 15], [15, 17], [17, 12], [12, 7], [7, 3], [3, 1]], \ +'outsideEdgeDegrees' : [6., -6., 150., 138., 66., 54., -66., -78., \ +78., 66., -6., -18., -138., -150., 6., -6., -18., -30., 126., 114., \ +42., 30., -90., -102., 54., 42., -30., -42., -162., -174., -18., \ +-30., -102., -114., 126., 114., -90., -102., -174., 174., 54., 42., \ +-162., -174., 114., -90., 150., 138., -66., -78., -150., -162., 78., \ +66., -138., -150., 138., 126.], 'insideEdgeIndices' : [[3, 5], [4, \ +9], [6, 11], [10, 11], [10, 12], [10, 17], [11, 18], [13, 18], [15, \ +19], [17, 20], [17, 23], [18, 20], [20, 24], [21, 22], [26, 30], [28, \ +30], [30, 33], [35, 41], [36, 42], [37, 39], [38, 40], [39, 41], [39, \ +44], [41, 47], [44, 48], [44, 49], [45, 47], [47, 49], [49, 52], [51, \ +56], [53, 55]]}, 'SmallRhombicosidodecahedron' : {'edgeCoordinates' : \ +[[0., 5.31473], [0.172816, 2.4487], [0.172816, 3.4487], [0.587785, \ +4.50571], [0.587785, 6.12374], [0.672816, 1.58268], [0.672816, \ +4.31473], [0.672816, 6.31473], [1.17282, 2.4487], [1.17282, 3.4487], \ +[1.17282, 7.18075], [1.53884, 1.08268], [1.53884, 4.81473], [1.53884, \ +5.81473], [2.03884, 1.9487], [2.03884, 3.9487], [2.03884, 6.68075], \ +[2.12387, 2.13968], [2.12387, 3.75772], [2.53884, 4.81473], [2.53884, \ +5.81473], [2.71166, 2.9487], [2.73205, 5.31473], [2.90487, 2.4487], \ +[2.90487, 3.4487], [3.31984, 4.50571], [3.31984, 6.12374], [3.40487, \ +1.58268], [3.40487, 4.31473], [3.40487, 6.31473], [3.90487, 2.4487], \ +[3.90487, 3.4487], [3.90487, 7.18075], [4.11278, 8.1589], [4.27089, \ +1.08268], [4.27089, 4.81473], [4.27089, 5.81473], [4.77089, 1.9487], \ +[4.77089, 3.9487], [4.77089, 6.68075], [4.85592, 2.13968], [4.85592, \ +3.75772], [5.1073, 8.26343], [5.27089, 4.81473], [5.27089, 5.81473], \ +[5.44371, 2.9487], [5.4641, 5.31473], [5.51404, 7.34988], [5.63692, \ +2.4487], [5.63692, 3.4487], [6.05189, 4.50571], [6.05189, 6.12374], \ +[6.13692, 1.58268], [6.13692, 4.31473], [6.13692, 6.31473], [6.63692, \ +2.4487], [6.63692, 3.4487], [6.63692, 7.18075], [7.00294, 1.08268], \ +[7.00294, 4.81473], [7.00294, 5.81473], [7.50294, 1.9487], [7.50294, \ +3.9487], [7.50294, 6.68075], [7.58797, 2.13968], [7.58797, 3.75772], \ +[8.00294, 4.81473], [8.00294, 5.81473], [8.17576, 2.9487], [8.19615, \ +5.31473], [8.36897, 2.4487], [8.36897, 3.4487], [8.78394, 4.50571], \ +[8.78394, 6.12374], [8.86897, 1.58268], [8.86897, 4.31473], [8.86897, \ +6.31473], [9.36897, 2.4487], [9.36897, 3.4487], [9.36897, 7.18075], \ +[9.73499, 1.08268], [9.73499, 4.81473], [9.73499, 5.81473], [10.235, \ +1.9487], [10.235, 3.9487], [10.235, 6.68075], [10.32, 2.13968], \ +[10.32, 3.75772], [10.735, 4.81473], [10.735, 5.81473], [10.8579, \ +0.913545], [10.9078, 2.9487], [10.9282, 5.31473], [11.101, 2.4487], \ +[11.101, 3.4487], [11.2646, 0.], [11.516, 4.50571], [11.516, \ +6.12374], [11.601, 1.58268], [11.601, 4.31473], [11.601, 6.31473], \ +[12.101, 2.4487], [12.101, 3.4487], [12.101, 7.18075], [12.2591, \ +0.104528], [12.467, 1.08268], [12.467, 4.81473], [12.467, 5.81473], \ +[12.967, 1.9487], [12.967, 3.9487], [12.967, 6.68075], [13.0521, \ +2.13968], [13.0521, 3.75772], [13.467, 4.81473], [13.467, 5.81473], \ +[13.6399, 2.9487], [13.8331, 3.4487], [14.3331, 4.31473]], \ +'outsideEdgeIndices' : [[1, 5], [5, 14], [14, 8], [8, 11], [11, 17], \ +[17, 21], [21, 20], [20, 29], [29, 36], [36, 26], [26, 23], [23, 27], \ +[27, 37], [37, 30], [30, 33], [33, 34], [34, 43], [43, 48], [48, 40], \ +[40, 45], [45, 44], [44, 54], [54, 60], [60, 51], [51, 47], [47, 52], \ +[52, 61], [61, 55], [55, 58], [58, 64], [64, 68], [68, 67], [67, 76], \ +[76, 82], [82, 73], [73, 70], [70, 74], [74, 83], [83, 77], [77, 80], \ +[80, 86], [86, 90], [90, 89], [89, 100], [100, 107], [107, 97], [97, \ +93], [93, 98], [98, 108], [108, 101], [101, 104], [104, 111], [111, \ +115], [115, 114], [114, 118], [118, 117], [117, 110], [110, 103], \ +[103, 113], [113, 116], [116, 112], [112, 102], [102, 109], [109, \ +106], [106, 105], [105, 96], [96, 91], [91, 99], [99, 94], [94, 95], \ +[95, 85], [85, 79], [79, 88], [88, 92], [92, 87], [87, 78], [78, 84], \ +[84, 81], [81, 75], [75, 71], [71, 72], [72, 63], [63, 57], [57, 66], \ +[66, 69], [69, 65], [65, 56], [56, 62], [62, 59], [59, 53], [53, 49], \ +[49, 50], [50, 39], [39, 32], [32, 42], [42, 46], [46, 41], [41, 31], \ +[31, 38], [38, 35], [35, 28], [28, 24], [24, 25], [25, 16], [16, 10], \ +[10, 19], [19, 22], [22, 18], [18, 9], [9, 15], [15, 12], [12, 6], \ +[6, 2], [2, 3], [3, 7], [7, 13], [13, 4], [4, 1]], \ +'outsideEdgeDegrees' : [54., -18., 150., 60., -30., -60., -90., -30., \ +30., -162., 126., 54., -18., 150., 60., 78., 6., -66., -138., -60., \ +-90., -30., 30., -162., 126., 54., -18., 150., 60., -30., -60., -90., \ +-30., 30., -162., 126., 54., -18., 150., 60., -30., -60., -90., -30., \ +30., -162., 126., 54., -18., 150., 60., -30., -60., -90., -30., \ +-120., 150., -150., 18., -54., -126., 162., -30., -120., -102., \ +-174., 114., 42., 120., 90., 150., -150., 18., -54., -126., 162., \ +-30., -120., 150., 120., 90., 150., -150., 18., -54., -126., 162., \ +-30., -120., 150., 120., 90., 150., -150., 18., -54., -126., 162., \ +-30., -120., 150., 120., 90., 150., -150., 18., -54., -126., 162., \ +-30., -120., 150., 120., 90., 60., 30., -162., 126.], \ +'insideEdgeIndices' : [[2, 9], [3, 10], [6, 9], [7, 10], [9, 10], \ +[13, 14], [13, 16], [13, 20], [14, 17], [14, 21], [16, 20], [24, 31], \ +[25, 29], [25, 32], [28, 31], [29, 32], [31, 32], [33, 40], [36, 37], \ +[36, 39], [36, 44], [37, 40], [37, 45], [39, 44], [49, 56], [50, 54], \ +[50, 57], [53, 56], [54, 57], [56, 57], [60, 61], [60, 63], [60, 67], \ +[61, 64], [61, 68], [63, 67], [71, 78], [72, 76], [72, 79], [75, 78], \ +[76, 79], [78, 79], [82, 83], [82, 85], [82, 89], [83, 86], [83, 90], \ +[85, 89], [94, 102], [95, 100], [95, 103], [99, 102], [99, 106], \ +[100, 103], [102, 103], [107, 108], [107, 110], [107, 114], [108, \ +111], [108, 115], [110, 114]]}, 'SmallRhombicuboctahedron' : \ +{'edgeCoordinates' : [[0., 1.], [0., 2.], [0., 3.], [0., 4.], [1., \ +1.], [1., 2.], [1., 3.], [1., 4.], [1.5, 1.13397], [1.5, 3.86603], \ +[2., 0.], [2., 1.], [2., 2.], [2., 3.], [2., 4.], [2., 5.], [3., 0.], \ +[3., 1.], [3., 2.], [3., 3.], [3., 4.], [3., 5.], [3.5, 1.13397], \ +[3.5, 3.86603], [4., 1.], [4., 2.], [4., 3.], [4., 4.], [5., 1.], \ +[5., 2.], [5., 3.], [5., 4.], [5.5, 1.13397], [5.5, 3.86603], [6., \ +1.], [6., 2.], [6., 3.], [6., 4.], [7., 1.], [7., 2.], [7., 3.], [7., \ +4.], [7.5, 1.13397], [7.5, 3.86603], [8., 2.], [8., 3.]], \ +'outsideEdgeIndices' : [[1, 2], [2, 3], [3, 4], [4, 8], [8, 7], [7, \ +10], [10, 14], [14, 15], [15, 16], [16, 22], [22, 21], [21, 20], [20, \ +24], [24, 27], [27, 28], [28, 32], [32, 31], [31, 34], [34, 37], [37, \ +38], [38, 42], [42, 41], [41, 44], [44, 46], [46, 45], [45, 43], [43, \ +40], [40, 39], [39, 35], [35, 36], [36, 33], [33, 30], [30, 29], [29, \ +25], [25, 26], [26, 23], [23, 19], [19, 18], [18, 17], [17, 11], [11, \ +12], [12, 13], [13, 9], [9, 6], [6, 5], [5, 1]], 'outsideEdgeDegrees' \ +: [90., 90., 90., 0., -90., 60., -60., 90., 90., 0., -90., -90., 60., \ +-60., 90., 0., -90., 60., -60., 90., 0., -90., 60., -60., -90., \ +-120., 120., -90., 180., 90., -120., 120., -90., 180., 90., -120., \ +120., -90., -90., 180., 90., 90., -120., 120., -90., 180.], \ +'insideEdgeIndices' : [[2, 6], [3, 7], [6, 7], [6, 13], [7, 14], [12, \ +18], [13, 14], [13, 19], [14, 20], [15, 21], [19, 20], [19, 26], [20, \ +27], [26, 27], [26, 30], [27, 31], [30, 31], [30, 36], [31, 37], [36, \ +37], [36, 40], [37, 41], [40, 41], [40, 45], [41, 46]]}, 'SnubCube' : \ +{'edgeCoordinates' : [[0., 2.23205], [0., 3.23205], [0.866025, \ +1.73205], [0.866025, 2.73205], [1.36603, 0.866025], [1.36603, \ +3.59808], [1.86603, 1.73205], [1.86603, 2.73205], [1.86603, 3.73205], \ +[2.73205, 1.23205], [2.73205, 2.23205], [2.73205, 3.23205], [3.23205, \ +1.36603], [3.23205, 4.09808], [3.23205, 5.09808], [3.73205, 2.23205], \ +[3.73205, 3.23205], [3.73205, 4.23205], [4.09808, 4.59808], [4.09808, \ +5.59808], [4.23205, 1.36603], [4.59808, 1.73205], [4.59808, 2.73205], \ +[4.59808, 3.73205], [4.59808, 6.4641], [5.09808, 0.866025], [5.09808, \ +1.86603], [5.09808, 4.59808], [5.09808, 5.59808], [5.59808, 0.], \ +[5.59808, 2.73205], [5.59808, 3.73205], [5.59808, 4.73205], [5.9641, \ +5.09808], [6.09808, 0.866025], [6.09808, 1.86603], [6.4641, 2.23205], \ +[6.4641, 3.23205], [6.4641, 4.23205], [6.9641, 1.36603], [6.9641, \ +2.36603], [6.9641, 5.09808], [7.4641, 3.23205], [7.4641, 4.23205], \ +[8.33013, 2.73205], [8.33013, 3.73205]], 'outsideEdgeIndices' : [[1, \ +2], [2, 4], [4, 6], [6, 8], [8, 9], [9, 12], [12, 14], [14, 17], [17, \ +18], [18, 24], [24, 19], [19, 15], [15, 20], [20, 25], [25, 29], [29, \ +34], [34, 28], [28, 32], [32, 33], [33, 39], [39, 42], [42, 44], [44, \ +46], [46, 45], [45, 43], [43, 41], [41, 38], [38, 37], [37, 31], [31, \ +36], [36, 40], [40, 35], [35, 30], [30, 26], [26, 21], [21, 27], [27, \ +23], [23, 22], [22, 16], [16, 13], [13, 11], [11, 10], [10, 7], [7, \ +5], [5, 3], [3, 1]], 'outsideEdgeDegrees' : [90., -30., 60., -60., \ +90., -30., 60., -60., 90., -30., 120., 150., 30., 60., -60., -30., \ +-150., -60., 90., -30., 60., -60., -30., -90., 150., -120., 120., \ +-90., 150., -60., -30., -150., -120., 120., 150., 30., 120., -90., \ +150., -120., 120., -90., 150., -120., 120., 150.], \ +'insideEdgeIndices' : [[1, 4], [3, 4], [3, 7], [4, 8], [7, 8], [7, \ +11], [8, 11], [8, 12], [11, 12], [11, 16], [12, 17], [16, 17], [16, \ +23], [17, 23], [17, 24], [19, 20], [19, 28], [20, 29], [23, 24], [23, \ +31], [24, 28], [24, 32], [26, 27], [26, 35], [27, 31], [27, 36], [28, \ +29], [31, 32], [31, 38], [32, 38], [32, 39], [35, 36], [38, 39], [38, \ +43], [39, 44], [43, 44], [43, 46]]}, 'SnubDodecahedron' : \ +{'edgeCoordinates' : [[0., 8.41355], [0.406737, 7.5], [0.743145, \ +9.08268], [1.40126, 7.60453], [1.60917, 8.58268], [1.60917, 9.58268], \ +[2.4752, 7.08268], [2.4752, 8.08268], [2.4752, 9.08268], [2.4752, \ +10.0827], [2.4752, 11.0827], [2.59808, 8.91355], [3.00481, 8.], \ +[3.34122, 6.58268], [3.34122, 7.58268], [3.34122, 9.58268], [3.34122, \ +10.5827], [3.54913, 11.5608], [3.99933, 8.10453], [4.20725, 5.08268], \ +[4.20725, 6.08268], [4.20725, 7.08268], [4.20725, 8.08268], [4.20725, \ +9.08268], [4.20725, 10.0827], [4.33013, 6.91355], [4.54365, 11.6654], \ +[4.73686, 6.], [4.95039, 10.7518], [5.07327, 4.58268], [5.07327, \ +5.58268], [5.07327, 7.58268], [5.07327, 8.58268], [5.07327, 9.58268], \ +[5.07327, 10.5827], [5.07327, 11.5827], [5.28118, 9.56082], [5.73139, \ +6.10453], [5.9393, 3.08268], [5.9393, 4.08268], [5.9393, 5.08268], \ +[5.9393, 6.08268], [5.9393, 7.08268], [5.9393, 8.08268], [5.9393, \ +10.0827], [5.9393, 11.0827], [6.06218, 4.91355], [6.27571, 9.66535], \ +[6.46891, 4.], [6.68244, 8.75181], [6.80532, 2.58268], [6.80532, \ +3.58268], [6.80532, 5.58268], [6.80532, 6.58268], [6.80532, 7.58268], \ +[6.80532, 8.58268], [6.80532, 9.58268], [6.80532, 10.5827], [7.01323, \ +7.56082], [7.46344, 4.10453], [7.67135, 1.08268], [7.67135, 2.08268], \ +[7.67135, 3.08268], [7.67135, 4.08268], [7.67135, 5.08268], [7.67135, \ +6.08268], [7.67135, 8.08268], [7.67135, 9.08268], [7.79423, 2.91355], \ +[8.00776, 7.66535], [8.20097, 2.], [8.41449, 6.75181], [8.53737, \ +0.582676], [8.53737, 1.58268], [8.53737, 3.58268], [8.53737, \ +4.58268], [8.53737, 5.58268], [8.53737, 6.58268], [8.53737, 7.58268], \ +[8.53737, 8.58268], [8.74529, 5.56082], [9.19549, 2.10453], [9.4034, \ +0.0826761], [9.4034, 1.08268], [9.4034, 2.08268], [9.4034, 3.08268], \ +[9.4034, 4.08268], [9.4034, 6.08268], [9.4034, 7.08268], [9.52628, \ +0.913545], [9.73981, 5.66535], [9.93302, 0.], [10.1465, 4.75181], \ +[10.2694, 1.58268], [10.2694, 2.58268], [10.2694, 3.58268], [10.2694, \ +4.58268], [10.2694, 5.58268], [10.2694, 6.58268], [10.4773, 3.56082], \ +[10.9275, 0.104528], [11.1354, 1.08268], [11.1354, 2.08268], \ +[11.1354, 4.08268], [11.1354, 5.08268], [11.4719, 3.66535], [11.8786, \ +2.75181], [12.0015, 0.582676], [12.0015, 1.58268], [12.0015, \ +2.58268], [12.0015, 3.58268], [12.0015, 4.58268], [12.8675, 2.08268], \ +[12.8675, 3.08268], [13.0754, 4.06082], [13.7335, 2.58268], [14.0699, \ +4.16535], [14.4767, 3.25181]], 'outsideEdgeIndices' : [[1, 3], [3, \ +6], [6, 10], [10, 11], [11, 17], [17, 18], [18, 27], [27, 29], [29, \ +25], [25, 35], [35, 36], [36, 46], [46, 58], [58, 45], [45, 34], [34, \ +33], [33, 37], [37, 48], [48, 50], [50, 44], [44, 56], [56, 57], [57, \ +68], [68, 80], [80, 67], [67, 55], [55, 54], [54, 59], [59, 70], [70, \ +72], [72, 66], [66, 78], [78, 79], [79, 89], [89, 99], [99, 88], [88, \ +77], [77, 76], [76, 81], [81, 91], [91, 93], [93, 87], [87, 97], [97, \ +98], [98, 105], [105, 112], [112, 104], [104, 96], [96, 95], [95, \ +100], [100, 106], [106, 107], [107, 103], [103, 110], [110, 111], \ +[111, 114], [114, 115], [115, 117], [117, 118], [118, 116], [116, \ +113], [113, 109], [109, 108], [108, 102], [102, 101], [101, 92], [92, \ +90], [90, 94], [94, 84], [84, 83], [83, 73], [73, 61], [61, 74], [74, \ +85], [85, 86], [86, 82], [82, 71], [71, 69], [69, 75], [75, 63], [63, \ +62], [62, 51], [51, 39], [39, 52], [52, 64], [64, 65], [65, 60], [60, \ +49], [49, 47], [47, 53], [53, 41], [41, 40], [40, 30], [30, 20], [20, \ +31], [31, 42], [42, 43], [43, 38], [38, 28], [28, 26], [26, 32], [32, \ +22], [22, 21], [21, 14], [14, 7], [7, 15], [15, 23], [23, 24], [24, \ +19], [19, 13], [13, 12], [12, 16], [16, 9], [9, 8], [8, 5], [5, 4], \ +[4, 2], [2, 1]], 'outsideEdgeDegrees' : [42., 30., 30., 90., -30., \ +78., 6., -66., -138., 30., 90., -30., -30., -150., -150., -90., 78., \ +6., -66., -138., 30., 90., -30., -30., -150., -150., -90., 78., 6., \ +-66., -138., 30., 90., -30., -30., -150., -150., -90., 78., 6., -66., \ +-138., 30., 90., -30., -30., -150., -150., -90., 78., 6., -66., \ +-138., 30., 90., -30., 78., 6., -66., -138., -150., -150., -90., \ +150., -102., -174., 114., 42., -150., -90., 150., 150., 30., 30., \ +90., -102., -174., 114., 42., -150., -90., 150., 150., 30., 30., 90., \ +-102., -174., 114., 42., -150., -90., 150., 150., 30., 30., 90., \ +-102., -174., 114., 42., -150., -90., 150., 150., 30., 30., 90., \ +-102., -174., 114., 42., -150., -90., 150., -102., -174., 114.], \ +'insideEdgeIndices' : [[3, 5], [5, 6], [5, 9], [6, 9], [9, 10], [10, \ +16], [10, 17], [14, 15], [14, 22], [15, 22], [16, 17], [16, 24], [16, \ +25], [17, 25], [22, 23], [23, 32], [23, 33], [24, 25], [24, 33], [24, \ +34], [25, 34], [30, 31], [30, 41], [31, 41], [32, 33], [32, 43], [32, \ +44], [33, 44], [34, 35], [35, 45], [35, 46], [41, 42], [42, 53], [42, \ +54], [43, 44], [43, 54], [43, 55], [44, 55], [45, 46], [51, 52], [51, \ +63], [52, 63], [53, 54], [53, 65], [53, 66], [54, 66], [55, 56], [56, \ +67], [56, 68], [63, 64], [64, 75], [64, 76], [65, 66], [65, 76], [65, \ +77], [66, 77], [67, 68], [73, 74], [73, 84], [74, 84], [75, 76], [75, \ +86], [75, 87], [76, 87], [77, 78], [78, 88], [78, 89], [84, 85], [85, \ +94], [85, 95], [86, 87], [86, 95], [86, 96], [87, 96], [88, 89], [94, \ +95], [94, 102], [94, 103], [95, 103], [96, 97], [97, 104], [97, 105], \ +[102, 103], [102, 109], [103, 109], [104, 105], [109, 110], [110, \ +113], [110, 114], [113, 114], [114, 116]]}, 'TruncatedCube' : \ +{'edgeCoordinates' : [[0., 3.12132], [0., 4.12132], [0.707107, \ +2.41421], [0.707107, 4.82843], [1.70711, 2.41421], [1.70711, \ +4.82843], [2.15539, 2.15539], [2.15539, 5.08725], [2.41421, \ +0.707107], [2.41421, 1.70711], [2.41421, 3.12132], [2.41421, \ +4.12132], [2.41421, 5.53553], [2.41421, 6.53553], [3.12132, 0.], \ +[3.12132, 2.41421], [3.12132, 4.82843], [3.12132, 7.24264], [4.12132, \ +0.], [4.12132, 2.41421], [4.12132, 4.82843], [4.12132, 7.24264], \ +[4.82843, 0.707107], [4.82843, 1.70711], [4.82843, 3.12132], \ +[4.82843, 4.12132], [4.82843, 5.53553], [4.82843, 6.53553], [5.08725, \ +2.15539], [5.08725, 5.08725], [5.53553, 2.41421], [5.53553, 4.82843], \ +[6.53553, 2.41421], [6.53553, 4.82843], [7.24264, 3.12132], [7.24264, \ +4.12132], [7.50146, 2.15539], [7.50146, 5.08725], [7.94975, 2.41421], \ +[7.94975, 4.82843], [8.94975, 2.41421], [8.94975, 4.82843], [9.65685, \ +3.12132], [9.65685, 4.12132], [9.91567, 2.15539], [9.91567, \ +5.08725]], 'outsideEdgeIndices' : [[1, 2], [2, 4], [4, 6], [6, 12], \ +[12, 8], [8, 17], [17, 13], [13, 14], [14, 18], [18, 22], [22, 28], \ +[28, 27], [27, 21], [21, 30], [30, 26], [26, 32], [32, 34], [34, 38], \ +[38, 36], [36, 40], [40, 42], [42, 46], [46, 44], [44, 43], [43, 45], \ +[45, 41], [41, 39], [39, 35], [35, 37], [37, 33], [33, 31], [31, 25], \ +[25, 29], [29, 20], [20, 24], [24, 23], [23, 19], [19, 15], [15, 9], \ +[9, 10], [10, 16], [16, 7], [7, 11], [11, 5], [5, 3], [3, 1]], \ +'outsideEdgeDegrees' : [90., 45., 0., -45., 105., -15., 135., 90., \ +45., 0., -45., -90., -135., 15., -105., 45., 0., 15., -105., 45., 0., \ +15., -105., -90., -75., 165., 180., 135., -75., 165., 180., 135., \ +-75., 165., -45., -90., -135., 180., 135., 90., 45., -165., 75., \ +-135., 180., 135.], 'insideEdgeIndices' : [[11, 12], [11, 16], [12, \ +17], [16, 20], [17, 21], [20, 25], [21, 26], [25, 26], [33, 35], [34, \ +36], [35, 36], [41, 43], [42, 44]]}, 'TruncatedDodecahedron' : \ +{'edgeCoordinates' : [[10.5902, 0.], [11.5902, 0.], [9.78115, \ +0.587785], [12.3992, 0.587785], [13.3773, 0.795697], [8.16312, \ +1.2606], [9.47214, 1.53884], [12.7082, 1.53884], [7.66312, 2.12663], \ +[8.66312, 2.12663], [13.5172, 2.12663], [14.5172, 2.12663], [9.78115, \ +2.4899], [12.3992, 2.4899], [9.67662, 2.67095], [12.5037, 2.67095], \ +[6.8541, 2.71441], [9.47214, 2.71441], [12.7082, 2.71441], [15.3262, \ +2.71441], [10.5902, 3.07768], [11.5902, 3.07768], [6.54508, 3.66547], \ +[9.78115, 3.66547], [12.3992, 3.66547], [15.6353, 3.66547], [6.8541, \ +4.61653], [9.47214, 4.61653], [12.7082, 4.61653], [15.3262, 4.61653], \ +[7.66312, 5.20431], [8.66312, 5.20431], [13.5172, 5.20431], [14.5172, \ +5.20431], [8.80301, 5.35967], [13.3773, 5.35967], [8.78115, 5.56758], \ +[9.78115, 5.56758], [12.3992, 5.56758], [13.3992, 5.56758], [15.4308, \ +5.61105], [4.14961, 6.1119], [7.97214, 6.15537], [10.5902, 6.15537], \ +[11.5902, 6.15537], [14.2082, 6.15537], [6.99399, 6.36328], [2.23607, \ +6.51864], [3.23607, 6.51864], [5.8541, 6.51864], [6.8541, 6.51864], \ +[11.0902, 7.02139], [1.42705, 7.10642], [4.04508, 7.10642], [5.04508, \ +7.10642], [7.66312, 7.10642], [10.8992, 7.10642], [11.2812, 7.10642], \ +[14.5172, 7.10642], [1.11803, 8.05748], [4.3541, 8.05748], [4.73607, \ +8.05748], [7.97214, 8.05748], [10.5902, 8.05748], [11.5902, 8.05748], \ +[14.2082, 8.05748], [4.54508, 8.14251], [8.78115, 8.64527], [9.78115, \ +8.64527], [12.3992, 8.64527], [13.3992, 8.64527], [8.64127, 8.80062], \ +[1.42705, 9.00854], [4.04508, 9.00854], [5.04508, 9.00854], [7.66312, \ +9.00854], [11.4856, 9.052], [0.204489, 9.55286], [2.23607, 9.59632], \ +[3.23607, 9.59632], [5.8541, 9.59632], [6.8541, 9.59632], [2.25792, \ +9.80423], [6.83225, 9.80423], [1.11803, 9.95959], [2.11803, 9.95959], \ +[6.97214, 9.95959], [7.97214, 9.95959], [0.309017, 10.5474], \ +[2.92705, 10.5474], [6.16312, 10.5474], [8.78115, 10.5474], [0., \ +11.4984], [3.23607, 11.4984], [5.8541, 11.4984], [9.09017, 11.4984], \ +[4.04508, 12.0862], [5.04508, 12.0862], [0.309017, 12.4495], \ +[2.92705, 12.4495], [6.16312, 12.4495], [8.78115, 12.4495], [3.13154, \ +12.493], [5.95863, 12.493], [3.23607, 12.674], [5.8541, 12.674], \ +[1.11803, 13.0373], [2.11803, 13.0373], [6.97214, 13.0373], [7.97214, \ +13.0373], [2.92705, 13.6251], [6.16312, 13.6251], [7.47214, 13.9033], \ +[2.25792, 14.3682], [3.23607, 14.5761], [5.8541, 14.5761], [4.04508, \ +15.1639], [5.04508, 15.1639]], 'outsideEdgeIndices' : [[93, 99], [99, \ +107], [107, 108], [108, 100], [100, 94], [94, 103], [103, 97], [97, \ +105], [105, 111], [111, 114], [114, 115], [115, 117], [117, 118], \ +[118, 116], [116, 112], [112, 106], [106, 98], [98, 104], [104, 95], \ +[95, 101], [101, 109], [109, 113], [113, 110], [110, 102], [102, 96], \ +[96, 92], [92, 88], [88, 87], [87, 91], [91, 84], [84, 81], [81, 82], \ +[82, 76], [76, 72], [72, 63], [63, 68], [68, 69], [69, 64], [64, 57], \ +[57, 44], [44, 52], [52, 45], [45, 58], [58, 65], [65, 77], [77, 70], \ +[70, 71], [71, 66], [66, 59], [59, 46], [46, 40], [40, 39], [39, 36], \ +[36, 29], [29, 33], [33, 34], [34, 41], [41, 30], [30, 26], [26, 20], \ +[20, 12], [12, 11], [11, 19], [19, 25], [25, 16], [16, 22], [22, 14], \ +[14, 8], [8, 5], [5, 4], [4, 2], [2, 1], [1, 3], [3, 7], [7, 13], \ +[13, 21], [21, 15], [15, 24], [24, 18], [18, 10], [10, 6], [6, 9], \ +[9, 17], [17, 23], [23, 27], [27, 31], [31, 32], [32, 28], [28, 35], \ +[35, 38], [38, 37], [37, 43], [43, 47], [47, 56], [56, 51], [51, 50], \ +[50, 55], [55, 62], [62, 75], [75, 67], [67, 74], [74, 61], [61, 54], \ +[54, 42], [42, 49], [49, 48], [48, 53], [53, 60], [60, 73], [73, 79], \ +[79, 80], [80, 83], [83, 90], [90, 86], [86, 85], [85, 78], [78, 89], \ +[89, 93]], 'outsideEdgeDegrees' : [72., 36., 0., -36., -72., 96., \ +-24., 144., 108., 132., 12., 36., 0., -36., -72., -108., -144., 24., \ +-96., 72., 36., 60., -60., -36., -72., -108., -144., 180., 144., \ +-48., -168., 0., -36., -12., -132., 36., 0., -36., -72., -108., 60., \ +-60., 108., 72., 96., -24., 0., -36., -72., -108., -144., 180., -12., \ +-132., 36., 0., 24., -96., -72., -108., -144., 180., 144., 108., \ +-84., 156., -36., -72., -48., -168., -144., 180., 144., 108., 72., \ +36., -156., 84., -108., -144., -120., 120., 144., 108., 72., 36., 0., \ +-36., 132., 12., 180., 144., 168., 48., -144., 180., 144., 108., 72., \ +-120., 120., -72., -108., -84., 156., 180., 144., 108., 72., 36., 0., \ +168., 48., -144., 180., -156., 84., 108.], 'insideEdgeIndices' : [[4, \ +8], [9, 10], [21, 22], [21, 24], [22, 25], [24, 28], [25, 29], [28, \ +38], [29, 39], [30, 34], [38, 44], [39, 45], [43, 56], [44, 45], [49, \ +54], [56, 63], [63, 76], [65, 70], [74, 75], [74, 80], [75, 81], [80, \ +90], [81, 91], [85, 89], [90, 94], [91, 95], [94, 97], [95, 98], [97, \ +98], [109, 110], [111, 115]]}, 'TruncatedIcosahedron' : \ +{'edgeCoordinates' : [[2.5, 0.], [1.69098, 0.587785], [3.30902, \ +0.587785], [2., 1.53884], [3., 1.53884], [5., 1.53884], [6., \ +1.53884], [8., 1.53884], [9., 1.53884], [11., 1.53884], [12., \ +1.53884], [14., 1.53884], [15., 1.53884], [1.5, 2.40487], [3.5, \ +2.40487], [4.5, 2.40487], [6.5, 2.40487], [7.5, 2.40487], [9.5, \ +2.40487], [10.5, 2.40487], [12.5, 2.40487], [13.5, 2.40487], [15.5, \ +2.40487], [1., 2.59808], [4., 2.59808], [7., 2.59808], [10., \ +2.59808], [13., 2.59808], [0.190983, 3.18586], [1.80902, 3.18586], \ +[3.19098, 3.18586], [4.80902, 3.18586], [6.19098, 3.18586], [7.80902, \ +3.18586], [9.19098, 3.18586], [10.809, 3.18586], [12.191, 3.18586], \ +[13.809, 3.18586], [2., 3.27089], [3., 3.27089], [5., 3.27089], [6., \ +3.27089], [8., 3.27089], [9., 3.27089], [11., 3.27089], [12., \ +3.27089], [14., 3.27089], [15., 3.27089], [0.5, 4.13692], [1.5, \ +4.13692], [3.5, 4.13692], [4.5, 4.13692], [6.5, 4.13692], [7.5, \ +4.13692], [9.5, 4.13692], [10.5, 4.13692], [12.5, 4.13692], [13.5, \ +4.13692], [15.5, 4.13692], [0., 5.00294], [2., 5.00294], [3., \ +5.00294], [5., 5.00294], [6., 5.00294], [8., 5.00294], [9., 5.00294], \ +[11., 5.00294], [12., 5.00294], [14., 5.00294], [15., 5.00294], [0.5, \ +5.86897], [1.5, 5.86897], [3.5, 5.86897], [4.5, 5.86897], [6.5, \ +5.86897], [7.5, 5.86897], [9.5, 5.86897], [10.5, 5.86897], [12.5, \ +5.86897], [13.5, 5.86897], [1.69098, 5.954], [3.30902, 5.954], \ +[4.69098, 5.954], [6.30902, 5.954], [7.69098, 5.954], [9.30902, \ +5.954], [10.691, 5.954], [12.309, 5.954], [13.691, 5.954], [15.309, \ +5.954], [2.5, 6.54179], [5.5, 6.54179], [8.5, 6.54179], [11.5, \ +6.54179], [14.5, 6.54179], [0., 6.73499], [2., 6.73499], [3., \ +6.73499], [5., 6.73499], [6., 6.73499], [8., 6.73499], [9., 6.73499], \ +[11., 6.73499], [12., 6.73499], [14., 6.73499], [0.5, 7.60102], [1.5, \ +7.60102], [3.5, 7.60102], [4.5, 7.60102], [6.5, 7.60102], [7.5, \ +7.60102], [9.5, 7.60102], [10.5, 7.60102], [12.5, 7.60102], [13.5, \ +7.60102], [3.19098, 8.55208], [4.80902, 8.55208], [4., 9.13986]], \ +'outsideEdgeIndices' : [[60, 71], [71, 96], [96, 106], [106, 107], \ +[107, 97], [97, 72], [72, 61], [61, 81], [81, 91], [91, 82], [82, \ +62], [62, 73], [73, 98], [98, 108], [108, 116], [116, 118], [118, \ +117], [117, 109], [109, 99], [99, 74], [74, 63], [63, 83], [83, 92], \ +[92, 84], [84, 64], [64, 75], [75, 100], [100, 110], [110, 111], \ +[111, 101], [101, 76], [76, 65], [65, 85], [85, 93], [93, 86], [86, \ +66], [66, 77], [77, 102], [102, 112], [112, 113], [113, 103], [103, \ +78], [78, 67], [67, 87], [87, 94], [94, 88], [88, 68], [68, 79], [79, \ +104], [104, 114], [114, 115], [115, 105], [105, 80], [80, 69], [69, \ +89], [89, 95], [95, 90], [90, 70], [70, 59], [59, 48], [48, 23], [23, \ +13], [13, 12], [12, 22], [22, 47], [47, 58], [58, 38], [38, 28], [28, \ +37], [37, 57], [57, 46], [46, 21], [21, 11], [11, 10], [10, 20], [20, \ +45], [45, 56], [56, 36], [36, 27], [27, 35], [35, 55], [55, 44], [44, \ +19], [19, 9], [9, 8], [8, 18], [18, 43], [43, 54], [54, 34], [34, \ +26], [26, 33], [33, 53], [53, 42], [42, 17], [17, 7], [7, 6], [6, \ +16], [16, 41], [41, 52], [52, 32], [32, 25], [25, 31], [31, 51], [51, \ +40], [40, 15], [15, 5], [5, 3], [3, 1], [1, 2], [2, 4], [4, 14], [14, \ +39], [39, 50], [50, 30], [30, 24], [24, 29], [29, 49], [49, 60]], \ +'outsideEdgeDegrees' : [60., 120., 60., 0., -60., -120., -60., 108., \ +36., -36., -108., 60., 120., 60., 108., 36., -36., -108., -60., \ +-120., -60., 108., 36., -36., -108., 60., 120., 60., 0., -60., -120., \ +-60., 108., 36., -36., -108., 60., 120., 60., 0., -60., -120., -60., \ +108., 36., -36., -108., 60., 120., 60., 0., -60., -120., -60., 108., \ +36., -36., -108., -60., -120., -60., -120., 180., 120., 60., 120., \ +-72., -144., 144., 72., -120., -60., -120., 180., 120., 60., 120., \ +-72., -144., 144., 72., -120., -60., -120., 180., 120., 60., 120., \ +-72., -144., 144., 72., -120., -60., -120., 180., 120., 60., 120., \ +-72., -144., 144., 72., -120., -60., -120., -72., -144., 144., 72., \ +120., 60., 120., -72., -144., 144., 72., 120.], 'insideEdgeIndices' : \ +[[4, 5], [39, 40], [41, 42], [43, 44], [45, 46], [47, 48], [49, 50], \ +[50, 61], [51, 52], [51, 62], [52, 63], [53, 54], [53, 64], [54, 65], \ +[55, 56], [55, 66], [56, 67], [57, 58], [57, 68], [58, 69], [61, 62], \ +[63, 64], [65, 66], [67, 68], [69, 70], [71, 72], [73, 74], [75, 76], \ +[77, 78], [79, 80], [108, 109]]}, 'TruncatedOctahedron' : \ +{'edgeCoordinates' : [[0., 2.59808], [0.5, 1.73205], [0.5, 3.4641], \ +[0.5, 4.4641], [1.5, 1.73205], [1.5, 3.4641], [1.5, 4.4641], [2., \ +1.59808], [2., 2.59808], [2., 4.33013], [3., 0.866025], [3., \ +1.59808], [3., 2.59808], [3., 4.33013], [3.5, 0.], [3.5, 1.73205], \ +[3.5, 3.4641], [3.5, 4.4641], [4.5, 0.], [4.5, 1.73205], [4.5, \ +3.4641], [4.5, 4.4641], [4.5, 5.19615], [5., 0.866025], [5., \ +1.59808], [5., 2.59808], [5., 4.33013], [5., 6.06218], [6., 1.59808], \ +[6., 2.59808], [6., 4.33013], [6., 6.06218], [6.5, 1.73205], [6.5, \ +3.4641], [6.5, 4.4641], [6.5, 5.19615], [7.5, 1.73205], [7.5, \ +3.4641], [7.5, 4.4641], [8., 1.59808], [8., 2.59808], [8., 4.33013], \ +[9., 1.59808], [9., 2.59808], [9., 4.33013], [9.5, 3.4641]], \ +'outsideEdgeIndices' : [[1, 3], [3, 4], [4, 7], [7, 6], [6, 10], [10, \ +14], [14, 17], [17, 18], [18, 22], [22, 21], [21, 27], [27, 23], [23, \ +28], [28, 32], [32, 36], [36, 31], [31, 34], [34, 35], [35, 39], [39, \ +38], [38, 42], [42, 45], [45, 46], [46, 44], [44, 43], [43, 40], [40, \ +41], [41, 37], [37, 33], [33, 30], [30, 29], [29, 25], [25, 26], [26, \ +20], [20, 24], [24, 19], [19, 15], [15, 11], [11, 16], [16, 13], [13, \ +12], [12, 8], [8, 9], [9, 5], [5, 2], [2, 1]], 'outsideEdgeDegrees' : \ +[60., 90., 0., -90., 60., 0., -60., 90., 0., -90., 60., 120., 60., \ +0., -60., -120., -60., 90., 0., -90., 60., 0., -60., -120., -90., \ +180., 90., -120., 180., 120., -90., 180., 90., -120., -60., -120., \ +180., 120., 60., 120., -90., 180., 90., -120., 180., 120.], \ +'insideEdgeIndices' : [[3, 6], [6, 9], [9, 13], [13, 17], [16, 20], \ +[17, 21], [21, 26], [26, 30], [27, 31], [30, 34], [34, 38], [38, 41], \ +[41, 44]]}, 'TruncatedTetrahedron' : {'edgeCoordinates' : [[0., \ +1.73205], [0.5, 0.866025], [0.5, 2.59808], [1., 0.], [1.5, 0.866025], \ +[1.5, 2.59808], [2., 0.], [2., 1.73205], [2.5, 2.59808], [3., 0.], \ +[3., 1.73205], [3.5, 0.866025], [3.5, 2.59808], [4., 0.], [4.5, \ +0.866025], [4.5, 2.59808], [5., 0.], [5., 1.73205], [5.5, 2.59808], \ +[6., 0.], [6., 1.73205], [6.5, 0.866025]], 'outsideEdgeIndices' : \ +[[1, 3], [3, 6], [6, 8], [8, 9], [9, 11], [11, 13], [13, 16], [16, \ +18], [18, 19], [19, 21], [21, 22], [22, 20], [20, 17], [17, 15], [15, \ +14], [14, 12], [12, 10], [10, 7], [7, 5], [5, 4], [4, 2], [2, 1]], \ +'outsideEdgeDegrees' : [60., 0., -60., 60., -60., 60., 0., -60., 60., \ +-60., -60., -120., 180., 120., -120., 120., -120., 180., 120., -120., \ +120., 120.], 'insideEdgeIndices' : [[2, 5], [5, 8], [8, 11], [11, \ +12], [12, 15], [15, 18], [18, 21]]}, 'DeltoidalHexecontahedron' : \ +{'edgeCoordinates' : [[0., 5.24151], [0.24835, 4.27284], [0.597438, \ +3.33575], [0.735523, 5.91901], [1.44482, 2.13209], [1.53837, \ +6.51519], [1.5881, 3.1994], [1.69317, 1.16342], [1.71721, 4.73332], \ +[2.04226, 0.226332], [2.14857, 3.25565], [2.18034, 2.80959], \ +[2.33226, 7.75479], [2.44369, 6.09045], [2.58061, 6.78612], [2.84461, \ +6.28852], [2.98319, 3.40577], [3.03292, 0.0899751], [3.06779, \ +8.43229], [3.09238, 3.58615], [3.16203, 1.6239], [3.24828, 4.57393], \ +[3.29918, 5.57263], [3.50745, 5.53976], [3.87064, 9.02847], [3.88851, \ +2.98103], [4.03243, 0.0588187], [4.04947, 7.24659], [4.48084, \ +5.76892], [4.53325, 0.924372], [4.6931, 1.46451], [4.744, 2.46321], \ +[4.75401, 4.25401], [4.77595, 8.60372], [4.8697, 2.29393], [5.04412, \ +5.76577], [5.11805, 1.32526], [5.42464, 6.09943], [5.46714, \ +0.388167], [5.58054, 7.0872], [5.60522, 2.97143], [5.63144, 8.0859], \ +[6.01488, 5.52573], [6.26323, 4.55706], [6.40807, 3.56761], [6.4578, \ +0.25181], [6.58691, 1.78573], [6.61232, 3.61997], [6.75041, 6.20323], \ +[7.24269, 6.47701], [7.31339, 3.14286], [7.45731, 0.220653], \ +[7.60298, 3.48362], [7.73209, 5.01754], [7.75298, 2.79065], [7.95813, \ +1.08621], [8.20626, 6.74445], [8.36491, 1.99973], [8.60249, 3.45246], \ +[8.79511, 3.36669], [8.89816, 6.02245], [9.10331, 4.31801], [9.48701, \ +2.6447], [9.51009, 5.23154], [10.2512, 1.99967], [10.5073, 5.30593], \ +[10.519, 7.9539], [10.5406, 3.76695], [10.9049, 7.03133], [11.1488, \ +8.73065], [11.1812, 2.3672], [11.2381, 6.57715], [11.5068, 5.27478], \ +[11.5803, 1.36702], [11.6419, 2.69135], [11.857, 9.43668], [11.8855, \ +4.93896], [11.8885, 2.31834], [11.9528, 4.37971], [12.0022, 5.93212], \ +[12.2722, 0.645027], [12.2917, 7.6994], [12.2953, 3.23187], [12.3019, \ +3.44262], [12.8142, 9.14734], [12.9322, 6.29966], [13.0364, 0.], \ +[13.2925, 3.30626], [13.3258, 1.76728], [13.4216, 4.84018], [13.7356, \ +8.75869], [13.8183, 6.76319], [13.8297, 7.76313], [13.8958, 6.5671], \ +[13.9664, 0.367534], [14.204, 7.51842], [14.292, 3.27511], [14.4846, \ +3.18934], [14.4985, 7.99861], [14.5877, 5.8451], [14.738, 2.38004], \ +[14.7928, 4.14066], [14.8525, 0.831067], [14.8639, 1.831], [15.1765, \ +2.46734], [15.1996, 5.05419], [15.2067, 8.70463], [15.3519, 5.20008], \ +[15.6413, 6.96736], [15.9407, 1.82232], [16.1639, 8.41529], [16.1969, \ +5.12858], [16.2302, 3.5896], [16.2819, 5.56761], [16.7527, 5.03753], \ +[16.8707, 2.18985], [17.0853, 8.02664], [17.1679, 6.03114], [17.1793, \ +7.03108], [17.6741, 4.64888], [17.7568, 2.65338], [17.7682, \ +3.65332]], 'outsideEdgeIndices' : [[1, 4], [4, 6], [6, 14], [14, 23], \ +[23, 22], [22, 24], [24, 16], [16, 28], [28, 15], [15, 13], [13, 19], \ +[19, 25], [25, 34], [34, 42], [42, 40], [40, 38], [38, 29], [29, 33], \ +[33, 36], [36, 43], [43, 49], [49, 54], [54, 50], [50, 57], [57, 61], \ +[61, 64], [64, 66], [66, 73], [73, 79], [79, 90], [90, 77], [77, 80], \ +[80, 72], [72, 82], [82, 69], [69, 67], [67, 70], [70, 76], [76, 85], \ +[85, 91], [91, 93], [93, 92], [92, 86], [86, 94], [94, 96], [96, \ +109], [109, 99], [99, 107], [107, 111], [111, 117], [117, 119], [119, \ +118], [118, 114], [114, 108], [108, 100], [100, 106], [106, 112], \ +[112, 113], [113, 115], [115, 120], [120, 122], [122, 121], [121, \ +116], [116, 110], [110, 105], [105, 98], [98, 102], [102, 97], [97, \ +101], [101, 89], [89, 104], [104, 103], [103, 95], [95, 87], [87, \ +81], [81, 74], [74, 78], [78, 83], [83, 88], [88, 84], [84, 75], [75, \ +68], [68, 71], [71, 65], [65, 63], [63, 60], [60, 62], [62, 59], [59, \ +53], [53, 48], [48, 44], [44, 45], [45, 51], [51, 47], [47, 55], [55, \ +58], [58, 56], [56, 52], [52, 46], [46, 39], [39, 37], [37, 35], [35, \ +41], [41, 32], [32, 31], [31, 21], [21, 30], [30, 27], [27, 18], [18, \ +10], [10, 8], [8, 5], [5, 12], [12, 17], [17, 26], [26, 20], [20, \ +11], [11, 9], [9, 7], [7, 3], [3, 2], [2, 1]], 'outsideEdgeDegrees' : \ +[42.6486, 36.5969, -25.1345, -31.1861, -92.9175, 74.9792, 131.517, \ +38.4907, -162.594, 104.38, 42.6486, 36.5969, -25.1345, -31.1861, \ +-92.9175, -98.9692, -160.7, -79.778, 79.137, -13.8888, 42.6486, \ +-50.3773, 108.538, 15.5119, -46.2194, -52.2711, 4.26627, -1.78542, \ +-63.5167, 17.4057, 176.321, 83.2949, 139.832, 46.8064, -154.278, \ +112.696, 50.9643, 44.9126, -16.8187, -22.8704, -84.6017, -90.6534, \ +-152.385, 15.5119, 72.0493, -20.9766, 137.938, 44.9126, -16.8187, \ +-22.8704, -84.6017, -90.6534, -152.385, -158.436, 139.832, -52.2711, \ +4.26627, -88.7596, 70.1555, -22.8704, -84.6017, -90.6534, -152.385, \ +-158.436, 139.832, 133.781, 72.0493, -120.054, -63.5167, -156.543, \ +2.37247, -90.6534, -152.385, -158.436, 139.832, 133.781, 72.0493, \ +65.9976, 4.26627, 172.163, -131.3, 135.674, -65.4105, -158.436, \ +139.832, 133.781, 72.0493, -120.054, 178.215, 172.163, 110.432, \ +-81.6718, -25.1345, -118.16, 40.7548, -52.2711, -114.002, -120.054, \ +178.215, 172.163, 110.432, 104.38, 42.6486, -149.455, -92.9175, \ +174.057, -27.0283, -120.054, 178.215, 172.163, 110.432, 104.38, \ +42.6486, 36.5969, -25.1345, 142.762, -160.7, 106.274, -94.8113, \ +172.163, 110.432, 104.38], 'insideEdgeIndices' : [[2, 9], [4, 9], [8, \ +21], [9, 14], [9, 22], [12, 21], [18, 21], [19, 28], [20, 22], [21, \ +26], [22, 33], [24, 29], [26, 32], [26, 33], [28, 29], [28, 34], [28, \ +40], [33, 41], [33, 44], [37, 47], [41, 45], [41, 47], [43, 44], [44, \ +54], [46, 47], [47, 56], [53, 54], [54, 61], [54, 62], [62, 64], [62, \ +68], [63, 68], [66, 68], [68, 79], [70, 82], [78, 89], [79, 84], [80, \ +86], [81, 89], [82, 85], [82, 86], [82, 93], [86, 90], [88, 89], [88, \ +90], [88, 97], [89, 95], [90, 100], [90, 102], [94, 100], [100, 109], \ +[102, 106], [102, 113], [105, 113], [109, 111], [109, 114], [109, \ +119], [113, 116], [113, 122]]}, 'DeltoidalIcositetrahedron' : \ +{'edgeCoordinates' : [[0., 2.83078], [0.0111126, 3.07792], [0.289775, \ +1.57078], [0.412803, 4.30683], [0.753236, 0.982143], [0.927224, \ +4.85149], [0.996875, 2.90978], [1.49376, 2.04196], [1.56964, 3.7295], \ +[1.91007, 0.404813], [2.1312, 5.32267], [2.15291, 0.357629], \ +[2.22076, 1.35532], [2.35533, 4.34811], [2.3773, 5.34787], [2.50958, \ +2.84176], [3.15199, 1.71976], [3.2501, 3.90157], [3.44177, 0.459757], \ +[3.65179, 5.13048], [3.71356, 3.31293], [3.93769, 2.33837], [4.09193, \ +0.832016], [4.83245, 1.89183], [4.91469, 2.12514], [4.96458, \ +1.88284], [5.00241, 3.41506], [5.5548, 0.732533], [5.91156, 2.20414], \ +[6.14858, 0.275679], [6.26557, 3.13938], [6.57284, 4.93183], \ +[6.60628, 1.48485], [6.80446, 3.65985], [7.39468, 2.50954], [7.41174, \ +0.], [7.47959, 0.997696], [7.65874, 0.0138732], [7.75144, 3.98115], \ +[7.86502, 4.97467], [8.10544, 4.91639], [8.29286, 1.57958], [8.44615, \ +3.26186], [8.88308, 0.42927], [8.90266, 2.37214], [9.23455, 4.28655], \ +[9.42197, 0.949744], [9.67049, 3.67725], [9.87966, 2.15891], \ +[9.90211, 2.40528]], 'outsideEdgeIndices' : [[1, 7], [7, 2], [2, 4], \ +[4, 9], [9, 6], [6, 11], [11, 14], [14, 15], [15, 20], [20, 18], [18, \ +16], [16, 21], [21, 27], [27, 31], [31, 35], [35, 34], [34, 32], [32, \ +40], [40, 39], [39, 41], [41, 46], [46, 43], [43, 48], [48, 50], [50, \ +45], [45, 49], [49, 47], [47, 42], [42, 44], [44, 38], [38, 37], [37, \ +36], [36, 30], [30, 33], [33, 28], [28, 26], [26, 29], [29, 25], [25, \ +22], [22, 24], [24, 23], [23, 17], [17, 19], [19, 12], [12, 13], [13, \ +10], [10, 5], [5, 8], [8, 3], [3, 1]], 'outsideEdgeDegrees' : \ +[4.53063, 170.32, 71.8991, -26.522, 119.794, 21.3727, -77.0483, \ +88.7412, -9.67985, -108.101, -124.943, 21.3727, 4.53063, -12.3115, \ +-29.1536, 117.162, 100.32, 1.89899, -96.5221, 69.2675, -29.1536, \ +-127.575, 18.7411, -79.68, -178.101, -12.3115, -110.733, 150.846, \ +-62.8378, -161.259, 100.32, -93.8904, 167.689, 69.2675, -144.417, \ +117.162, 18.7411, -175.469, 167.689, -26.522, -124.943, 136.636, \ +-77.0483, -175.469, 86.1096, -108.101, 153.478, 55.057, -158.627, \ +102.952], 'insideEdgeIndices' : [[7, 8], [7, 9], [8, 13], [8, 16], \ +[9, 14], [9, 16], [13, 17], [14, 18], [16, 17], [17, 22], [21, 22], \ +[25, 27], [29, 31], [29, 33], [33, 35], [33, 37], [34, 39], [35, 42], \ +[35, 43], [37, 42], [39, 43], [42, 45], [43, 45]]}, \ +'DisdyakisDodecahedron' : {'edgeCoordinates' : [[0., 2.57676], \ +[0.085127, 2.06871], [0.191077, 3.90076], [0.314609, 4.13055], \ +[0.697332, 0.879308], [0.88902, 0.702332], [0.995539, 2.48241], \ +[1.31356, 5.02024], [1.42548, 3.38527], [1.69629, 1.769], [1.78429, \ +5.22948], [1.9414, 4.2419], [2.12342, 0.186845], [2.46326, 1.12733], \ +[2.60372, 2.75187], [2.63664, 0.142471], [2.923, 4.05092], [3.11408, \ +5.37491], [3.32899, 1.62784], [3.8732, 3.17364], [3.9412, 0.438438], \ +[4.14179, 2.21038], [4.16042, 0.579887], [4.96769, 1.64656], \ +[5.13875, 2.13246], [5.17776, 3.4696], [5.2812, 1.63741], [6.02492, \ +0.525498], [6.13856, 2.15213], [6.23552, 0.37151], [6.46284, \ +3.09809], [6.91602, 1.5232], [7.349, 5.25137], [7.46257, 3.91849], \ +[7.5206, 0.], [7.70558, 2.60304], [7.7511, 0.973074], [8.03554, \ +0.0143809], [8.45363, 4.05193], [8.55417, 1.56895], [8.66804, \ +5.02867], [8.91874, 3.16668], [9.12577, 4.79235], [9.29532, 2.24029], \ +[9.29789, 0.457034], [9.49957, 0.622536], [10.0712, 3.84594], \ +[10.1801, 1.77422], [10.1811, 3.60933], [10.2947, 2.27646]], \ +'outsideEdgeIndices' : [[1, 3], [3, 9], [9, 4], [4, 8], [8, 12], [12, \ +11], [11, 18], [18, 17], [17, 15], [15, 20], [20, 26], [26, 31], [31, \ +36], [36, 34], [34, 33], [33, 41], [41, 39], [39, 43], [43, 47], [47, \ +42], [42, 49], [49, 50], [50, 44], [44, 48], [48, 46], [46, 40], [40, \ +45], [45, 38], [38, 37], [37, 35], [35, 30], [30, 32], [32, 28], [28, \ +27], [27, 29], [29, 25], [25, 22], [22, 24], [24, 23], [23, 19], [19, \ +21], [21, 16], [16, 14], [14, 13], [13, 6], [6, 10], [10, 5], [5, 2], \ +[2, 7], [7, 1]], 'outsideEdgeDegrees' : [81.7878, -22.6655, 146.142, \ +41.689, -51.109, 99.0392, 6.24114, -98.2122, -103.808, 18.3785, \ +12.7824, -16.1242, -21.7203, 100.466, 94.8703, -9.58298, -102.381, \ +47.7672, -45.0309, -149.484, 19.3237, -85.1297, -177.928, -27.7795, \ +-120.578, 134.969, -56.223, -160.676, 106.526, -103.326, 163.876, \ +59.4224, -131.77, 123.777, 30.9789, -178.873, 175.531, -34.3208, \ +-127.119, 128.428, -62.7643, -167.218, 99.9844, -109.867, 157.335, \ +52.8812, -138.311, 117.236, 24.4377, 174.586], 'insideEdgeIndices' : \ +[[3, 7], [4, 12], [5, 7], [6, 14], [7, 9], [7, 10], [7, 15], [9, 12], \ +[9, 15], [10, 14], [10, 15], [12, 15], [12, 17], [12, 18], [14, 15], \ +[14, 19], [14, 21], [15, 19], [15, 22], [19, 22], [20, 22], [22, 23], \ +[22, 26], [25, 26], [26, 29], [28, 29], [29, 31], [29, 32], [29, 36], \ +[30, 37], [32, 36], [32, 37], [33, 39], [34, 39], [36, 37], [36, 39], \ +[36, 40], [36, 42], [36, 44], [37, 40], [37, 45], [39, 42], [39, 47], \ +[40, 44], [42, 44], [44, 46], [44, 49]]}, 'DisdyakisTriacontahedron' \ +: {'edgeCoordinates' : [[0., 5.2298], [0.104202, 6.22435], [0.243333, \ +7.21463], [0.600263, 1.88276], [0.704465, 2.87731], [0.843596, \ +3.86759], [0.843794, 4.69313], [1.19178, 7.53157], [1.44406, \ +1.34609], [1.66335, 6.03321], [1.70595, 4.18648], [1.79204, 4.18453], \ +[2.15079, 7.81495], [2.26361, 2.68617], [2.30621, 0.839446], \ +[2.68608, 4.49496], [2.75105, 4.46791], [2.79812, 7.16203], [2.83199, \ +7.08285], [2.90232, 8.15658], [3.04145, 9.14686], [3.10267, 5.40405], \ +[3.14409, 1.38531], [3.43225, 3.73582], [3.48473, 6.39758], [3.48701, \ +6.32724], [3.89546, 1.04331], [3.96224, 1.96031], [3.9899, 9.4638], \ +[3.99966, 2.03787], [4.08727, 2.98021], [4.13879, 3.02814], [4.46147, \ +7.96544], [4.48473, 6.39479], [4.56323, 4.82593], [4.73925, \ +0.506645], [4.9489, 9.74718], [5.08724, 3.34509], [5.4842, 6.42719], \ +[5.55881, 1.84672], [5.60141, 0.], [5.63011, 9.01508], [5.90079, \ +7.33628], [6.02394, 3.69521], [6.04624, 3.62846], [6.12814, 4.68976], \ +[6.19729, 5.68737], [6.26727, 5.68004], [6.28513, 8.25947], [6.43928, \ +0.545861], [6.86773, 3.15854], [6.92284, 3.09236], [7.10748, \ +2.10956], [7.11821, 6.25599], [7.25743, 1.12087], [7.68729, 4.49862], \ +[7.72989, 2.6519], [8.08927, 6.01713], [8.56776, 3.19776], [9.05132, \ +5.74426], [9.23596, 4.76145], [9.38592, 3.77276], [9.45508, 3.78575], \ +[9.84873, 6.34769], [10.4261, 3.5469], [10.6669, 6.9227], [10.6898, \ +2.76459], [10.7744, 5.07862], [10.8744, 1.78178], [11.0935, \ +0.806084], [11.405, 3.34236], [11.4872, 3.36802], [11.5463, 6.4467], \ +[12.0646, 0.56723], [12.2351, 3.94789], [12.3053, 3.94303], [12.3393, \ +4.94245], [12.4085, 5.94006], [12.4129, 2.09895], [12.7445, 7.54828], \ +[13.0434, 0.36269], [13.0806, 8.4901], [13.1848, 3.46703], [13.2168, \ +6.57443], [13.2312, 6.64331], [13.4497, 9.41951], [13.6629, 1.1477], \ +[13.9081, 4.86143], [14.0469, 2.96039], [14.0804, 3.02227], [14.2023, \ +6.40445], [14.2544, 1.95402], [14.2651, 2.03947], [14.4462, 9.50352], \ +[14.4842, 1.06377], [14.5506, 7.93617], [14.8779, 3.62571], [15.1811, \ +6.19991], [15.2498, 6.21533], [15.4344, 5.23253], [15.445, 9.55242], \ +[15.4553, 0.824915], [15.6535, 4.25683], [15.696, 4.20071], [15.8006, \ +6.98492], [15.8036, 2.35663], [15.872, 7.03303], [15.934, 8.68017], \ +[16.3921, 7.79125], [16.4341, 0.620376], [16.6246, 4.01797], [16.698, \ +3.97288], [16.8685, 7.11704], [16.9729, 5.54969], [17.0536, 1.40539], \ +[17.187, 3.10063], [17.6034, 3.81343], [17.6451, 2.21171], [17.8673, \ +7.16594], [18.2229, 4.59844], [18.3563, 6.29369], [18.8144, \ +5.40477]], 'outsideEdgeIndices' : [[1, 2], [2, 3], [3, 8], [8, 13], \ +[13, 19], [19, 26], [26, 34], [34, 25], [25, 33], [33, 18], [18, 20], \ +[20, 21], [21, 29], [29, 37], [37, 42], [42, 49], [49, 43], [43, 39], \ +[39, 35], [35, 47], [47, 46], [46, 48], [48, 56], [56, 54], [54, 58], \ +[58, 60], [60, 64], [64, 66], [66, 73], [73, 78], [78, 88], [88, 84], \ +[84, 91], [91, 85], [85, 96], [96, 80], [80, 82], [82, 86], [86, 94], \ +[94, 101], [101, 108], [108, 109], [109, 105], [105, 98], [98, 100], \ +[100, 99], [99, 114], [114, 107], [107, 113], [113, 119], [119, 121], \ +[121, 122], [122, 120], [120, 117], [117, 111], [111, 103], [103, \ +97], [97, 104], [104, 106], [106, 112], [112, 116], [116, 118], [118, \ +115], [115, 110], [110, 102], [102, 95], [95, 93], [93, 90], [90, \ +83], [83, 89], [89, 79], [79, 92], [92, 87], [87, 81], [81, 74], [74, \ +70], [70, 69], [69, 67], [67, 72], [72, 76], [76, 77], [77, 75], [75, \ +68], [68, 71], [71, 65], [65, 63], [63, 61], [61, 62], [62, 59], [59, \ +57], [57, 51], [51, 44], [44, 38], [38, 45], [45, 40], [40, 52], [52, \ +53], [53, 55], [55, 50], [50, 41], [41, 36], [36, 27], [27, 30], [30, \ +32], [32, 24], [24, 31], [31, 14], [14, 28], [28, 23], [23, 15], [15, \ +9], [9, 4], [4, 5], [5, 6], [6, 12], [12, 17], [17, 22], [22, 16], \ +[16, 10], [10, 11], [11, 7], [7, 1]], 'outsideEdgeDegrees' : \ +[84.0188, 82.0024, 18.4783, 16.4619, -47.0623, -49.0787, 3.87299, \ +179.84, 58.0781, -154.219, 84.0188, 82.0024, 18.4783, 16.4619, \ +-47.0623, -49.0787, -112.603, -114.619, -119.905, 27.7973, -93.9648, \ +82.0024, -39.7597, 107.943, -13.819, -15.8353, 37.1163, 35.0999, \ +-28.4242, -30.4406, -35.7269, 111.976, -9.78616, 166.181, 44.419, \ +-167.878, 70.3597, 68.3433, 4.81911, 2.80272, -60.7214, -62.7378, \ +-126.262, -128.278, -75.3267, 100.64, -21.1216, 126.581, 4.81911, \ +2.80272, -60.7214, -62.7378, -126.262, -128.278, 168.197, 166.181, \ +-140.867, 35.0999, -86.6621, 61.0406, -60.7214, -62.7378, -126.262, \ +-128.278, 168.197, 166.181, 102.657, 100.64, 153.592, -30.4406, \ +-152.203, -4.49992, -126.262, -128.278, 168.197, 166.181, 102.657, \ +100.64, 37.1163, 35.0999, 88.0516, -95.9812, 142.257, -70.0405, \ +168.197, 166.181, 102.657, -81.3759, -144.9, -146.916, 149.559, \ +147.543, -159.505, 16.4619, -105.3, 42.4026, -79.3595, -81.3759, \ +-144.9, -146.916, 149.559, 147.543, 84.0188, 82.0024, 134.954, \ +-49.0787, -170.841, -23.138, -144.9, -146.916, 149.559, 147.543, \ +84.0188, 82.0024, 18.4783, 16.4619, 69.4135, -114.619, 123.619, \ +-88.6785, 149.559, 147.543], 'insideEdgeIndices' : [[1, 10], [2, 10], \ +[3, 10], [4, 14], [5, 14], [6, 14], [7, 10], [8, 10], [9, 14], [10, \ +13], [10, 19], [10, 22], [10, 26], [12, 14], [14, 15], [14, 17], [14, \ +23], [14, 24], [17, 24], [17, 35], [20, 33], [21, 33], [22, 26], [22, \ +35], [24, 35], [26, 35], [27, 40], [29, 33], [30, 40], [32, 35], [32, \ +38], [32, 40], [33, 34], [33, 37], [33, 39], [33, 42], [33, 43], [33, \ +49], [34, 35], [34, 39], [35, 38], [35, 44], [35, 46], [36, 40], [38, \ +40], [40, 41], [40, 50], [40, 53], [40, 55], [44, 46], [44, 56], [46, \ +56], [51, 56], [56, 57], [56, 58], [56, 59], [56, 60], [56, 61], [56, \ +62], [60, 61], [60, 68], [61, 68], [63, 68], [64, 68], [65, 68], [66, \ +68], [67, 79], [68, 73], [68, 77], [68, 78], [69, 79], [70, 79], [72, \ +79], [74, 79], [76, 79], [76, 83], [76, 88], [77, 78], [77, 88], [79, \ +81], [79, 83], [79, 87], [82, 96], [83, 88], [86, 96], [88, 90], [88, \ +91], [88, 97], [88, 98], [88, 100], [88, 103], [90, 97], [90, 106], \ +[91, 96], [91, 98], [93, 106], [94, 96], [95, 106], [96, 98], [96, \ +101], [96, 105], [96, 108], [96, 109], [97, 106], [100, 103], [100, \ +114], [102, 106], [103, 114], [106, 110], [106, 115], [106, 116], \ +[106, 118], [111, 114], [113, 114], [114, 117], [114, 119], [114, \ +120], [114, 121], [114, 122]]}, 'PentagonalHexecontahedron' : \ +{'edgeCoordinates' : [[0., 3.61618], [0.169343, 8.16626], [0.173198, \ +5.37131], [0.342541, 9.92139], [0.470873, 2.73398], [0.555895, \ +4.44744], [0.640216, 7.28406], [0.725238, 8.99751], [0.807422, \ +6.14446], [0.976765, 10.6945], [1.47087, 2.73319], [1.64022, \ +7.28326], [1.78829, 5.94978], [1.95763, 10.4999], [2.02544, 1.90105], \ +[2.19479, 6.45112], [2.29729, 4.27559], [2.46663, 8.82567], [2.47478, \ +6.01642], [2.66415, 11.2075], [3.02076, 1.99767], [3.19011, 6.54774], \ +[3.39989, 6.39612], [3.40493, 2.92093], [3.57428, 7.47101], [3.59775, \ +7.37635], [3.62139, 10.9182], [3.81769, 9.9377], [3.97312, 4.77916], \ +[4.06955, 4.76454], [4.13738, 1.71513], [4.14246, 9.32923], [4.17098, \ +5.75939], [4.38611, 3.11405], [4.43658, 9.75451], [4.55545, 7.66412], \ +[4.60825, 0.832933], [4.60978, 11.5096], [4.67851, 4.07034], \ +[4.69327, 2.54639], [4.84786, 8.62042], [4.89495, 3.20596], [4.89899, \ +4.20595], [4.90746, 8.87231], [4.99248, 10.5858], [5.244, 12.2828], \ +[5.60825, 0.832137], [5.78272, 4.67396], [5.78979, 6.4238], [5.82903, \ +8.81353], [5.90746, 8.87151], [6.16282, 0.], [6.22487, 12.0881], \ +[6.43467, 2.37454], [6.46203, 8.03937], [6.61216, 4.11537], [6.66263, \ +4.19883], [6.73387, 10.4139], [6.93139, 12.7958], [7.15814, \ +0.0966189], [7.40317, 5.74632], [7.45735, 8.13599], [7.49656, \ +4.75069], [7.53727, 4.49507], [7.53964, 6.42241], [7.54231, 1.01988], \ +[7.84152, 9.05926], [7.88863, 12.5065], [8.01192, 7.30386], [8.08493, \ +11.526], [8.1105, 2.87811], [8.2371, 6.29819], [8.24115, 7.29818], \ +[8.27895, 9.59251], [8.30836, 3.85834], [8.41857, 4.36353], [8.52349, \ +1.213], [8.81589, 2.16929], [8.9504, 3.92436], [9.00944, 11.1448], \ +[9.10929, 10.1498], [9.12487, 7.76619], [9.50498, 3.09223], [9.77682, \ +5.46677], [9.95431, 7.2076], [10.5003, 3.18885], [10.8794, 7.58729], \ +[10.8845, 4.11211], [10.9451, 7.65938], [11.2375, 8.61567], [11.4527, \ +5.97033], [11.6505, 6.95056], [11.8656, 4.30522], [12.158, 5.26152], \ +[12.2187, 8.80879], [12.2237, 5.3336], [12.6029, 9.73205], [13.1488, \ +5.7133], [13.3263, 7.45413], [13.5982, 9.82867], [13.9783, 5.15471], \ +[13.9939, 2.77112], [14.0937, 1.77611], [14.1528, 8.99653], [14.2873, \ +10.7516], [14.5797, 11.7079], [14.6846, 8.55737], [14.7948, 9.06256], \ +[14.8242, 3.32838], [14.862, 5.62272], [14.8661, 6.62271], [14.9927, \ +10.0428], [15.0182, 1.39494], [15.0912, 5.61704], [15.2145, 0.4144], \ +[15.2616, 3.86164], [15.5609, 11.901], [15.5635, 6.49849], [15.5659, \ +8.42583], [15.6066, 8.1702], [15.6458, 4.7849], [15.7, 7.17458], \ +[15.945, 12.8243], [16.1718, 0.1251], [16.3693, 2.50698], [16.4405, \ +8.72207], [16.491, 8.80553], [16.6411, 4.88152], [16.6685, 10.5464], \ +[16.8783, 0.832791], [16.9403, 12.9209], [17.1957, 4.04939], \ +[17.2741, 4.10736], [17.3134, 6.4971], [17.3204, 8.24694], [17.4949, \ +12.0888], [17.8592, 0.638114], [18.1107, 2.33514], [18.1957, \ +4.04859], [18.2042, 8.71494], [18.2082, 9.71493], [18.2553, 4.30048], \ +[18.4099, 10.3745], [18.4246, 8.85056], [18.4934, 1.41126], [18.4949, \ +12.088], [18.5477, 5.25677], [18.6666, 3.16639], [18.7171, 9.80685], \ +[18.9322, 7.16151], [18.9607, 3.59166], [18.9658, 11.2058], [19.0336, \ +8.15635], [19.13, 8.14174], [19.2855, 2.98319], [19.4818, 2.00265], \ +[19.5054, 5.54455], [19.5289, 5.44989], [19.6982, 9.99997], [19.7033, \ +6.52478], [19.9131, 6.37315], [20.0824, 10.9232], [20.439, 1.71335], \ +[20.6284, 6.90448], [20.6365, 4.09523], [20.8059, 8.6453], [20.9084, \ +6.46977], [21.0777, 11.0198], [21.1455, 2.42104], [21.3149, 6.97112], \ +[21.4629, 5.63764], [21.6323, 10.1877], [22.1264, 2.22636], [22.2957, \ +6.77644], [22.3779, 3.92339], [22.4629, 5.63684], [22.5473, 8.47346], \ +[22.6323, 10.1869], [22.7606, 2.99951], [22.93, 7.54959], [22.9338, \ +4.75464], [23.1032, 9.30471]], 'outsideEdgeIndices' : [[1, 6], [6, \ +3], [3, 9], [9, 13], [13, 17], [17, 19], [19, 23], [23, 26], [26, \ +36], [36, 25], [25, 22], [22, 16], [16, 12], [12, 7], [7, 2], [2, 8], \ +[8, 4], [4, 10], [10, 14], [14, 20], [20, 27], [27, 28], [28, 18], \ +[18, 32], [32, 41], [41, 50], [50, 55], [55, 51], [51, 44], [44, 35], \ +[35, 45], [45, 38], [38, 46], [46, 53], [53, 59], [59, 68], [68, 70], \ +[70, 80], [80, 81], [81, 74], [74, 58], [58, 67], [67, 62], [62, 69], \ +[69, 65], [65, 49], [49, 61], [61, 72], [72, 73], [73, 82], [82, 85], \ +[85, 87], [87, 92], [92, 89], [89, 90], [90, 95], [95, 97], [97, \ +100], [100, 104], [104, 99], [99, 107], [107, 120], [120, 126], [126, \ +135], [135, 127], [127, 119], [119, 108], [108, 112], [112, 105], \ +[105, 106], [106, 117], [117, 123], [123, 131], [131, 136], [136, \ +146], [146, 152], [152, 143], [143, 129], [129, 141], [141, 140], \ +[140, 153], [153, 150], [150, 154], [154, 144], [144, 149], [149, \ +159], [159, 162], [162, 168], [168, 172], [172, 178], [178, 182], \ +[182, 177], [177, 180], [180, 174], [174, 170], [170, 166], [166, \ +164], [164, 160], [160, 157], [157, 147], [147, 158], [158, 161], \ +[161, 167], [167, 171], [171, 176], [176, 181], [181, 175], [175, \ +179], [179, 173], [173, 169], [169, 163], [163, 156], [156, 155], \ +[155, 165], [165, 151], [151, 142], [142, 133], [133, 128], [128, \ +132], [132, 139], [139, 148], [148, 138], [138, 145], [145, 137], \ +[137, 130], [130, 124], [124, 115], [115, 113], [113, 103], [103, \ +102], [102, 109], [109, 125], [125, 116], [116, 121], [121, 114], \ +[114, 118], [118, 134], [134, 122], [122, 111], [111, 110], [110, \ +101], [101, 98], [98, 96], [96, 91], [91, 94], [94, 93], [93, 88], \ +[88, 86], [86, 83], [83, 79], [79, 84], [84, 76], [76, 63], [63, 57], \ +[57, 48], [48, 56], [56, 64], [64, 75], [75, 71], [71, 78], [78, 77], \ +[77, 66], [66, 60], [60, 52], [52, 47], [47, 37], [37, 31], [31, 40], \ +[40, 54], [54, 42], [42, 43], [43, 30], [30, 33], [33, 29], [29, 39], \ +[39, 34], [34, 24], [24, 21], [21, 15], [15, 11], [11, 5], [5, 1]], \ +'outsideEdgeDegrees' : [56.2276, 112.501, 50.6375, -11.2259, \ +-73.0893, 84.1783, 22.3149, 78.5881, 16.7248, -168.865, -112.592, \ +-174.455, 123.681, 179.954, 118.091, 56.2276, 112.501, 50.6375, \ +-11.2259, 45.0474, -16.816, -78.6794, -140.543, 16.7248, -45.1386, \ +11.1346, -50.7287, 123.681, 179.954, 118.091, 56.2276, 112.501, \ +50.6375, -11.2259, 45.0474, -16.816, -78.6794, -22.4062, -84.2695, \ +-146.133, 152.004, -50.7287, -112.592, -56.3189, -118.182, 179.954, \ +-22.7781, 33.4952, 89.7684, 27.905, -33.9583, 22.3149, -39.5485, \ +134.861, 72.998, 11.1346, 67.4079, 5.54451, -56.3189, -118.182, \ +39.0853, -22.7781, 33.4952, -28.3682, 146.042, -157.685, 140.452, \ +78.5881, 134.861, 72.998, 11.1346, 67.4079, 5.54451, -56.3189, \ +-0.0456262, -61.909, -123.772, 174.364, -28.3682, -90.2316, -33.9583, \ +-95.8217, 78.5881, 134.861, 72.998, 11.1346, 67.4079, 5.54451, \ +-56.3189, -0.0456262, -61.909, -123.772, -67.4991, -129.363, 168.774, \ +106.911, -95.8217, -157.685, -101.412, -163.275, 11.1346, 67.4079, \ +5.54451, -56.3189, -0.0456262, -61.909, -123.772, -67.4991, -129.363, \ +168.774, -134.953, 163.184, 101.321, 39.4572, -163.275, 134.861, \ +-168.865, 129.271, -56.3189, -0.0456262, -61.909, -123.772, -67.4991, \ +-129.363, 168.774, -134.953, 163.184, 101.321, 157.594, 95.7305, \ +33.8671, -27.9963, 129.271, 67.4079, 123.681, 61.8178, -0.0456262, \ +157.222, -146.505, -90.2316, -152.095, 146.042, -157.685, 140.452, \ +-45.1386, -107.002, -168.865, -112.592, -174.455, 123.681, 61.8178, \ +-140.915, 157.222, -146.505, 151.632, -33.9583, 22.3149, -39.5485, \ +-101.412, -45.1386, -107.002, -168.865, -112.592, -174.455, 123.681, \ +179.954, 118.091, 56.2276, -5.63576, 151.632, 89.7684, 146.042, \ +84.1783, -101.412, -45.1386, -107.002, -168.865, -112.592, -174.455, \ +123.681, 179.954, 118.091], 'insideEdgeIndices' : [[6, 17], [8, 18], \ +[11, 17], [12, 18], [14, 18], [17, 24], [17, 29], [18, 25], [23, 33], \ +[33, 49], [36, 41], [36, 49], [43, 48], [45, 58], [47, 54], [48, 49], \ +[49, 55], [51, 58], [53, 58], [54, 56], [54, 66], [54, 71], [55, 62], \ +[58, 70], [61, 63], [72, 84], [84, 85], [84, 88], [84, 91], [91, 92], \ +[92, 99], [95, 99], [98, 99], [99, 111], [112, 129], [113, 125], \ +[117, 129], [120, 122], [121, 128], [125, 130], [125, 132], [125, \ +138], [127, 129], [128, 134], [129, 136], [134, 135], [134, 147], \ +[134, 150], [135, 140], [142, 147], [150, 160], [154, 166], [158, \ +165], [159, 166], [165, 169], [165, 171], [165, 175], [166, 172], \ +[166, 177]]}, 'PentagonalIcositetrahedron' : {'edgeCoordinates' : \ +[[0., 4.35723], [0.394152, 2.18944], [0.430299, 5.25992], [0.638786, \ +3.58785], [1.4608, 0.808884], [1.60521, 3.8448], [1.8137, 2.17273], \ +[1.84984, 5.24321], [2.18817, 7.17717], [2.21608, 5.12629], [2.244, \ +3.07542], [2.32771, 5.7644], [2.34838, 2.82554], [2.44667, 1.83038], \ +[2.47589, 5.40966], [2.62746, 0.], [3.05021, 7.684], [3.21042, \ +3.33237], [3.38274, 4.31741], [3.39121, 1.50199], [3.48951, \ +0.506832], [3.69969, 6.12918], [3.87201, 7.11422], [4.34917, \ +4.57436], [4.37708, 2.52349], [4.43405, 0.178442], [4.52149, 5.5594], \ +[4.61501, 2.22151], [4.74332, 2.40657], [4.77946, 5.47705], [4.83843, \ +7.37118], [4.98795, 3.80498], [5.1285, 0.897986], [5.74528, 6.27892], \ +[5.95438, 4.06193], [6.16286, 2.38986], [6.19901, 5.46034], [6.42084, \ +2.3075], [6.56525, 5.34342], [6.59316, 3.29255], [7.11787, 6.09878], \ +[7.24264, 1.73773], [7.55959, 3.5495], [7.6984, 4.80326], [7.73191, \ +4.53454], [8.46643, 2.45725], [8.48984, 6.46356], [8.69833, 4.79149], \ +[8.72625, 2.74061], [8.92014, 7.36625], [9.09248, 2.6237], [9.12863, \ +5.69418], [9.14005, 2.24221], [9.33712, 4.02211], [9.72058, \ +0.946689], [9.92007, 7.35448], [10.3035, 4.27906], [10.5006, \ +6.05896], [10.512, 2.60699], [10.5482, 5.67747], [10.7205, 0.934917], \ +[10.7224, 6.02015], [10.9423, 3.50967], [11.1253, 3.70928], [11.1508, \ +1.8376], [11.7083, 7.04165], [11.8891, 5.21127], [11.9423, 3.4979], \ +[11.9874, 4.21611], [12.5228, 2.20238], [12.6528, 6.71326], [12.7511, \ +5.7181], [13.3283, 3.74991], [13.5729, 5.14832]], \ +'outsideEdgeIndices' : [[1, 3], [3, 8], [8, 6], [6, 10], [10, 19], \ +[19, 15], [15, 22], [22, 12], [12, 9], [9, 17], [17, 23], [23, 31], \ +[31, 34], [34, 27], [27, 24], [24, 30], [30, 37], [37, 35], [35, 39], \ +[39, 45], [45, 48], [48, 44], [44, 41], [41, 47], [47, 50], [50, 56], \ +[56, 58], [58, 52], [52, 60], [60, 67], [67, 62], [62, 66], [66, 71], \ +[71, 72], [72, 74], [74, 73], [73, 69], [69, 64], [64, 57], [57, 63], \ +[63, 68], [68, 70], [70, 65], [65, 61], [61, 55], [55, 53], [53, 59], \ +[59, 51], [51, 54], [54, 49], [49, 43], [43, 46], [46, 42], [42, 38], \ +[38, 40], [40, 36], [36, 29], [29, 32], [32, 25], [25, 20], [20, 28], \ +[28, 33], [33, 26], [26, 21], [21, 16], [16, 5], [5, 14], [14, 13], \ +[13, 18], [18, 11], [11, 7], [7, 2], [2, 4], [4, 1]], \ +'outsideEdgeDegrees' : [64.5134, -0.674489, -99.9228, 64.5134, \ +-34.7349, 129.701, 30.4531, -165.111, 95.641, 30.4531, -34.7349, \ +14.8893, -50.2986, -149.547, -99.9228, 64.5134, -0.674489, -99.9228, \ +64.5134, -34.7349, 14.8893, 179.326, 114.138, 14.8893, 64.5134, \ +-0.674489, -65.8624, -165.111, -0.674489, -19.1711, 145.265, 46.0168, \ +-19.1711, -84.359, -34.7349, -99.9228, 160.829, -149.547, 145.265, \ +-50.2986, -0.674489, -65.8624, -165.111, -115.487, 179.326, 114.138, \ +14.8893, 179.326, 80.0772, -115.487, 145.265, -50.2986, -149.547, \ +145.265, 80.0772, -115.487, 179.326, 80.0772, -115.487, -133.983, \ +30.4531, -68.7952, -133.983, 160.829, -149.547, 145.265, 46.0168, \ +95.641, 30.4531, -165.111, -115.487, 179.326, 80.0772, 129.701], \ +'insideEdgeIndices' : [[4, 6], [6, 11], [14, 20], [18, 19], [18, 25], \ +[19, 24], [20, 21], [22, 23], [22, 27], [24, 32], [32, 35], [35, 40], \ +[40, 43], [43, 45], [47, 52], [48, 52], [48, 54], [54, 57], [57, 60], \ +[59, 63], [59, 65], [67, 69], [67, 72]]}, 'PentakisDodecahedron' : \ +{'edgeCoordinates' : [[0., 3.34303], [0.26284, 2.24678], [0.695721, \ +1.61552], [0.924979, 3.98745], [0.934661, 2.98749], [0.958561, \ +0.519262], [1.35498, 4.62068], [1.37947, 2.09187], [1.61782, \ +3.52442], [1.6207, 2.25993], [1.63038, 1.25998], [1.86226, 3.36107], \ +[2.07519, 0.364349], [2.27996, 5.26509], [2.28964, 4.26514], [2.5483, \ +2.6335], [2.55798, 1.63355], [2.62653, 1.34765], [2.82082, 0.537294], \ +[2.97568, 3.53757], [3.21724, 4.63871], [3.22012, 3.37421], [3.36391, \ +3.63066], [3.48296, 2.27796], [3.49264, 1.27801], [3.77418, 2.23756], \ +[3.93745, 0.382381], [4.15478, 3.01867], [4.43632, 3.97823], \ +[4.48879, 1.36568], [4.59959, 2.12305], [5.15093, 3.10635], [5.31421, \ +1.25117], [5.59574, 2.21072], [5.71429, 4.37323], [5.97906, \ +0.871881], [6.14447, 3.33122], [6.26756, 2.95143], [6.38661, \ +1.59874], [6.41135, 3.20787], [6.52776, 5.1537], [6.5304, 1.85518], \ +[6.69288, 4.16743], [6.69368, 0.], [6.97521, 0.959552], [7.20222, \ +2.59589], [7.48375, 3.55544], [7.50131, 3.84892], [7.55107, 4.68075], \ +[7.64703, 1.70027], [7.76608, 0.347571], [7.8334, 1.47288], [8.19837, \ +2.68356], [8.31478, 4.62939], [8.36165, 0.828385], [8.47805, \ +2.77421], [8.4799, 3.64312], [8.64318, 1.78794], [9.27077, 3.03113], \ +[9.33809, 4.15644], [9.43405, 1.17596], [9.50137, 2.30127]], \ +'outsideEdgeIndices' : [[1, 4], [4, 12], [12, 15], [15, 9], [9, 7], \ +[7, 14], [14, 21], [21, 20], [20, 16], [16, 22], [22, 28], [28, 23], \ +[23, 29], [29, 32], [32, 38], [38, 46], [46, 40], [40, 43], [43, 37], \ +[37, 35], [35, 41], [41, 49], [49, 47], [47, 57], [57, 48], [48, 54], \ +[54, 60], [60, 59], [59, 53], [53, 58], [58, 56], [56, 62], [62, 61], \ +[61, 55], [55, 50], [50, 45], [45, 52], [52, 51], [51, 44], [44, 36], \ +[36, 42], [42, 34], [34, 39], [39, 33], [33, 31], [31, 24], [24, 25], \ +[25, 26], [26, 30], [30, 27], [27, 19], [19, 17], [17, 11], [11, 18], \ +[18, 13], [13, 6], [6, 3], [3, 10], [10, 5], [5, 8], [8, 2], [2, 1]], \ +'outsideEdgeDegrees' : [34.8641, -33.7546, 64.6986, -132.208, \ +103.483, 34.8641, -33.7546, -102.373, -115.301, 47.7922, -20.8265, \ +142.267, 17.9577, -50.661, -7.89843, -20.8265, 142.267, 73.6484, \ +-123.258, 112.433, 43.8139, -24.8048, -93.4235, 5.02965, 168.123, \ +43.8139, -24.8048, -93.4235, -162.042, -63.5891, 99.5045, -24.8048, \ +-93.4235, -162.042, 129.339, -132.208, 30.8858, -93.4235, -162.042, \ +129.339, 60.7203, 159.173, -37.7329, -162.042, 129.339, 172.102, \ +-89.4452, 73.6484, -50.661, -119.28, 172.102, 103.483, -158.064, \ +5.02965, -119.28, 172.102, 103.483, 34.8641, 133.317, -63.5891, \ +172.102, 103.483], 'insideEdgeIndices' : [[1, 5], [2, 5], [3, 11], \ +[4, 5], [5, 12], [6, 11], [7, 15], [10, 11], [10, 12], [10, 16], [10, \ +17], [11, 13], [12, 16], [12, 20], [14, 15], [15, 20], [15, 21], [16, \ +17], [16, 24], [17, 24], [17, 25], [19, 25], [22, 24], [24, 28], [25, \ +27], [25, 30], [28, 29], [28, 31], [28, 32], [31, 32], [31, 34], [32, \ +34], [33, 34], [34, 38], [35, 43], [36, 45], [38, 42], [40, 47], [41, \ +43], [42, 45], [42, 46], [42, 50], [43, 47], [43, 49], [44, 45], [45, \ +51], [46, 47], [46, 50], [46, 53], [47, 53], [50, 53], [50, 58], [53, \ +57], [54, 57], [55, 58], [57, 59], [57, 60], [58, 61], [58, 62]]}, \ +'RhombicDodecahedron' : {'edgeCoordinates' : [[0.272166, 2.11695], \ +[0., 1.1547], [0.816497, 0.57735], [1.08866, 1.5396], [1.08866, \ +2.6943], [1.36083, 0.57735], [1.90516, 2.11695], [2.17732, 0.], \ +[2.17732, 1.1547], [2.44949, 2.11695], [2.99382, 0.57735], [3.26599, \ +1.5396], [3.26599, 2.6943], [3.53815, 0.57735], [4.08248, 2.11695], \ +[4.35465, 0.], [4.35465, 1.1547], [4.62681, 2.11695], [5.17115, \ +0.57735], [5.44331, 1.5396], [5.44331, 2.6943], [5.71548, 0.57735], \ +[6.25981, 2.11695], [6.53197, 0.], [6.53197, 1.1547], [7.34847, \ +0.57735]], 'outsideEdgeIndices' : [[2, 1], [1, 5], [5, 7], [7, 9], \ +[9, 10], [10, 13], [13, 15], [15, 17], [17, 18], [18, 21], [21, 23], \ +[23, 25], [25, 26], [26, 24], [24, 22], [22, 20], [20, 19], [19, 16], \ +[16, 14], [14, 12], [12, 11], [11, 8], [8, 6], [6, 4], [4, 3], [3, \ +2]], 'outsideEdgeDegrees' : [74.2068, 35.2644, -35.2644, -74.2068, \ +74.2068, 35.2644, -35.2644, -74.2068, 74.2068, 35.2644, -35.2644, \ +-74.2068, -35.2644, -144.736, 144.736, 105.793, -105.793, -144.736, \ +144.736, 105.793, -105.793, -144.736, 144.736, 105.793, -105.793, \ +144.736], 'insideEdgeIndices' : [[1, 4], [4, 7], [6, 9], [9, 11], \ +[10, 12], [12, 15], [14, 17], [17, 19], [18, 20], [20, 23], [22, \ +25]]}, 'RhombicTriacontahedron' : {'edgeCoordinates' : [[0.0898056, \ +3.04338], [5.64274, 4.03934], [5.64274, 0.995959], [5.64274, \ +2.04742], [5.64274, 2.04742], [6.49339, 1.52169], [6.49339, 4.56507], \ +[1.79111, 3.04338], [1.79111, 0.], [9.40456, 0.995959], [9.40456, \ +2.04742], [7.52365, 0.995959], [7.52365, 2.04742], [8.46411, \ +2.51765], [8.46411, 3.56911], [8.46411, 0.525731], [7.52365, \ +4.03934], [7.52365, 2.04742], [10.2552, 1.52169], [6.58319, 2.51765], \ +[6.58319, 3.56911], [6.58319, 0.525731], [9.31476, 3.04338], \ +[9.31476, 0.], [8.3743, 1.52169], [8.3743, 4.56507], [3.76183, \ +0.995959], [3.76183, 2.04742], [7.43385, 3.04338], [7.43385, 0.], \ +[4.70228, 2.51765], [4.70228, 3.56911], [4.70228, 0.525731], \ +[3.76183, 4.03934], [3.76183, 2.04742], [4.61248, 1.52169], [4.61248, \ +4.56507], [2.82137, 2.51765], [2.82137, 3.56911], [2.82137, \ +0.525731], [5.55293, 3.04338], [5.55293, 0.], [0., 4.03934], [0., \ +2.04742], [3.67202, 3.04338], [3.67202, 0.], [1.88091, 4.03934], \ +[1.88091, 0.995959], [1.88091, 2.04742], [1.88091, 2.04742], \ +[1.03026, 1.52169], [1.97072, 3.04338], [2.91117, 1.52169], [3.85163, \ +3.04338], [4.79209, 1.52169], [5.73254, 3.04338], [6.673, 1.52169], \ +[7.61346, 3.04338], [8.55391, 1.52169], [0.940456, 2.51765], \ +[0.940456, 3.56911], [0.940456, 0.525731], [0.850651, 1.52169], \ +[0.850651, 4.56507], [2.73156, 1.52169], [2.73156, 4.56507]], \ +'outsideEdgeIndices' : [[44, 1], [1, 43], [43, 64], [64, 61], [61, \ +8], [8, 49], [49, 52], [52, 47], [47, 66], [66, 39], [39, 45], [45, \ +28], [28, 54], [54, 34], [34, 37], [37, 32], [32, 41], [41, 4], [4, \ +56], [56, 2], [2, 7], [7, 21], [21, 29], [29, 13], [13, 58], [58, \ +17], [17, 26], [26, 15], [15, 23], [23, 11], [11, 19], [19, 10], [10, \ +24], [24, 16], [16, 59], [59, 14], [14, 25], [25, 12], [12, 30], [30, \ +22], [22, 57], [57, 20], [20, 6], [6, 3], [3, 42], [42, 33], [33, \ +55], [55, 31], [31, 36], [36, 27], [27, 46], [46, 40], [40, 53], [53, \ +38], [38, 65], [65, 48], [48, 9], [9, 62], [62, 51], [51, 60], [60, \ +63], [63, 44]], 'outsideEdgeDegrees' : [84.8476, 95.1524, 31.7175, \ +-84.8476, -31.7175, -84.8476, 84.8476, 95.1524, 31.7175, -84.8476, \ +-31.7175, -84.8476, 84.8476, 95.1524, 31.7175, -84.8476, -31.7175, \ +-84.8476, 84.8476, 95.1524, 31.7175, -84.8476, -31.7175, -84.8476, \ +84.8476, 95.1524, 31.7175, -84.8476, -31.7175, -84.8476, -31.7175, \ +-148.283, -95.1524, 148.283, 84.8476, 95.1524, -95.1524, -148.283, \ +-95.1524, 148.283, 84.8476, 95.1524, -95.1524, -148.283, -95.1524, \ +148.283, 84.8476, 95.1524, -95.1524, -148.283, -95.1524, 148.283, \ +84.8476, 95.1524, -95.1524, -148.283, -95.1524, 148.283, 84.8476, \ +95.1524, -95.1524, 148.283], 'insideEdgeIndices' : [[1, 60], [1, 61], \ +[3, 55], [4, 6], [4, 55], [8, 60], [10, 59], [11, 59], [12, 57], [13, \ +25], [13, 57], [14, 23], [14, 58], [15, 58], [20, 29], [20, 56], [21, \ +56], [27, 53], [28, 36], [28, 53], [31, 41], [31, 54], [32, 54], [38, \ +45], [38, 52], [39, 52], [48, 51], [49, 51], [49, 65]]}, \ +'SmallTriakisOctahedron' : {'edgeCoordinates' : [[0., 1.51832], \ +[1.70711, 1.51832], [2.70711, 0.997316], [2.70711, 2.03933], \ +[3.70711, 1.51832], [3.70711, 4.55496], [0.853553, 2.03933], \ +[5.41421, 1.51832], [0.926777, 3.03664], [1.85355, 1.51832], \ +[2.78033, 3.03664], [2.78033, 0.], [4.63388, 3.03664], [5.56066, \ +1.51832], [0.78033, 3.03664], [1.78033, 2.51564], [3.56066, 1.51832], \ +[3.56066, 4.55496], [4.56066, 2.03933], [2.63388, 3.03664], [2.63388, \ +0.], [3.63388, 3.55765], [3.63388, 2.51564], [3.63388, 3.55765], \ +[4.48744, 3.03664], [5.48744, 2.51564], [6.34099, 3.03664]], \ +'outsideEdgeIndices' : [[1, 15], [15, 7], [7, 9], [9, 20], [20, 4], \ +[4, 11], [11, 18], [18, 22], [22, 6], [6, 25], [25, 19], [19, 13], \ +[13, 27], [27, 14], [14, 26], [26, 8], [8, 5], [5, 23], [23, 17], \ +[17, 12], [12, 3], [3, 21], [21, 10], [10, 16], [16, 2], [2, 1]], \ +'outsideEdgeDegrees' : [62.7994, -85.8009, 85.8009, 0., -85.8009, \ +85.8009, 62.7994, -85.8009, 85.8009, -62.7994, -85.8009, 85.8009, 0., \ +-117.201, 94.1991, -94.1991, 180., 94.1991, -94.1991, -117.201, \ +94.1991, -94.1991, 117.201, 94.1991, -94.1991, 180.], \ +'insideEdgeIndices' : [[1, 7], [2, 7], [2, 9], [3, 10], [3, 17], [4, \ +10], [4, 17], [5, 19], [5, 25], [8, 13], [8, 19], [9, 16], [10, 17], \ +[10, 20], [11, 17], [11, 22], [11, 23], [11, 25], [13, 26], [16, 20], \ +[22, 25], [23, 25], [26, 27]]}, 'TetrakisHexahedron' : \ +{'edgeCoordinates' : [[0., 2.44849], [0.578072, 3.64999], [0.959843, \ +0.707993], [0.960697, 2.72609], [1.12983, 1.74049], [1.53791, \ +1.9095], [1.83637, 3.209], [1.84046, 4.54233], [1.92054, 0.985593], \ +[2.08968, 0.], [2.41358, 2.39241], [2.49776, 0.169003], [2.58376, \ +3.87337], [2.79621, 1.4685], [3.15689, 1.72346], [3.16098, 3.05678], \ +[3.16598, 4.68641], [3.45645, 3.3851], [3.90429, 2.38783], [4.48151, \ +1.57124], [4.48651, 3.20087], [4.77698, 1.89956], [5.3592, 2.71259], \ +[5.74748, 3.63413], [6.1025, 2.04364], [6.31817, 2.4291]], \ +'outsideEdgeIndices' : [[1, 2], [2, 7], [7, 8], [8, 17], [17, 18], \ +[18, 13], [13, 16], [16, 21], [21, 24], [24, 26], [26, 23], [23, 25], \ +[25, 22], [22, 19], [19, 20], [20, 15], [15, 11], [11, 14], [14, 12], \ +[12, 9], [9, 10], [10, 3], [3, 6], [6, 4], [4, 5], [5, 1]], \ +'outsideEdgeDegrees' : [64.3067, -19.3139, 89.8242, 6.20355, \ +-77.4171, 150.773, -54.7449, 6.20355, 18.9623, -64.6583, 163.531, \ +-41.9861, -173.796, 150.773, -54.7449, 173.445, 138.014, -67.5036, \ +-102.935, 125.255, -80.2624, 147.927, 64.3067, 125.255, -80.2624, \ +147.927], 'insideEdgeIndices' : [[1, 4], [2, 4], [3, 9], [4, 7], [6, \ +7], [6, 9], [6, 11], [6, 14], [7, 11], [7, 13], [7, 16], [8, 13], [9, \ +14], [11, 16], [13, 17], [15, 16], [15, 19], [16, 19], [19, 21], [21, \ +22], [21, 23], [22, 23], [23, 24]]}, 'TriakisIcosahedron' : \ +{'edgeCoordinates' : [[0., 1.50693], [8.8695, 1.50693], [8.8695, \ +4.52078], [4.43475, 3.01385], [4.43475, 0.], [5.29656, 3.52109], \ +[5.29656, 2.50661], [6.15836, 3.01385], [6.15836, 0.], [4.38446, \ +3.01385], [4.38446, 0.], [7.95741, 2.01417], [7.95741, 0.999684], \ +[7.07046, 3.52109], [7.07046, 2.50661], [6.18351, 2.01417], [6.18351, \ +0.999684], [9.70616, 3.01385], [4.40961, 2.01417], [4.40961, \ +0.999684], [8.81921, 1.50693], [8.81921, 4.52078], [3.52265, \ +3.52109], [3.52265, 2.50661], [7.04531, 1.50693], [7.04531, 4.52078], \ +[2.6357, 2.01417], [2.6357, 0.999684], [5.27141, 1.50693], [5.27141, \ +4.52078], [8.84436, 3.52109], [8.84436, 2.50661], [0.88695, 3.01385], \ +[0.88695, 0.], [1.7739, 1.50693], [1.7739, 4.52078], [3.5478, \ +1.50693], [3.5478, 4.52078], [5.3217, 1.50693], [5.3217, 4.52078], \ +[7.0956, 1.50693], [7.0956, 4.52078], [7.93226, 3.01385], [7.93226, \ +0.], [1.74875, 3.52109], [1.74875, 2.50661], [2.61056, 3.01385], \ +[2.61056, 0.], [0.861803, 2.01417], [0.861803, 0.999684], [1.72361, \ +1.50693], [1.72361, 4.52078], [3.49751, 1.50693], [3.49751, 4.52078], \ +[0.836656, 3.01385], [0.836656, 0.], [7.98255, 3.01385], [7.98255, \ +0.], [6.20865, 3.01385], [6.20865, 0.], [2.66085, 3.01385], [2.66085, \ +0.]], 'outsideEdgeIndices' : [[1, 55], [55, 49], [49, 33], [33, 52], \ +[52, 45], [45, 36], [36, 47], [47, 27], [27, 61], [61, 54], [54, 23], \ +[23, 38], [38, 10], [10, 19], [19, 4], [4, 30], [30, 6], [6, 40], \ +[40, 8], [8, 16], [16, 59], [59, 26], [26, 14], [14, 42], [42, 43], \ +[43, 12], [12, 57], [57, 22], [22, 31], [31, 3], [3, 18], [18, 2], \ +[2, 32], [32, 21], [21, 58], [58, 13], [13, 44], [44, 41], [41, 15], \ +[15, 25], [25, 60], [60, 17], [17, 9], [9, 39], [39, 7], [7, 29], \ +[29, 5], [5, 20], [20, 11], [11, 37], [37, 24], [24, 53], [53, 62], \ +[62, 28], [28, 48], [48, 35], [35, 46], [46, 51], [51, 34], [34, 50], \ +[50, 56], [56, 1]], 'outsideEdgeDegrees' : [60.9606, -88.559, 88.559, \ +60.9606, -88.559, 88.559, -60.9606, -88.559, 88.559, 60.9606, \ +-88.559, 88.559, -60.9606, -88.559, 88.559, 60.9606, -88.559, 88.559, \ +-60.9606, -88.559, 88.559, 60.9606, -88.559, 88.559, -60.9606, \ +-88.559, 88.559, 60.9606, -88.559, 88.559, -60.9606, -119.039, \ +91.441, -91.441, -119.039, 91.441, -91.441, 119.039, 91.441, -91.441, \ +-119.039, 91.441, -91.441, 119.039, 91.441, -91.441, -119.039, \ +91.441, -91.441, 119.039, 91.441, -91.441, -119.039, 91.441, -91.441, \ +119.039, 91.441, -91.441, -119.039, 91.441, -91.441, 119.039], \ +'insideEdgeIndices' : [[1, 49], [1, 50], [1, 51], [4, 6], [4, 7], [4, \ +8], [4, 29], [6, 8], [7, 8], [8, 39], [10, 23], [10, 24], [10, 37], \ +[10, 61], [12, 21], [12, 41], [13, 21], [13, 41], [14, 43], [14, 59], \ +[15, 43], [15, 59], [16, 25], [16, 39], [17, 25], [17, 39], [18, 31], \ +[18, 32], [18, 57], [19, 29], [19, 37], [20, 29], [20, 37], [21, 41], \ +[21, 57], [23, 61], [24, 61], [25, 39], [25, 59], [27, 35], [27, 53], \ +[28, 35], [28, 53], [29, 37], [31, 57], [32, 57], [33, 45], [33, 46], \ +[33, 47], [33, 51], [35, 47], [35, 53], [41, 43], [43, 59], [45, 47], \ +[46, 47], [49, 51], [50, 51], [53, 61]]}, 'TriakisTetrahedron' : \ +{'edgeCoordinates' : [[0., 0.606607], [0.977366, 0.818161], [1.01852, \ +2.14208], [1.16255, 1.80087], [1.55235, 0.], [1.8107, 0.265391], \ +[1.99588, 1.24809], [1.99588, 2.35364], [2.18107, 0.265391], \ +[2.18107, 3.33634], [2.82922, 1.80087], [3.0144, 0.818161], [3.19959, \ +1.80087], [3.84774, 0.265391]], 'outsideEdgeIndices' : [[1, 4], [4, \ +8], [8, 3], [3, 10], [10, 11], [11, 12], [12, 13], [13, 14], [14, 9], \ +[9, 7], [7, 6], [6, 2], [2, 5], [5, 1]], 'outsideEdgeDegrees' : \ +[45.7708, 33.5573, -167.787, 45.7708, -67.1146, -79.3281, 79.3281, \ +-67.1146, 180., 100.672, -100.672, 146.443, -54.9012, 158.656], \ +'insideEdgeIndices' : [[1, 2], [2, 4], [4, 6], [4, 7], [4, 11], [7, \ +11], [8, 10], [8, 11], [9, 11], [9, 12], [12, 14]]}, \ +'MathematicaPolyhedron' : {'edgeCoordinates' : [[0., 0.5], [0., 1.5], \ +[0., 2.5], [0., 3.5], [0.866025, 1.], [0.866025, 2.], [0.866025, 3.], \ +[1.73205, 0.5], [1.73205, 1.5], [1.73205, 2.5], [2.59808, 0.], \ +[2.59808, 1.], [2.59808, 2.], [2.59808, 3.], [2.59808, 4.], [2.59808, \ +5.], [3.4641, 2.5], [3.4641, 3.5], [3.4641, 4.5], [4.33013, 2.], \ +[4.33013, 3.], [4.33013, 4.], [5.19615, 1.5], [5.19615, 2.5], \ +[5.19615, 3.5], [5.19615, 4.5], [5.19615, 5.5], [5.19615, 6.5], \ +[6.06218, 4.], [6.06218, 5.], [6.06218, 6.], [6.9282, 3.5], [6.9282, \ +4.5], [6.9282, 5.5], [7.79423, 3.], [7.79423, 4.], [7.79423, 5.], \ +[7.79423, 6.], [7.79423, 7.], [7.79423, 8.], [8.66025, 5.5], \ +[8.66025, 6.5], [8.66025, 7.5], [9.52628, 5.], [9.52628, 6.], \ +[9.52628, 7.], [10.3923, 4.5], [10.3923, 5.5], [10.3923, 6.5], \ +[10.3923, 7.5], [10.3923, 8.5], [10.3923, 9.5], [11.2583, 7.], \ +[11.2583, 8.], [11.2583, 9.], [12.1244, 6.5], [12.1244, 7.5], \ +[12.1244, 8.5], [12.9904, 6.], [12.9904, 7.], [12.9904, 8.], \ +[12.9904, 9.]], 'outsideEdgeIndices' : [[1, 2], [2, 3], [3, 4], [4, \ +7], [7, 6], [6, 10], [10, 14], [14, 15], [15, 16], [16, 19], [19, \ +18], [18, 22], [22, 26], [26, 27], [27, 28], [28, 31], [31, 30], [30, \ +34], [34, 38], [38, 39], [39, 40], [40, 43], [43, 42], [42, 46], [46, \ +50], [50, 51], [51, 52], [52, 55], [55, 54], [54, 58], [58, 62], [62, \ +61], [61, 60], [60, 59], [59, 56], [56, 57], [57, 53], [53, 49], [49, \ +48], [48, 47], [47, 44], [44, 45], [45, 41], [41, 37], [37, 36], [36, \ +35], [35, 32], [32, 33], [33, 29], [29, 25], [25, 24], [24, 23], [23, \ +20], [20, 21], [21, 17], [17, 13], [13, 12], [12, 11], [11, 8], [8, \ +9], [9, 5], [5, 1]], 'outsideEdgeDegrees' : [90., 90., 90., -30., \ +-90., 30., 30., 90., 90., -30., -90., 30., 30., 90., 90., -30., -90., \ +30., 30., 90., 90., -30., -90., 30., 30., 90., 90., -30., -90., 30., \ +30., -90., -90., -90., 150., 90., -150., -150., -90., -90., 150., \ +90., -150., -150., -90., -90., 150., 90., -150., -150., -90., -90., \ +150., 90., -150., -150., -90., -90., 150., 90., -150., -150.], \ +'insideEdgeIndices' : [[2, 5], [2, 6], [3, 6], [3, 7], [5, 6], [6, \ +9], [8, 12], [9, 10], [9, 12], [9, 13], [10, 13], [13, 14], [14, 17], \ +[14, 18], [15, 18], [15, 19], [17, 18], [18, 21], [20, 24], [21, 22], \ +[21, 24], [21, 25], [22, 25], [25, 26], [26, 29], [26, 30], [27, 30], \ +[27, 31], [29, 30], [30, 33], [32, 36], [33, 34], [33, 36], [33, 37], \ +[34, 37], [37, 38], [38, 41], [38, 42], [39, 42], [39, 43], [41, 42], \ +[42, 45], [44, 48], [45, 46], [45, 48], [45, 49], [46, 49], [49, 50], \ +[50, 53], [50, 54], [51, 54], [51, 55], [53, 54], [54, 57], [56, 60], \ +[57, 58], [57, 60], [57, 61], [58, 61]]}, 'ElongatedDodecahedron' : \ +{'edgeCoordinates' : [[1.13364, 0.963525], [1.13364, 2.96353], \ +[0.267617, 1.46353], [0.267617, 2.46353], [1.99967, 1.46353], \ +[1.99967, 2.46353], [2.86569, 0.963525], [2.86569, 2.96353], \ +[3.73172, 1.46353], [3.73172, 2.46353], [4.59774, 0.963525], \ +[4.59774, 2.96353], [5.46377, 1.46353], [5.46377, 2.46353], [6.32979, \ +0.963525], [6.32979, 2.96353], [7.19582, 1.46353], [7.19582, \ +2.46353], [6.06218, 3.92705], [5.19615, 3.42705], [4.33013, 3.92705], \ +[3.4641, 3.42705], [2.59808, 3.92705], [1.73205, 3.42705], [1.40126, \ +0.], [0., 3.42705], [2.26728, 0.5], [3.13331, 0.], [3.99933, 0.5], \ +[4.86536, 0.], [5.73139, 0.5], [6.59741, 0.], [7.46344, 0.5], \ +[0.866025, 3.92705]], 'outsideEdgeIndices' : [[26, 34], [34, 2], [2, \ +6], [6, 24], [24, 23], [23, 8], [8, 10], [10, 22], [22, 21], [21, \ +12], [12, 14], [14, 20], [20, 19], [19, 16], [16, 18], [18, 17], [17, \ +33], [33, 32], [32, 15], [15, 13], [13, 31], [31, 30], [30, 11], [11, \ +9], [9, 29], [29, 28], [28, 7], [7, 5], [5, 27], [27, 25], [25, 1], \ +[1, 3], [3, 4], [4, 26]], 'outsideEdgeDegrees' : [30., -74.4775, \ +-30., 105.522, 30., -74.4775, -30., 105.522, 30., -74.4775, -30., \ +105.522, 30., -74.4775, -30., -90., -74.4775, -150., 105.522, 150., \ +-74.4775, -150., 105.522, 150., -74.4775, -150., 105.522, 150., \ +-74.4775, -150., 105.522, 150., 90., 105.522], 'insideEdgeIndices' : \ +[[1, 5], [2, 4], [5, 6], [6, 8], [7, 9], [9, 10], [10, 12], [11, 13], \ +[13, 14], [14, 16], [15, 17]]}} + +# for poly in polyhedronData.keys(): + # print poly + # print '<_option value="' + poly + '">' + poly + '' diff --git a/extensions/fablabchemnitz/random_line/meta.json b/extensions/fablabchemnitz/random_line/meta.json new file mode 100644 index 0000000..e8fbd7c --- /dev/null +++ b/extensions/fablabchemnitz/random_line/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Random Line", + "id": "fablabchemnitz.de.random_line", + "path": "random_line", + "dependent_extensions": null, + "original_name": "Random Line", + "original_id": "org.inkscape.render.randomline", + "license": "GNU GPL v3", + "license_url": "https://github.com/opensourcebear/inkscape-extension-random-line/blob/main/LICENSE", + "comment": "", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/random_line", + "fork_url": "https://github.com/opensourcebear/inkscape-extension-random-line", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Random+Line", + "inkscape_gallery_url": null, + "main_authors": [ + "github.com/opensourcebear", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/random_line/random_line.inx b/extensions/fablabchemnitz/random_line/random_line.inx new file mode 100644 index 0000000..4fe2594 --- /dev/null +++ b/extensions/fablabchemnitz/random_line/random_line.inx @@ -0,0 +1,20 @@ + + + Random Line + fablabchemnitz.de.random_line + 120 + 120 + 25 + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/random_line/random_line.py b/extensions/fablabchemnitz/random_line/random_line.py new file mode 100644 index 0000000..b8f15d5 --- /dev/null +++ b/extensions/fablabchemnitz/random_line/random_line.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +## +# Copyright (C) 2021 Reginald Waters opensourcebear@nthebare.com +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# +# 5/19/2021 - v.01 +# Ideas: +# 1. If we move randist out of next nextmove(), can we re-used that code? +# 2. Should we allow users to specify the max line length drawn? +# 3. Should we provide an option to draw past the height/width boundry? + +import inkex +import random + +from inkex import turtle as pturtle + +class RandomLine(inkex.GenerateExtension): + + container_label = 'Random Line' + + def add_arguments(self, pars): + pars.add_argument("--height", type=int, default=300, help="Shape Height") + pars.add_argument("--width", type=int, default=300, help="Shape Width") + pars.add_argument("--seg_count", type=int, default=10, help="Number of line segments") + + def generate(self): + # Let's simplify the variable names + ht = int(self.options.height) + wt = int(self.options.width) + sc = int(self.options.seg_count) + cp = self.svg.namedview.center # center point + maxx = cp[0] + (wt / 2) + maxy = cp[0] + (ht / 2) + minx = cp[1] - (wt / 2) + miny = cp[1] - (ht / 2) + + # We need to decide what the maximum length we can draw a line + # maxlen is used to seed the random length of the line + # It may still be too long, but we'll check on that later! + if ht > wt: + maxlen = ht + else: + maxlen = wt + + style = inkex.Style({ + 'stroke-linejoin': 'miter', 'stroke-width': str(self.svg.unittouu('1px')), + 'stroke-opacity': '1.0', 'fill-opacity': '1.0', + 'stroke': '#000000', 'stroke-linecap': 'butt', + 'fill': 'none' + }) + + tur = pturtle.pTurtle() + tur.pu() # Pen up + tur.setpos(cp) # position to center of window + + def nextmove(maxlen): + randdist = random.randint(0, maxlen) # how far should we draw? + tur.forward(randdist) # Let's move the new distance + newpos = tur.getpos() # Did we go to far? + return newpos, randdist + + while sc > 0: + turpos = tur.getpos() # where are we? + + randangle = random.randint(1, 360) # Let's Pick a new direction + tur.rt(randangle) # Let's turn to face that new direction + + tur.pu() # We don't want to draw just yet, so pick up the pen/pencil + + newpos, randdist = nextmove(maxlen) # If we make this move will we go out of bounds? + + while newpos[0] > maxx or newpos[0] < minx or newpos[1] < miny or newpos[1] > maxy: + tur.setpos(turpos) + newpos, randdist = nextmove(maxlen) + + # If it all tests ok, we reset the position + # and draw the line for real! + tur.setpos(turpos) + tur.pd() + tur.forward(randdist) + + + sc = sc - 1 + + return inkex.PathElement(d=tur.getPath(), style=str(style)) + +if __name__ == "__main__": + RandomLine().run() diff --git a/extensions/fablabchemnitz/ratchet/meta.json b/extensions/fablabchemnitz/ratchet/meta.json new file mode 100644 index 0000000..d537428 --- /dev/null +++ b/extensions/fablabchemnitz/ratchet/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Ratchet", + "id": "fablabchemnitz.de.ratchet", + "path": "ratchet", + "dependent_extensions": null, + "original_name": "Ratchet", + "original_id": "ratchet", + "license": "GNU GPL v3", + "license_url": "https://github.com/kie27/draw_ratchet/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/ratchet", + "fork_url": "https://github.com/kie27/draw_ratchet", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Ratchet", + "inkscape_gallery_url": null, + "main_authors": [ + "github.com/kie27", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/ratchet/ratchet.inx b/extensions/fablabchemnitz/ratchet/ratchet.inx new file mode 100644 index 0000000..c8ed24c --- /dev/null +++ b/extensions/fablabchemnitz/ratchet/ratchet.inx @@ -0,0 +1,55 @@ + + + + Ratchet + fablabchemnitz.de.ratchet + 12 + true + 1.000 + 30.000 + 28.000 + + + + + + + + + + + + path + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/ratchet/ratchet.py b/extensions/fablabchemnitz/ratchet/ratchet.py new file mode 100644 index 0000000..8de9a9e --- /dev/null +++ b/extensions/fablabchemnitz/ratchet/ratchet.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2016 Kie Brooks +# Distributed under GPLv3 or later. +# draw_ratchet +# version 0.1 +# +# This file is part of draw_ratchet +# +# draw_ratchet 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 . +# +# +# description : +# draw ratchet gears to arbitrary sizes and precision +# +# I am not an engineer, I am sure this little program could be improved or made much more complex, +# it may contain horrible flaws but it fills my current need and should be easy to improve on. +# Alternatively pay me some money and I might do it for you ;P +# +# This program is used by inkscape. +# It can be found in the menu : extensions -> render -> ratchet +# see file draw_ratch.inx +# (directory ~/.config/inkscape/extensions/) + +import inkex +import math +from lxml import etree + +def draw_SVG_circle(parent, r, cx, cy, name, style): + " structure an SVG circle entity under parent " + circ_attribs = {'style': str(inkex.Style(style)), + 'cx': str(cx), 'cy': str(cy), + 'r': str(r), + inkex.addNS('label','inkscape'): name} + circle = etree.SubElement(parent, inkex.addNS('circle','svg'), circ_attribs ) + +class Ratchet(inkex.EffectExtension): + + def add_arguments(self, pars): + pars.add_argument("--centre_hole", type=inkex.Boolean, default=True, help="Show or not") + pars.add_argument("--teeth", type=int, default=12, help="Number of teeth around outside") + pars.add_argument("--centre_hole_diam", type=float, default=1, help="Dia of central hole") + pars.add_argument("--diam_in", type=float, default=28, help="Inner diamter of the Ratchet") + pars.add_argument("--diam_out", type=float, default=30, help="Outer diamter of the Ratchet") + pars.add_argument('--vtooth_shape', default='straight', help="Shape of tooth") + pars.add_argument('--htooth_shape', default='curve', help="Shape of tooth") + + def effect(self): + Line_style = {'stroke':'#000000','stroke-width':self.svg.unittouu(str(0.1) + "mm"),'fill':'none'} + # sort out the options + teeth = self.options.teeth + diam_in = self.options.diam_in + diam_out = self.options.diam_out + vtooth_shape = self.options.vtooth_shape + htooth_shape = self.options.htooth_shape + # Create group center of view + t = 'translate(%s,%s)' % (self.svg.namedview.center[0], self.svg.namedview.center[1]) + grp_attribs = {inkex.addNS('label','inkscape'):'Ratchet', 'transform':t} + grp = etree.SubElement(self.svg.get_current_layer(), 'g', grp_attribs) + # + # Central hole (at origin) + if self.options.centre_hole: + draw_SVG_circle(grp, self.options.centre_hole_diam, 0,0, 'Central_hole', Line_style) + #Polygon drawing + # + # L - line : x posn, y posn + # A - arc : radius x, radius y, x-axis rotation, large arc flag, sweep flag + # Points on the ratchet + # b + # |\ + # | \ |\ + # \ | \ | \ + # \ | \| \ + # a c \ + path = "M%s %s " %(diam_in,0) + for i in range(teeth): + angle = math.radians(i * 360/ teeth) + x_a = diam_in * math.cos(angle) + y_a = diam_in * math.sin(angle) + x_b = diam_out * math.cos(angle) + y_b = diam_out * math.sin(angle) + if vtooth_shape == "straight": + path += ' L %s %s ' %(x_b, y_b) + else: + path += " A %s,%s 0 0 0 %s %s" % (diam_in, diam_in, x_b, y_b) + # + angle_next = math.radians((i+1) * 360 / teeth) + x_c = diam_in * math.cos(angle_next) + y_c = diam_in * math.sin(angle_next) + if htooth_shape == "straight": + path += ' L %s %s ' %(x_c, y_c) + else: # Arc + path += " A %s,%s 0 0 1 %s %s" % (diam_in, diam_in, x_c, y_c) + # close path + path += 'z' + # draw it + line_attribs = {'style' : str(inkex.Style(Line_style)), inkex.addNS('label','inkscape') : 'Cone' } + line_attribs['d'] = path + etree.SubElement(grp, inkex.addNS('path','svg'), line_attribs ) + +if __name__ == "__main__": + Ratchet().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/reload/meta.json b/extensions/fablabchemnitz/reload/meta.json new file mode 100644 index 0000000..392f9db --- /dev/null +++ b/extensions/fablabchemnitz/reload/meta.json @@ -0,0 +1,20 @@ +[ + { + "name": "Reload", + "id": "fablabchemnitz.de.reload", + "path": "reload", + "dependent_extensions": null, + "original_name": "Reload", + "original_id": "fablabchemnitz.de.reload", + "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/reload", + "fork_url": null, + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Reload", + "inkscape_gallery_url": null, + "main_authors": [ + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/reload/reload.inx b/extensions/fablabchemnitz/reload/reload.inx new file mode 100644 index 0000000..6044de4 --- /dev/null +++ b/extensions/fablabchemnitz/reload/reload.inx @@ -0,0 +1,14 @@ + + + Reload drawing + fablabchemnitz.de.reload + + all + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/reload/reload.py b/extensions/fablabchemnitz/reload/reload.py new file mode 100644 index 0000000..09cac76 --- /dev/null +++ b/extensions/fablabchemnitz/reload/reload.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 + +import os +import inkex +from lxml import etree + +class Reload(inkex.EffectExtension): + + ''' + This extension gets the current doc path and reads it as doc + Then we clear the recent document from all items except basic stuff like root (svg:svg), defs and namedview + finally we overwrite all attributes from svg:svg, defs and namedview to the recent + ''' + + def effect(self): + currentDoc = self.document_path() + if currentDoc == "": + self.msg("Your document is not saved as a permanent file yet. Cannot reload.") + exit(1) + + originalRoot = self.document.getroot() + originalNamedview = self.svg.namedview + originalDefs = originalRoot.find("{http://www.w3.org/2000/svg}defs") + originalRoot.clear() #drop all children and attributes from root + + if not os.path.exists(currentDoc): + self.msg("The input file does not exist (anymore). Please check and try again.") + exit(1) + + with open(currentDoc, 'r') as stream: + try: + doc = etree.parse(stream, parser=etree.XMLParser(huge_tree=True)) + except Exception as e: + inkex.utils.debug("Malformed file: {}".format(e)) + exit(1) + + copyRoot = doc.getroot() + copyNamedview = copyRoot.find(inkex.addNS('namedview', 'sodipodi')) + copyDefs = copyRoot.find("{http://www.w3.org/2000/svg}defs") + for child in copyRoot.getchildren(): + originalRoot.append(child) + + #update all attributes in originalSVG + for copyAttrib in copyRoot.attrib: + originalRoot.attrib[copyAttrib] = copyRoot.attrib[copyAttrib] + + #update all attributes in originalNamedview + for copyAttrib in copyNamedview.attrib: + originalNamedview.attrib[copyAttrib] = copyNamedview.attrib[copyAttrib] + + #update all attributes in originalDefs + for copyAttrib in copyDefs.attrib: + originalDefs.attrib[copyAttrib] = copyDefs.attrib[copyAttrib] + +if __name__ == '__main__': + Reload().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/remove_empty_groups/meta.json b/extensions/fablabchemnitz/remove_empty_groups/meta.json new file mode 100644 index 0000000..cb48be2 --- /dev/null +++ b/extensions/fablabchemnitz/remove_empty_groups/meta.json @@ -0,0 +1,20 @@ +[ + { + "name": "Remove Empty Groups", + "id": "fablabchemnitz.de.remove_empty_groups", + "path": "remove_empty_groups", + "dependent_extensions": null, + "original_name": "Remove Empty Groups", + "original_id": "fablabchemnitz.de.remove_empty_groups", + "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/remove_empty_groups", + "fork_url": null, + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Remove+Empty+Groups", + "inkscape_gallery_url": "https://inkscape.org/de/~MarioVoigt/%E2%98%85remove-empty-groups", + "main_authors": [ + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/remove_empty_groups/remove_empty_groups.inx b/extensions/fablabchemnitz/remove_empty_groups/remove_empty_groups.inx new file mode 100644 index 0000000..82e1e25 --- /dev/null +++ b/extensions/fablabchemnitz/remove_empty_groups/remove_empty_groups.inx @@ -0,0 +1,16 @@ + + + Remove Empty Groups + fablabchemnitz.de.remove_empty_groups + + path + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/remove_empty_groups/remove_empty_groups.py b/extensions/fablabchemnitz/remove_empty_groups/remove_empty_groups.py new file mode 100644 index 0000000..a8c65c6 --- /dev/null +++ b/extensions/fablabchemnitz/remove_empty_groups/remove_empty_groups.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 + +import inkex + +""" +Extension for InkScape 1.0 + +This extension is totally minimal. It will just clean the whole document from groups without content (dangling groups). That usually happens if you have a group but remove its paths for example. The group will possibly stay in the XML tree. This also applies for layers because layers are just special types of groups. This effect applies to the whole document ONLY! + +Author: Mario Voigt / FabLab Chemnitz +Mail: mario.voigt@stadtfabrikanten.org +Date: 19.08.2020 +Last Patch: 23.04.2022 +License: GNU GPL v3 + +Thanks to Cyrille +""" + +class RemoveEmptyGroups(inkex.EffectExtension): + + def effect(self): + # gets all group elements in document, at any/all nested levels + groups = self.document.xpath('//svg:g',namespaces=inkex.NSS) + + # end if there are no groups + if len(groups) == 0: + return + + # loop through groups + for group in groups: + + # checks if item is empty leaf, and if so prune up branch + while len(group.getchildren()) == 0: + # this group is empty, delete it + parent = group.getparent() + parent.remove(group) + # see if we should delete the parent too, recursively + group = parent + +if __name__ == '__main__': + RemoveEmptyGroups().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/robot_boxes/meta.json b/extensions/fablabchemnitz/robot_boxes/meta.json new file mode 100644 index 0000000..1e873df --- /dev/null +++ b/extensions/fablabchemnitz/robot_boxes/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Robot Boxes", + "id": "fablabchemnitz.de.robot_boxes", + "path": "robot_boxes", + "dependent_extensions": null, + "original_name": "Robot Boxes", + "original_id": "robotbox", + "license": "Apache-2.0 License", + "license_url": "https://github.com/sadr0b0t/box4robot/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/robot_boxes", + "fork_url": "https://github.com/sadr0b0t/box4robot", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Robot+Boxes", + "inkscape_gallery_url": null, + "main_authors": [ + "github.com/sadr0b0t", + "github.com/eridur-de" + ] + } +] diff --git a/extensions/fablabchemnitz/robot_boxes/robot_boxes.inx b/extensions/fablabchemnitz/robot_boxes/robot_boxes.inx new file mode 100644 index 0000000..2501cb1 --- /dev/null +++ b/extensions/fablabchemnitz/robot_boxes/robot_boxes.inx @@ -0,0 +1,36 @@ + + + Robot Boxes + fablabchemnitz.de.robot_boxes + 62.0 + 38.0 + 23.0 + 1.0 + 1.0 + 5.0 + 5.0 + + + + + + + + + + + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/robot_boxes/robot_boxes.py b/extensions/fablabchemnitz/robot_boxes/robot_boxes.py new file mode 100644 index 0000000..d7e9f93 --- /dev/null +++ b/extensions/fablabchemnitz/robot_boxes/robot_boxes.py @@ -0,0 +1,357 @@ +#!/usr/bin/env python3 +''' +Draw box with given width, height and depth. + +Use "Extensions / Modify Path / Convert to dashes" to convert dashed bend lines to CNC-friendly style. + +Copyright (C) 2015 Anton Moiseev (1i7.livejournal.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 inkex +from lxml import etree +from inkex.paths import Path +from inkex.styles import Style + +def dirtyFormat(path): + return str(path).replace('[','').replace(']','').replace(',','').replace('\'','') + +class RobotBoxes(inkex.EffectExtension): + + def add_arguments(self, pars): + pars.add_argument("-x", "--width", type=float, default=62.0, help="The Box Width - in the X dimension") + pars.add_argument("-y", "--height", type=float, default=38.0, help="The Box Height - in the Y dimension") + pars.add_argument("-z", "--depth", type=float, default=23.0, help="The Box Depth - in the Z dimension") + pars.add_argument("-p", "--thickness", type=float, default=1.0, help="Paper thickness - important for thick carton") + pars.add_argument("-c", "--crampheight", type=float, default=1.0, help="Cramp ear height - render cramping ears and slots on the left and right walls (0 for no cramp)") + pars.add_argument("-d", "--dashwidth", type=float, default=5.0, help="Bend line dash width") + pars.add_argument("-s", "--dashstep", type=float, default=5.0, help="Bend line dash step") + pars.add_argument("-b", "--bendsurface", default="inner", help="Bend line surface (innder or outer) - depends on the way you will make actual bends") + pars.add_argument("-u", "--unit", default="mm", help="The unit of dimensions") + + def effect(self): + width = self.svg.unittouu( str(self.options.width) + self.options.unit ) + height = self.svg.unittouu( str(self.options.height) + self.options.unit ) + depth = self.svg.unittouu( str(self.options.depth) + self.options.unit ) + thickness = self.svg.unittouu( str(self.options.thickness) + self.options.unit ) + crampheight = self.svg.unittouu( str(self.options.crampheight) + self.options.unit ) + dashwidth = self.svg.unittouu( str(self.options.dashwidth) + self.options.unit ) + dashstep = self.svg.unittouu( str(self.options.dashstep) + self.options.unit ) + bendsurface = self.options.bendsurface + + # bend correction: it makes sense when compose the box whether the bend line would + # lay on the inner or outer surface of the thick carton + bcorr = 0 + if bendsurface == "inner": + bcorr = 0 + elif bendsurface == "outer": + bcorr = thickness + else :# "middle" + bcorr = thickness/2 + + # small ears (to be hidden inside the box borders) length + ear1 = height / 2 - bcorr*2 + + # slot width - make slots a bit thiner than ears (thickness) + slot_width_factor = 0.8 + slot_width = thickness * slot_width_factor + + # big ears skew = ~25 degrees + # skew_shift = depth*2/3 * tg(25) + skew_shift = depth*2/3 * 0.47 + + # render 2 cramps as 1/5 of box height with same (1/5 of height) step + cramp_width = height/5 + + # Generate box points + # Details on the shape here: + # https://github.com/1i7/metalrobot/blob/master/inkscape/extensions/robotbox-devel/draft1.svg + # https://github.com/1i7/metalrobot/blob/master/inkscape/extensions/robotbox-devel/draft2.svg + + # points for straight lines of the left bound + left_points = [ + # start from left bottom "ear" and go left and up + # ear 1 + 0,0, -ear1,0, + -ear1,depth, 0,depth, + # ear 2 + 0,depth+bcorr*2, -((thickness-bcorr)+slot_width+bcorr),depth+bcorr*2, -((thickness-bcorr)+slot_width+bcorr)-(depth+bcorr*2),depth+bcorr*2, + -((thickness-bcorr)+slot_width+bcorr)-(depth+bcorr*2)-(bcorr+slot_width+thickness+bcorr),depth+bcorr*2, + -((thickness-bcorr)+slot_width+bcorr)-(depth+bcorr*2)-(bcorr+slot_width+thickness+bcorr)-(bcorr+depth),depth+bcorr*2 + ] + + # render cramping ears if set + if crampheight > 0: + left_cramp_x = -((thickness-bcorr)+slot_width+bcorr)-(depth+bcorr*2)-(bcorr+slot_width+thickness+bcorr)-(bcorr+depth) + + left_points += [ + # left cramp ear1 + left_cramp_x,(depth+bcorr*2)+cramp_width, left_cramp_x-crampheight,(depth+bcorr*2)+cramp_width+thickness, + left_cramp_x-crampheight,(depth+bcorr*2)+cramp_width*2-thickness, left_cramp_x,(depth+bcorr*2)+cramp_width*2, + + # left cramp ear2 + left_cramp_x,(depth+bcorr*2)+cramp_width*3, left_cramp_x-crampheight,(depth+bcorr*2)+cramp_width*3+thickness, + left_cramp_x-crampheight,(depth+bcorr*2)+cramp_width*4-thickness, left_cramp_x,(depth+bcorr*2)+cramp_width*4 + ] + + + left_points += [ + # ear 2 finish + -((thickness-bcorr)+slot_width+bcorr)-(depth+bcorr*2)-(bcorr+slot_width+thickness+bcorr)-(bcorr+depth),(depth+bcorr*2)+height, + -((thickness-bcorr)+slot_width+bcorr)-(depth+bcorr*2)-(bcorr+slot_width+thickness+bcorr),(depth+bcorr*2)+height, + -((thickness-bcorr)+slot_width+bcorr)-(depth+bcorr*2),(depth+bcorr*2)+height, + -((thickness-bcorr)+slot_width+bcorr),(depth+bcorr*2)+height, 0,(depth+bcorr*2)+height, + # ear 3 + 0,(depth+bcorr*2)+(height+bcorr*2), -ear1,(depth+bcorr*2)+(height+bcorr*2), + -ear1,(depth+bcorr*2)+(height+bcorr*2)+depth, 0,(depth+bcorr*2)+(height+bcorr*2)+depth, bcorr+thickness+(thickness-bcorr),(depth+bcorr*2)+(height+bcorr*2)+depth, + # ear 4 + (bcorr+thickness+(thickness-bcorr)),(depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2), (bcorr+thickness+(thickness-bcorr))-depth*2/3,(depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2)+skew_shift, + (bcorr+thickness+(thickness-bcorr))-depth*2/3,(depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2)+height-skew_shift, (bcorr+thickness+(thickness-bcorr)),(depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2)+height + ] + + + # points for straight lines of the right bound + right_base_x = bcorr+thickness+width+thickness+bcorr + right_points = [ + # ear 7 + right_base_x-(bcorr+thickness+(thickness-bcorr)),(depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2)+height, right_base_x-(bcorr+thickness+(thickness-bcorr))+depth*2/3,(depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2)+height-skew_shift, + right_base_x-(bcorr+thickness+(thickness-bcorr))+depth*2/3,(depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2)+skew_shift, right_base_x-(bcorr+thickness+(thickness-bcorr)),(depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2), + # ear 8 + right_base_x-(bcorr+thickness+(thickness-bcorr)),(depth+bcorr*2)+(height+bcorr*2)+depth, right_base_x,(depth+bcorr*2)+(height+bcorr*2)+depth, right_base_x+ear1,(depth+bcorr*2)+(height+bcorr*2)+depth, + right_base_x+ear1,(depth+bcorr*2)+(height+bcorr*2), right_base_x,(depth+bcorr*2)+(height+bcorr*2), + # ear 9 + right_base_x,(depth+bcorr*2)+height, right_base_x+((thickness-bcorr)+slot_width+bcorr),(depth+bcorr*2)+height, + right_base_x+((thickness-bcorr)+slot_width+bcorr)+(depth+bcorr*2),(depth+bcorr*2)+height, + right_base_x+((thickness-bcorr)+slot_width+bcorr)+(depth+bcorr*2)+(bcorr+slot_width+thickness+bcorr),(depth+bcorr*2)+height, + right_base_x+((thickness-bcorr)+slot_width+bcorr)+(depth+bcorr*2)+(bcorr+slot_width+thickness+bcorr)+(bcorr+depth),(depth+bcorr*2)+height + ] + + # render cramping ears if set + if crampheight > 0: + right_cramp_x = right_base_x+((thickness-bcorr)+slot_width+bcorr)+(depth+bcorr*2)+(bcorr+slot_width+thickness+bcorr)+(bcorr+depth) + right_points += [ + # right cramp ear1 + right_cramp_x,(depth+bcorr*2)+height-cramp_width, right_cramp_x+crampheight,(depth+bcorr*2)+height-cramp_width-thickness, + right_cramp_x+crampheight,(depth+bcorr*2)+height-cramp_width*2+thickness, right_cramp_x,(depth+bcorr*2)+height-cramp_width*2, + + # right cramp ear2 + right_cramp_x,(depth+bcorr*2)+height-cramp_width*3, right_cramp_x+crampheight,(depth+bcorr*2)+height-cramp_width*3-thickness, + right_cramp_x+crampheight,(depth+bcorr*2)+height-cramp_width*4+thickness, right_cramp_x,(depth+bcorr*2)+height-cramp_width*4 + ] + + right_points += [ + # ear 9 finish + right_base_x+((thickness-bcorr)+slot_width+bcorr)+(depth+bcorr*2)+(bcorr+slot_width+thickness+bcorr)+(bcorr+depth),(depth+bcorr*2), + right_base_x+((thickness-bcorr)+slot_width+bcorr)+(depth+bcorr*2)+(bcorr+slot_width+thickness+bcorr),(depth+bcorr*2), + right_base_x+((thickness-bcorr)+slot_width+bcorr)+(depth+bcorr*2),(depth+bcorr*2), + right_base_x+((thickness-bcorr)+slot_width+bcorr),(depth+bcorr*2), right_base_x,(depth+bcorr*2), + # ear 10 + right_base_x,depth, right_base_x+ear1,depth, + right_base_x+ear1,0, right_base_x,0 + ] + + + bound_points = [ + [ 'M', left_points ], + # ear 5: manual shape (drawn for 62x38x23 box), converted to proportion based on depth value + # m 0,0 c -39.88719,-0.7697 -90.44391,-0.7593 -73.26685,35.3985 11.37507,22.1855 33.21015,45.182 73.26685,46.0975 z + [ 'L', [ + (bcorr+thickness+(thickness-bcorr)),(depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2)+(height+thickness+bcorr*2), + -(bcorr+(thickness-bcorr)),(depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2)+(height+thickness+bcorr*2) + ] ], + [ 'C', [ +# -39.88719, (depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2)+(height+bcorr*2)-0.7697, +# -90.44391, (depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2)+(height+bcorr*2)-0.7593, +# -73.26685, (depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2)+(height+bcorr*2)+35.3985, +# -73.26685+11.37507, (depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2)+(height+bcorr*2)+35.3985+22.1855, +# -73.26685+33.21015, (depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2)+(height+bcorr*2)+35.3985+45.182, + + -(bcorr+(thickness-bcorr))-depth/2, (depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2)+(height+thickness+bcorr*2), + -(bcorr+(thickness-bcorr))-depth/10*11, (depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2)+(height+thickness+bcorr*2), + -(bcorr+(thickness-bcorr))-depth/8*7, (depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2)+(height+thickness+bcorr*2)+depth/16*7, + -(bcorr+(thickness-bcorr))-depth/8*6, (depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2)+(height+thickness+bcorr*2)+depth/16*11, + -(bcorr+(thickness-bcorr))-depth/2, (depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2)+(height+thickness+bcorr*2)+depth, + -(bcorr+(thickness-bcorr)), (depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2)+(height+thickness+bcorr*2)+depth + ] ], + # now go to the right and go down in reverse order + # ear 6: manual shape (drawn for 62x38x23 box), converted to proportion based on depth value + [ 'L', [ + right_base_x+(bcorr+(thickness-bcorr)),(depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2)+(height+thickness+bcorr*2)+depth + ] ], + [ 'C', [ + right_base_x+(bcorr+(thickness-bcorr))+depth/2, (depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2)+(height+thickness+bcorr*2)+depth, + right_base_x+(bcorr+(thickness-bcorr))+depth/8*6, (depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2)+(height+thickness+bcorr*2)+depth/16*11, + right_base_x+(bcorr+(thickness-bcorr))+depth/8*7, (depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2)+(height+thickness+bcorr*2)+depth/16*7, + right_base_x+(bcorr+(thickness-bcorr))+depth/10*11, (depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2)+(height+thickness+bcorr*2), + right_base_x+(bcorr+(thickness-bcorr))+depth/2, (depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2)+(height+thickness+bcorr*2), + right_base_x+(bcorr+(thickness-bcorr)), (depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2)+(height+thickness+bcorr*2) + ] ], + [ 'L', [ + right_base_x-(bcorr+thickness+(thickness-bcorr)),(depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2)+(height+thickness+bcorr*2) + ] ], + # ear 7 + [ 'L', right_points ], + [ 'Z', [] ] + ] + + # render slots for cramp ears + # slot for left cramp ear1 + slot_l1 = [ [ 'M', [ + bcorr,(depth+bcorr*2)+cramp_width, bcorr+thickness,(depth+bcorr*2)+cramp_width, + bcorr+thickness,(depth+bcorr*2)+cramp_width*2, bcorr,(depth+bcorr*2)+cramp_width*2 + ] ], + [ 'Z', [] ] + ] + + # slot for left cramp ear2 + slot_l2 = [ [ 'M', [ + bcorr,(depth+bcorr*2)+cramp_width*3, bcorr+thickness,(depth+bcorr*2)+cramp_width*3, + bcorr+thickness,(depth+bcorr*2)+cramp_width*4, bcorr,(depth+bcorr*2)+cramp_width*4 + ] ], + [ 'Z', [] ] + ] + + # slot for right cramp ear1 + slot_r1 = [ [ 'M', [ + right_base_x-bcorr,(depth+bcorr*2)+height-cramp_width, right_base_x-(bcorr+thickness),(depth+bcorr*2)+height-cramp_width, + right_base_x-(bcorr+thickness),(depth+bcorr*2)+height-cramp_width*2, right_base_x-bcorr,(depth+bcorr*2)+height-cramp_width*2 + ] ], + [ 'Z', [] ] + ] + + # slot for right cramp ear2 + slot_r2 = [ [ 'M', [ + right_base_x-bcorr,(depth+bcorr*2)+height-cramp_width*3, right_base_x-(bcorr+thickness),(depth+bcorr*2)+height-cramp_width*3, + right_base_x-(bcorr+thickness),(depth+bcorr*2)+height-cramp_width*4, right_base_x-bcorr,(depth+bcorr*2)+height-cramp_width*4 + ] ], + [ 'Z', [] ] + ] + + # vertical bends + # left + # isinstance(item[1], (list, tuple)): self.append(PathCommand.letter_to_class(item[0])(*item[1])) + bend_line_vl1 = [ [ 'M', [ 0, 0, 0, depth ] ] ] + bend_line_vl2 = [ [ 'M', [ + -((thickness-bcorr)+slot_width+bcorr),(depth+bcorr*2), + -((thickness-bcorr)+slot_width+bcorr),(depth+bcorr*2)+height ] ] ] + bend_line_vl3 = [ [ 'M', [ + 0, (depth+bcorr*2)+(height+bcorr*2), + 0, (depth+bcorr*2)+(height+bcorr*2)+depth ] ] ] + bend_line_vl4 = [ [ 'M', [ + (bcorr+thickness+(thickness-bcorr)),(depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2), + (bcorr+thickness+(thickness-bcorr)),(depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2)+height ] ] ] + bend_line_vl5 = [ [ 'M', [ + -(bcorr+(thickness-bcorr)), (depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2)+(height+thickness+bcorr*2), + -(bcorr+(thickness-bcorr)), (depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2)+(height+thickness+bcorr*2)+depth ] ] ] + + + bend_line_vl6 = [ [ 'M', [ -((thickness-bcorr)+slot_width+bcorr)-(depth+bcorr*2), (depth+bcorr*2), + -((thickness-bcorr)+slot_width+bcorr)-(depth+bcorr*2), (depth+bcorr*2)+height ] ] ] + bend_line_vl7 = [ [ 'M', [ -((thickness-bcorr)+slot_width+bcorr)-(depth+bcorr*2)-(bcorr+slot_width+thickness+bcorr), (depth+bcorr*2), + -((thickness-bcorr)+slot_width+bcorr)-(depth+bcorr*2)-(bcorr+slot_width+thickness+bcorr), (depth+bcorr*2)+height ] ] ] + + # right + bend_line_vr1 = [ [ 'M', [ + right_base_x, 0, + right_base_x, depth ] ] ] + bend_line_vr2 = [ [ 'M', [ + right_base_x+((thickness-bcorr)+slot_width+bcorr),(depth+bcorr*2), + right_base_x+((thickness-bcorr)+slot_width+bcorr),(depth+bcorr*2)+height ] ] ] + bend_line_vr3 = [ [ 'M', [ + right_base_x, (depth+bcorr*2)+(height+bcorr*2), + right_base_x, (depth+bcorr*2)+(height+bcorr*2)+depth ] ] ] + bend_line_vr4 = [ [ 'M', [ + right_base_x-(bcorr+thickness+(thickness-bcorr)),(depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2), + right_base_x-(bcorr+thickness+(thickness-bcorr)),(depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2)+height ] ] ] + bend_line_vr5 = [ [ 'M', [ + right_base_x+(bcorr+(thickness-bcorr)), (depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2)+(height+thickness+bcorr*2), + right_base_x+(bcorr+(thickness-bcorr)), (depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2)+(height+thickness+bcorr*2)+depth ] ] ] + + + bend_line_vr6 = [ [ 'M', [ + right_base_x+((thickness-bcorr)+slot_width+bcorr)+(depth+bcorr*2), (depth+bcorr*2), + right_base_x+((thickness-bcorr)+slot_width+bcorr)+(depth+bcorr*2), (depth+bcorr*2)+height ] ] ] + bend_line_vr7 = [ [ 'M', [ + right_base_x+((thickness-bcorr)+slot_width+bcorr)+(depth+bcorr*2)+(bcorr+slot_width+thickness+bcorr), (depth+bcorr*2), + right_base_x+((thickness-bcorr)+slot_width+bcorr)+(depth+bcorr*2)+(bcorr+slot_width+thickness+bcorr), (depth+bcorr*2)+height ] ] ] + + # horizontal bends + bend_line_h1 = [ [ 'M', [ + 0,depth+bcorr, + right_base_x, depth+bcorr ] ] ] + bend_line_h2 = [ [ 'M', [ + 0,(depth+bcorr*2)+height+bcorr, + right_base_x, (depth+bcorr*2)+height+bcorr ] ] ] + bend_line_h3 = [ [ 'M', [ + (bcorr+thickness+(thickness-bcorr)), (depth+bcorr*2)+(height+bcorr*2)+depth+bcorr, + right_base_x-(bcorr+thickness+(thickness-bcorr)), (depth+bcorr*2)+(height+bcorr*2)+depth+bcorr ] ] ] + bend_line_h4 = [ [ 'M', [ + (bcorr+thickness+(thickness-bcorr)), (depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2)+height+thickness+bcorr, + right_base_x-(bcorr+thickness+(thickness-bcorr)), (depth+bcorr*2)+(height+bcorr*2)+(depth+bcorr*2)+height+thickness+bcorr ] ] ] + + + # Embed drawing in group to make animation easier: + # Translate group + #transform = 'translate(' + str( self.svg.namedview.center[0] ) + ',' + str( self.svg.namedview.center[1] ) + ')' + g = etree.SubElement(self.svg.get_current_layer(), 'g', {inkex.addNS('label','inkscape'):'RobotBox'}) + #g.transform = transform + + # Create SVG Path for box bounds + style = { 'stroke': '#000000', 'fill': 'none', 'stroke-width':str(self.svg.unittouu("1px")) } + etree.SubElement(g, inkex.addNS('path','svg'), {'style':str(inkex.Style(style)), 'd':dirtyFormat(bound_points)} ) + + # Create SVG paths for crmap slots if set + # render slots for cramp ears + if crampheight > 0: + etree.SubElement(g, inkex.addNS('path','svg'), {'style':str(inkex.Style(style)), 'd':dirtyFormat(slot_l1)} ) + etree.SubElement(g, inkex.addNS('path','svg'), {'style':str(inkex.Style(style)), 'd':dirtyFormat(slot_l2)} ) + etree.SubElement(g, inkex.addNS('path','svg'), {'style':str(inkex.Style(style)), 'd':dirtyFormat(slot_r1)} ) + etree.SubElement(g, inkex.addNS('path','svg'), {'style':str(inkex.Style(style)), 'd':dirtyFormat(slot_r2)} ) + + # Create SVG Paths for bend lines + # draw bend lines with blue + style = { 'stroke': '#44aaff', 'fill': 'none', 'stroke-width': str(self.svg.unittouu("1px")), + #'stroke-dasharray': str(dashwidth) + ',' + str(dashstep), + # positive dash offset moves dash backward + #'stroke-dashoffset': str(dashwidth) + } + + # left + etree.SubElement(g, inkex.addNS('path','svg'), {'style':str(inkex.Style(style)), 'd':dirtyFormat(bend_line_vl1)} ) + etree.SubElement(g, inkex.addNS('path','svg'), {'style':str(inkex.Style(style)), 'd':dirtyFormat(bend_line_vl2)} ) + etree.SubElement(g, inkex.addNS('path','svg'), {'style':str(inkex.Style(style)), 'd':dirtyFormat(bend_line_vl3)} ) + etree.SubElement(g, inkex.addNS('path','svg'), {'style':str(inkex.Style(style)), 'd':dirtyFormat(bend_line_vl4)} ) + etree.SubElement(g, inkex.addNS('path','svg'), {'style':str(inkex.Style(style)), 'd':dirtyFormat(bend_line_vl5)} ) + etree.SubElement(g, inkex.addNS('path','svg'), {'style':str(inkex.Style(style)), 'd':dirtyFormat(bend_line_vl6)} ) + etree.SubElement(g, inkex.addNS('path','svg'), {'style':str(inkex.Style(style)), 'd':dirtyFormat(bend_line_vl7)} ) + + # right + etree.SubElement(g, inkex.addNS('path','svg'), {'style':str(inkex.Style(style)), 'd':dirtyFormat(bend_line_vr1)} ) + etree.SubElement(g, inkex.addNS('path','svg'), {'style':str(inkex.Style(style)), 'd':dirtyFormat(bend_line_vr2)} ) + etree.SubElement(g, inkex.addNS('path','svg'), {'style':str(inkex.Style(style)), 'd':dirtyFormat(bend_line_vr3)} ) + etree.SubElement(g, inkex.addNS('path','svg'), {'style':str(inkex.Style(style)), 'd':dirtyFormat(bend_line_vr4)} ) + etree.SubElement(g, inkex.addNS('path','svg'), {'style':str(inkex.Style(style)), 'd':dirtyFormat(bend_line_vr5)} ) + etree.SubElement(g, inkex.addNS('path','svg'), {'style':str(inkex.Style(style)), 'd':dirtyFormat(bend_line_vr6)} ) + etree.SubElement(g, inkex.addNS('path','svg'), {'style':str(inkex.Style(style)), 'd':dirtyFormat(bend_line_vr7)} ) + + # horizontal + etree.SubElement(g, inkex.addNS('path','svg'), {'style':str(inkex.Style(style)), 'd':dirtyFormat(bend_line_h1)} ) + etree.SubElement(g, inkex.addNS('path','svg'), {'style':str(inkex.Style(style)), 'd':dirtyFormat(bend_line_h2)} ) + etree.SubElement(g, inkex.addNS('path','svg'), {'style':str(inkex.Style(style)), 'd':dirtyFormat(bend_line_h3)} ) + etree.SubElement(g, inkex.addNS('path','svg'), {'style':str(inkex.Style(style)), 'd':dirtyFormat(bend_line_h4)} ) + +if __name__ == '__main__': + RobotBoxes().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/sheet_metal_conus/meta.json b/extensions/fablabchemnitz/sheet_metal_conus/meta.json new file mode 100644 index 0000000..8b1c87a --- /dev/null +++ b/extensions/fablabchemnitz/sheet_metal_conus/meta.json @@ -0,0 +1,22 @@ +[ + { + "name": "Sheet Metal Conus", + "id": "fablabchemnitz.de.sheet_metal_conus", + "path": "sheet_metal_conus", + "dependent_extensions": null, + "original_name": "Sheet Metal Conus", + "original_id": "sheet_metal_conus", + "license": "GNU LGPL v3", + "license_url": "https://github.com/quirxi/SheetMetalCone/blob/master/LICENSE.md", + "comment": "ported to Inkscape v1 by Mario Voigt", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/sheet_metal_conus", + "fork_url": "https://github.com/quirxi/SheetMetalCone", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Sheet+Metal+Conus", + "inkscape_gallery_url": null, + "main_authors": [ + "github.com/quirxi", + "github.com/Neon22", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/sheet_metal_conus/sheet_metal_conus.inx b/extensions/fablabchemnitz/sheet_metal_conus/sheet_metal_conus.inx new file mode 100644 index 0000000..2a71878 --- /dev/null +++ b/extensions/fablabchemnitz/sheet_metal_conus/sheet_metal_conus.inx @@ -0,0 +1,32 @@ + + + Sheet Metal Conus + fablabchemnitz.de.sheet_metal_conus + + + + + + + + 300.0 + 100.0 + 200.0 + 0.4 + false + + 255 + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/sheet_metal_conus/sheet_metal_conus.py b/extensions/fablabchemnitz/sheet_metal_conus/sheet_metal_conus.py new file mode 100644 index 0000000..1c9336c --- /dev/null +++ b/extensions/fablabchemnitz/sheet_metal_conus/sheet_metal_conus.py @@ -0,0 +1,433 @@ +#!/usr/bin/env python3 + +# Distributed under the terms of the GNU Lesser General Public License v3.0 + +import math +import inkex +from copy import deepcopy +from lxml import etree +from inkex.transforms import Transform +from inkex import Color + +# Helper functions +def calc_angle_between_points(p1, p2): + xDiff = p2[0] - p1[0] + yDiff = p2[1] - p1[1] + return math.degrees(math.atan2(yDiff, xDiff)) + +def calc_dist_between_points(p1, p2): + xDiff = p2[0] - p1[0] + yDiff = p2[1] - p1[1] + return math.sqrt(yDiff*yDiff + xDiff*xDiff) + +def normalize(p1, p2): + " p1,p2 defines a vector return normalized " + xDiff = p2[0] - p1[0] + yDiff = p2[1] - p1[1] + magn = calc_dist_between_points(p1,p2) + return (xDiff/magn, yDiff/magn) + +def polar_to_cartesian(cx, cy, radius, angle_degrees): + " So we can make arcs in the 'A' svg syntax. " + angle_radians = math.radians(angle_degrees) + return [ + cx + (radius * math.cos(angle_radians)), + cy + (radius * math.sin(angle_radians)) + ] + +def point_on_circle(radius, angle): + " return xy coord of the point at distance radius from origin at angle " + x = radius * math.cos(angle) + y = radius * math.sin(angle) + return [x, y] + +class SheetMetalConus(inkex.EffectExtension): + """ Program to unfold a frustum of a cone or a cone + (if parameter diaCut=0) and generate a sheet cutting layout + or flat pattern projection that can be rolled or bend up into a (truncated) cone shape. + """ + color_marker_dim = '#703cd6' # purple + color_marker_chords = '#9d2222' # red + color_marker_base = '#36ba36' # green + # Arrowed lines + dimline_style = {'stroke' : '#000000', + 'stroke-width' : '0.75px', + 'fill' : 'none', + 'marker-start' : 'url(#ArrowDIN-start)', + 'marker-end' : 'url(#ArrowDIN-end)' } + + def add_arguments(self, pars): + pars.add_argument('-b', '--diaBase', type = float, dest = 'diaBase', default = 300.0, help = 'The diameter of the cones base.') + pars.add_argument('-c', '--diaCut', type = float, default = 100.0, help = 'The diameter of cones cut (0.0 if cone is not cut.') + pars.add_argument('-l', '--heightCone', type = float, default = 200.0, help = 'The height of the (cut) cone.') + pars.add_argument('-u', '--units', default = 'mm', help = 'The units in which the cone values are given. mm or in for real objects') + pars.add_argument('-w', '--strokeWidth', type = float, default = 0.3, help = 'The line thickness in given unit. For laser cutting it should be rather small.') + pars.add_argument('-f', '--strokeColour', type=Color, default = 255, help = 'The line colour.') + pars.add_argument('-d', '--verbose', type = inkex.Boolean, default = False, help = 'Enable verbose output of calculated parameters. Used for debugging or is someone needs the calculated values.') + + + # Marker arrows + def makeMarkerstyle(self, name, rotate): + " Markers added to defs for reuse " + defs = self.svg.getElement('/svg:svg//svg:defs') + if defs == None: + defs = etree.SubElement(self.document.getroot(),inkex.addNS('defs','svg')) + marker = etree.SubElement(defs ,inkex.addNS('marker','svg')) + marker.set('id', name) + marker.set('orient', 'auto') + marker.set('refX', '0.0') + marker.set('refY', '0.0') + marker.set('style', 'overflow:visible') + marker.set(inkex.addNS('stockid','inkscape'), name) + + arrow = etree.Element("path") + # definition of arrows in beautiful DIN-shapes: + if name.startswith('ArrowDIN-'): + if rotate: + arrow.set('d', 'M 8,0 -8,2.11 -8,-2.11 z') + else: + arrow.set('d', 'M -8,0 8,-2.11 8,2.11 z') + if name.startswith('ArrowDINout-'): + if rotate: + arrow.set('d', 'M 0,0 16,2.11 16,0.5 26,0.5 26,-0.5 16,-0.5 16,-2.11 z') + else: + arrow.set('d', 'M 0,0 -16,2.11 -16,0.5 -26,0.5 -26,-0.5 -16,-0.5 -16,-2.11 z') + arrow.set('style', 'fill:#000000;stroke:none') + marker.append(arrow) + + def set_arrow_dir(self, option, style): + if option=='inside': + # inside + self.arrowlen = 6.0 + style['marker-start'] = 'url(#ArrowDIN-start)' + style['marker-end'] = 'url(#ArrowDIN-end)' + self.makeMarkerstyle('ArrowDIN-start', False) + self.makeMarkerstyle('ArrowDIN-end', True) + else: + # outside + self.arrowlen = 0 + style['marker-start'] = 'url(#ArrowDINout-start)' + style['marker-end'] = 'url(#ArrowDINout-end)' + self.makeMarkerstyle('ArrowDINout-start', False) + self.makeMarkerstyle('ArrowDINout-end', True) + + def drawDimArc(self, start, end, radius, style, parent, gap=0, lowside=True): + " just the arrowed arc line " + angle = abs(end-start) + # inside or outside + inside = True + critical_length = 35 + dist = calc_dist_between_points(point_on_circle(radius, start), point_on_circle(radius, end)) + if angle < 45 and dist > critical_length: inside = False + # change start and end angles to make room for arrow markers + arrow_angle = math.degrees(math.sin(self.arrowlen/radius)) + if lowside: + start += arrow_angle + angle -= arrow_angle + anglefac = 1 + else: + start -= arrow_angle + angle -= arrow_angle + anglefac = -1 + + if gap == 0: + line_attribs = {'style' : str(inkex.Style(style)), + 'd' : self.build_arc(0, 0, start, angle*anglefac, radius, lowside) } + ell = etree.SubElement(parent, inkex.addNS('path','svg'), line_attribs ) + else: # leave a gap for label + gap_angle = math.degrees(math.sin(gap/radius)) + startstyle = deepcopy(style) + #startstyle['marker-start'] = None + line_attribs = {'style' : str(inkex.Style(startstyle)), + 'd' : self.build_arc(0, 0, start, angle*anglefac/2-gap_angle/2*anglefac, radius, lowside) } + ell = etree.SubElement(parent, inkex.addNS('path','svg'), line_attribs ) + endstyle = deepcopy(style) + #endstyle['marker-end'] = None + line_attribs = {'style' : str(inkex.Style(endstyle)), + 'd' : self.build_arc(0, 0, angle/2*anglefac+gap_angle/2*anglefac, angle*anglefac, radius, lowside) } + etree.SubElement(parent, inkex.addNS('path','svg'), line_attribs ) + # return pos in center of gap (or arc) + textposangle = angle/2*anglefac + return (point_on_circle(radius, math.radians(textposangle))) + + def drawDimension(self, a, b, style, parent): + " draw arrowed dimensions using markers " + # draw arrows as inside or outside dimension + critical_length = 35. + if calc_dist_between_points(a,b) > critical_length: + self.set_arrow_dir('inside', style) + else: + self.set_arrow_dir('outside', style) + attribs = {'style' : str(inkex.Style(style))} + # account for length change so arrows fit + norm = normalize(a, b) + dim_start_x = a[0] + self.arrowlen*norm[0] + dim_start_y = a[1] + self.arrowlen*norm[1] + dim_end_x = b[0] - self.arrowlen*norm[0] + dim_end_y = b[1] - self.arrowlen*norm[1] + # + attribs['d'] = 'M %f,%f %f,%f' % (dim_start_x, dim_start_y, dim_end_x, dim_end_y) + dimline = etree.SubElement(parent, inkex.addNS('path', 'svg'), attribs) + return dimline + + def calculateCone(self, dictCone): + """ Calculates all relevant values in order to construct a cone. + These values are: + - short radius + - long radius + - angle of cone layout + - chord of base diameter + - chord of cut diameter + - coordinates of points A, B, C and D + """ + dBase = dictCone['diaBase'] + dCut = dictCone['diaCut'] + hCone = dictCone['heightCone'] + base = dBase - dCut + # radius from top of cone to cut + if dCut > 0: + shortRadius = math.sqrt( dCut*dCut/4 + (dCut*hCone)/base * (dCut*hCone)/base ) + else: + shortRadius=0.0 + dictCone['shortRadius'] = shortRadius + ## radius from top of cone to base of cone + longRadius=math.sqrt( dBase*dBase/4 + (dBase*hCone)/base * (dBase*hCone)/base ) + dictCone['longRadius'] = longRadius + + ## angle of circle sector + angle=(math.pi * dBase) / longRadius + dictCone['angle'] = angle + # chord is the straight line between the 2 endpoints of an arc. + # Not used directly, but available in verbose output. + chordBase = longRadius * math.sqrt( 2* (1-math.cos(angle)) ) + dictCone['chordBase'] = chordBase + chordCut = shortRadius * math.sqrt( 2* (1-math.cos(angle)) ) + dictCone['chordCut'] = chordCut + + # calculate coordinates of points A, B, C and D + # center M is at (0,0) and points A and B are on the x-axis: + ptA = (shortRadius, 0.0) + ptB = (longRadius, 0.0) + # we can calculate points C and D with the given radii and the calculated angle + ptC=(longRadius * math.cos(angle), longRadius * math.sin(angle)) + ptD=(shortRadius * math.cos(angle), shortRadius * math.sin(angle)) + dictCone['ptA'] = ptA + dictCone['ptB'] = ptB + dictCone['ptC'] = ptC + dictCone['ptD'] = ptD + + def effect(self): + + if self.options.diaBase == self.options.diaCut: + inkex.utils.debug("Warning. Cut diameter may not be equal to base diameter.") + exit(1) + + # calc scene scale + convFactor = self.svg.unittouu("1" + self.options.units) + # Store all the relevants values in a dictionary for easy access + dictCone={'diaBase': self.options.diaBase, + 'diaCut': self.options.diaCut, + 'heightCone': self.options.heightCone } + # Get all values needed in order to draw cone layout: + self.calculateCone(dictCone) + + # Draw the cone layout: + # Make top level group + t = 'translate(%s,%s)' % (self.svg.namedview.center[0], self.svg.namedview.center[1]) + grp_attribs = {inkex.addNS('label','inkscape'):'Sheet Metal Conus Group', 'transform':t} + grp = etree.SubElement(self.svg.get_current_layer(), 'g', grp_attribs) + + linestyle = { 'stroke' : self.options.strokeColour, 'fill' : 'none', + 'stroke-width': str(self.svg.unittouu(str(self.options.strokeWidth) + self.options.units)) } + line_attribs = {'style' : str(inkex.Style(linestyle)), inkex.addNS('label','inkscape') : 'Cone' } + + # Connect the points into a single path of lines and arcs + zeroCenter=(0.0, 0.0) + angle = math.degrees(dictCone['angle']) + path = "" + path += self.build_line(dictCone['ptA'][0], dictCone['ptA'][1], dictCone['ptB'][0], dictCone['ptB'][1], convFactor) # A,B + path += " " + self.build_arc(zeroCenter[0], zeroCenter[1], 0.0, angle, dictCone.get('longRadius')*convFactor) + path += " " + self.build_line(dictCone['ptC'][0], dictCone['ptC'][1],dictCone['ptD'][0], dictCone['ptD'][1], convFactor) # C,D + path += self.build_arc(zeroCenter[0], zeroCenter[1], 0.0, angle, dictCone['shortRadius']*convFactor) + line_attribs['d'] = path + ell = etree.SubElement(grp, inkex.addNS('path','svg'), line_attribs ) + + # Draw Dimensions Markup + if self.options.verbose == True: + grp_attribs = {inkex.addNS('label','inkscape'):'markup'} + markup_group = etree.SubElement(grp, 'g', grp_attribs) + self.beVerbose(dictCone, convFactor, markup_group) + + def build_arc(self, x, y, start_angle, end_angle, radius, reverse=True, swap=False): + # Not using internal arc rep - instead construct path A in svg style directly + # so we can append lines to make single path + start = polar_to_cartesian(x, y, radius, end_angle) + end = polar_to_cartesian(x, y, radius, start_angle) + arc_flag = 0 if reverse else 1 + sweep = 0 if (end_angle-start_angle) <=180 else 1 + if swap: sweep = 1-sweep + path = 'M %s,%s' % (start[0], start[1]) + path += " A %s,%s 0 %d %d %s %s" % (radius, radius, sweep, arc_flag, end[0], end[1]) + return path + + def build_line(self, x1, y1, x2, y2, unitFactor): + path = 'M %s,%s L %s,%s' % (x1*unitFactor, y1*unitFactor, x2*unitFactor, y2*unitFactor) + return path + + def beVerbose(self, dictCone, unitFactor, parent): + """ Verbose output of calculated values. + Can be used for debugging purposes or if calculated values needed. + """ + # unpack + base_dia = dictCone['diaBase'] + cut_dia = dictCone['diaCut'] + cone_height = dictCone['heightCone'] + shortradius = dictCone['shortRadius'] + longradius = dictCone['longRadius'] + angle = dictCone['angle'] + chord_base = dictCone['chordBase'] + chord_cut = dictCone['chordCut'] + ptA = dictCone['ptA'] + ptB = dictCone['ptB'] + ptC = dictCone['ptC'] + ptD = dictCone['ptD'] + + # styles for markup + stroke_width = max(0.1, self.svg.unittouu(str(self.options.strokeWidth/2) + self.options.units)) + line_style = { 'stroke': self.color_marker_dim, 'stroke-width': str(stroke_width), 'fill':'none' } + arrow_style = self.dimline_style + font_height = min(32, max( 8, int(self.svg.unittouu(str(longradius/40) + self.options.units)))) + text_style = { 'font-size': str(font_height), + 'font-family': 'arial', + 'text-anchor': 'middle', + 'text-align': 'center', + 'fill': self.color_marker_dim } + # verbose message for debug window + msg = "Base diameter: " + str(base_dia) + "Cut diameter: " + str(cut_dia) + \ + "\nCone height: " + str(cone_height) + "\nShort radius: " + str(shortradius) + \ + "\nLong radius: " + str(longradius) + "\nAngle of circle sector: " + str(angle) + \ + " radians (= " + str(math.degrees(angle)) + " degrees)" + \ + "\nChord length of base arc: " + str(chord_base) + \ + "\nChord length of cut arc: " + str(chord_cut) + #inkex.utils.debug( msg) + + # Mark center + marker_length = max(5, longradius* unitFactor/100) + line_attribs = {'style' : str(inkex.Style(line_style)), + inkex.addNS('label','inkscape') : 'center', + 'd' : 'M -{0},-{0} L {0},{0}'.format(marker_length)} + line = etree.SubElement(parent, inkex.addNS('path','svg'), line_attribs) + line_attribs = {'style' : str(inkex.Style(line_style)), + inkex.addNS('label','inkscape') : 'center', + 'd' : 'M -{0},{0} L {0},-{0}'.format(marker_length)} + line = etree.SubElement(parent, inkex.addNS('path','svg'), line_attribs) + # Draw tick marks + line_attribs = {'style' : str(inkex.Style(line_style)), 'd' : 'M 0,-3 L 0,-30'} + line = etree.SubElement(parent, inkex.addNS('path','svg'), line_attribs) + if cut_dia != 0: + line_attribs = {'style' : str(inkex.Style(line_style)), 'd' : 'M {0},-3 L {0},-30'.format(shortradius * unitFactor)} + line = etree.SubElement(parent, inkex.addNS('path','svg'), line_attribs) + line_attribs = {'style' : str(inkex.Style(line_style)), 'd' : 'M {0},-3 L {0},-30'.format(longradius * unitFactor)} + line = etree.SubElement(parent, inkex.addNS('path','svg'), line_attribs) + # span line + arrow_style['stroke'] = self.color_marker_dim + self.drawDimension((0,-10), (shortradius * unitFactor, -10), arrow_style, parent) + self.drawDimension((shortradius * unitFactor,-10), (longradius * unitFactor, -10), arrow_style, parent) + # labels for short, long radii + if cut_dia >= 0.001: + text_atts = {'style':str(inkex.Style(text_style)), + 'x': str(shortradius*unitFactor/2), + 'y': str(-15) } + text = etree.SubElement(parent, 'text', text_atts) + text.text = "%4.3f" %(shortradius) + text_atts = {'style':str(inkex.Style(text_style)), + 'x': str((shortradius + (longradius-shortradius)/2)*unitFactor), + 'y': str(-15) } + text = etree.SubElement(parent, 'text', text_atts) + text.text = "%4.3f" %(longradius) + # Draw angle + lowside = math.degrees(angle) < 180 + value = math.degrees(angle) if lowside else 360-math.degrees(angle) + # radial limit lines + line_attribs = {'style' : str(inkex.Style(line_style)), 'd' : 'M 3,0 L %4.2f,0' % (ptA[0]*unitFactor*0.8)} + line = etree.SubElement(parent, inkex.addNS('path','svg'), line_attribs) + line_attribs = {'style' : str(inkex.Style(line_style)), 'd' : 'M %4.2f,%4.2f L %4.2f,%4.2f' % (ptD[0]*unitFactor*0.02, ptD[1]*unitFactor*0.02,ptD[0]*unitFactor*0.8, ptD[1]*unitFactor*0.8)} + line = etree.SubElement(parent, inkex.addNS('path','svg'), line_attribs) + # arc + arc_rad = ptA[0]*unitFactor*0.50 + gap = self.svg.unittouu(str(font_height*2)+"pt") + textpos = self.drawDimArc(0, value, arc_rad, arrow_style, parent, gap, lowside) + # angle label + textpos[1] += font_height/4 if lowside else font_height/2 + text_atts = {'style':str(inkex.Style(text_style)), + 'x': str(textpos[0]), + 'y': str(textpos[1]) } + text = etree.SubElement(parent, 'text', text_atts) + text.text = "%4.2f deg" %(value) + # chord lines + dash_style = deepcopy(arrow_style) + dash_style['stroke'] = self.color_marker_chords + dash_style['stroke-dasharray'] = '4, 2, 1, 2' + line = self.drawDimension((ptA[0]*unitFactor, ptA[1]*unitFactor), (ptD[0]*unitFactor, ptD[1]*unitFactor), dash_style, parent) + line = self.drawDimension((ptB[0]*unitFactor, ptB[1]*unitFactor), (ptC[0]*unitFactor, ptC[1]*unitFactor), dash_style, parent) + # chord labels + centerx = ptB[0]*unitFactor + (ptC[0]-ptB[0])*unitFactor/2 + centery = ptB[1]*unitFactor + (ptC[1]-ptB[1])*unitFactor/2 + line_angle = calc_angle_between_points(ptC, ptB) + ypos = centery+font_height+2 if line_angle<0 else centery-2 + text_style['fill'] = self.color_marker_chords + text_atts = {'style':str(inkex.Style(text_style)), + 'transform': 'rotate(%f)' % (line_angle) } + text = etree.SubElement(parent, 'text', text_atts) + scale_matrix = [[1, 0.0, centerx], [0.0, 1, ypos]] # needs cos,sin corrections + text.transform = Transform(scale_matrix) @ text.transform + text.text = "%4.2f" % (chord_base) + if cut_dia >= 0.001: + centerx = ptA[0]*unitFactor + (ptD[0]-ptA[0])*unitFactor/2 + centery = ptA[1]*unitFactor + (ptD[1]-ptA[1])*unitFactor/2 + xpos = centerx - font_height*math.sin(math.radians(abs(line_angle))) + ypos = centery-2 if line_angle>0 else centery+font_height+2 + text = etree.SubElement(parent, 'text', text_atts) + scale_matrix = [[1, 0.0, centerx], [0.0, 1, ypos]] + text.transform = Transform(scale_matrix) @ text.transform + text.text = "%4.2f" % (chord_cut) + # frustum lines + frustrum_repos = [[1, 0.0, 1], [0.0, 1, math.sqrt(pow(shortradius*unitFactor,2)-pow(cut_dia*unitFactor/2,2))]] + text_style['fill'] = self.color_marker_base + line_style['stroke'] = self.color_marker_base + arrow_style['stroke'] = self.color_marker_base + line_attribs = {'style': str(inkex.Style(line_style)), + 'd': 'M %f,%f L %f,%f %f,%f %f,%f z' %(-cut_dia/2*unitFactor,0, cut_dia/2*unitFactor,0, base_dia/2*unitFactor,cone_height*unitFactor, -base_dia/2*unitFactor,cone_height*unitFactor)} + line = etree.SubElement(parent, inkex.addNS('path','svg'), line_attribs) + line.transform = Transform(frustrum_repos) @ line.transform + # ticks + line_attribs = {'style': str(inkex.Style(line_style)), + 'd': 'M %f,%f L %f,%f' %(-(5+cut_dia/2*unitFactor),0, -(5+base_dia/2*unitFactor),0 )} + line = etree.SubElement(parent, inkex.addNS('path','svg'), line_attribs) + line.transform = Transform(frustrum_repos) @ line.transform + # + line = self.drawDimension((-base_dia/2*unitFactor,0), (-base_dia/2*unitFactor,cone_height*unitFactor), arrow_style, parent) + line.transform = Transform(frustrum_repos) @ line.transform + # frustum text + text_atts = {'style':str(inkex.Style(text_style)), + 'x': str(-(18+base_dia/2*unitFactor)), + 'y': str(cone_height*unitFactor/2) } + text = etree.SubElement(parent, 'text', text_atts) + text.text = "%4.3f" %(cone_height) + text.transform = Transform(frustrum_repos) @ text.transform + if cut_dia >= 0.001: + text_atts = {'style':str(inkex.Style(text_style)), + 'x': '0', + 'y': str(font_height) } + text = etree.SubElement(parent, 'text', text_atts) + text.text = "%4.3f" %(cut_dia) + text.transform = Transform(frustrum_repos) @ text.transform + text_atts = {'style':str(inkex.Style(text_style)), + 'x': '0', + 'y': str(cone_height*unitFactor+font_height) } + text = etree.SubElement(parent, 'text', text_atts) + text.text = "%4.3f" %(base_dia) + text.transform = Transform(frustrum_repos) @ text.transform + +if __name__ == '__main__': + SheetMetalConus().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/sheriff_star/meta.json b/extensions/fablabchemnitz/sheriff_star/meta.json new file mode 100644 index 0000000..83b5c9d --- /dev/null +++ b/extensions/fablabchemnitz/sheriff_star/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Sheriff Star", + "id": "fablabchemnitz.de.sheriff_star", + "path": "sheriff_star", + "dependent_extensions": null, + "original_name": "Sheriff Star", + "original_id": "com.kacmarcik.pathmonkey.sheriff-star", + "license": "MIT License", + "license_url": "https://github.com/garykac/pathmonkey/blob/master/LICENSE", + "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/sheriff_star", + "fork_url": "https://github.com/garykac/pathmonkey", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Sheriff+Star", + "inkscape_gallery_url": null, + "main_authors": [ + "github.com/garykac", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/sheriff_star/sheriff_star.inx b/extensions/fablabchemnitz/sheriff_star/sheriff_star.inx new file mode 100644 index 0000000..b210a26 --- /dev/null +++ b/extensions/fablabchemnitz/sheriff_star/sheriff_star.inx @@ -0,0 +1,33 @@ + + + Sheriff Star + fablabchemnitz.de.sheriff_star + + + 6 + 10 + 58 + false + + + + + + + path + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/sheriff_star/sheriff_star.py b/extensions/fablabchemnitz/sheriff_star/sheriff_star.py new file mode 100644 index 0000000..d21aab7 --- /dev/null +++ b/extensions/fablabchemnitz/sheriff_star/sheriff_star.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python3 +""" +Sheriff Star +Create n-pointed sheriff star. +""" +import inkex +from math import * +from lxml import etree + +def addPathCommand(a, cmd): + for x in cmd: + a.append(str(x)) + +class SheriffStar(inkex.EffectExtension): + + def add_arguments(self, pars): + pars.add_argument('--tab') + pars.add_argument('--points', type=int, default=5, help='Number of points (or sides)') + pars.add_argument('--star_tip_ratio', type=float, default=10, help='Star tip circle % (star tip circle radius as a percentage of the outer radius)') + pars.add_argument('--inner_ratio', type=float, default=58, help='Inner circle % (inner radius as a percentage of the outer radius)') + pars.add_argument('--show_inner_circle', type=inkex.Boolean, default=False, help='Show inner circle') + + def effect(self): + layer = self.svg.get_current_layer(); + + if len(self.svg.selected) == 0: + inkex.errormsg('Please select a circle or ellipse.') + exit() + + numValid = 0 + for id, obj in self.svg.selected.items(): + cx,cy, rx,ry = 0,0, 0,0 + style = '' + isValid = False + if obj.tag == inkex.addNS('circle','svg'): + isValid = True + cx = float(obj.get('cx')) + cy = float(obj.get('cy')) + rx = float(obj.get('r')) + ry = rx + elif obj.tag == inkex.addNS('ellipse', 'svg'): + isValid = True + cx = float(obj.get('cx')) + cy = float(obj.get('cy')) + rx = float(obj.get('rx')) + ry = float(obj.get('ry')) + elif obj.tag == inkex.addNS('path', 'svg'): + if obj.get(inkex.addNS('type', 'sodipodi')) == 'arc': + isValid = True + cx = float(obj.get(inkex.addNS('cx', 'sodipodi'))) + cy = float(obj.get(inkex.addNS('cy', 'sodipodi'))) + rx = float(obj.get(inkex.addNS('rx', 'sodipodi'))) + ry = float(obj.get(inkex.addNS('ry', 'sodipodi'))) + + if not isValid: + continue; + + numValid += 1 + style = obj.get('style') + transform = obj.get('transform') + isEllipse = False + if rx != ry: + isEllipse = True + + skip = 1 + sides = self.options.points + innerRatio = float(self.options.inner_ratio) / 100.0 + starTipRatio = float(self.options.star_tip_ratio) / 100.0 + showInnerCircle = self.options.show_inner_circle + + if showInnerCircle: + if not isEllipse: + cin =etree.SubElement(layer, inkex.addNS('circle','svg')) + cin.set('r', str(rx * innerRatio)) + else: + cin =etree.SubElement(layer, inkex.addNS('ellipse','svg')) + cin.set('rx', str(rx * innerRatio)) + cin.set('ry', str(ry * innerRatio)) + cin.set('cx', str(cx)) + cin.set('cy', str(cy)) + cin.set('style', style) + if transform: + cin.set('transform', transform) + + tau = 2*pi + origin = -(tau / 4) + out_pts = [] + in_pts = [] + for i in range(sides): + # Outer points (on outer circle) + theta = (i * (tau / sides)) + px = cx + rx * cos(origin + theta) + py = cy + ry * sin(origin + theta) + out_pts.append([px, py]) + + # Inner points (on inner circle) + theta = ((i + (skip / 2.0)) * (tau / sides)) + px = cx + rx * innerRatio * cos(origin + theta) + py = cy + ry * innerRatio * sin(origin + theta) + in_pts.append([px, py]) + + # Add circles at each star tip. + for pt in out_pts: + cin =etree.SubElement(layer, inkex.addNS('circle','svg')) + cin.set('r', str(rx * starTipRatio)) + cin.set('cx', str(pt[0])) + cin.set('cy', str(pt[1])) + cin.set('style', style) + if transform: + cin.set('transform', transform) + + pts = [] + pt_done = {} + for i in range(sides): + if i in pt_done: + continue; + + p1 = out_pts[i] + addPathCommand(pts, ['M', p1[0], p1[1]]) + + pt_done[i] = True + start_index = i + curr = start_index + next = (curr + skip) % sides + while next != start_index: + p = out_pts[next] + pt_done[next] = True + + addPathCommand(pts, ['L', in_pts[curr][0], in_pts[curr][1]]) + addPathCommand(pts, ['L', p[0], p[1]]) + + curr = next + next = (curr + skip) % sides + addPathCommand(pts, ['L', in_pts[curr][0], in_pts[curr][1]]) + addPathCommand(pts, ['z']) + + # Create star polygon as a single path. + l1 =etree.SubElement(layer, inkex.addNS('path','svg')) + l1.set('style', style) + if transform: + l1.set('transform', transform) + l1.set('d', ' '.join(pts)) + + if numValid == 0: + inkex.errormsg('Selection must contain a circle or ellipse.') + +if __name__ == '__main__': + SheriffStar().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/simple_registration/meta.json b/extensions/fablabchemnitz/simple_registration/meta.json new file mode 100644 index 0000000..0f1e74d --- /dev/null +++ b/extensions/fablabchemnitz/simple_registration/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Simple Registration", + "id": "fablabchemnitz.de.simple_registration", + "path": "simple_registration", + "dependent_extensions": null, + "original_name": "Simple Registration", + "original_id": "org.inkscape.inklinea.simple_registration", + "license": "GNU GPL v3", + "license_url": "https://gitlab.com/inklinea/simple-registration/-/blob/main/LICENSE", + "comment": "", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/simple_registration", + "fork_url": "https://gitlab.com/inklinea/simple-registration", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Simple+Registration", + "inkscape_gallery_url": null, + "main_authors": [ + "gitlab.com/inklinea", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/simple_registration/simple_registration.inx b/extensions/fablabchemnitz/simple_registration/simple_registration.inx new file mode 100644 index 0000000..d6f11f3 --- /dev/null +++ b/extensions/fablabchemnitz/simple_registration/simple_registration.inx @@ -0,0 +1,77 @@ + + + Simple Registration + fablabchemnitz.de.simple_registration + + + 0xff0000ff + + + + + + + + 1 + 10 + 10 + true + + + 0x000000ff + false + true + 2 + + + + + + + + + + + + 20 + + + + + true + true + + + true + true + + + + + + + + + + + + path + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/simple_registration/simple_registration.py b/extensions/fablabchemnitz/simple_registration/simple_registration.py new file mode 100644 index 0000000..df41b73 --- /dev/null +++ b/extensions/fablabchemnitz/simple_registration/simple_registration.py @@ -0,0 +1,466 @@ +#!/usr/bin/env python3 +# +# Copyright (C) [2021] [Matt Cottam], [mpcottam@raincloud.co.uk] +# +# 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. +# + +############################################################################## +# Simple Registration - Registration Marks Across Objects +############################################################################## + +import inkex +from inkex import command, Group + +# Python Standard Library +import tempfile +from lxml import etree +import random + +unit_conversions = { + 'in': 96.0, + 'pt': 1.3333333333333333, + 'px': 1.0, + 'mm': 3.779527559055118, + 'cm': 37.79527559055118, + 'm': 3779.527559055118, + 'km': 3779527.559055118, + 'Q': 0.94488188976378, + 'pc': 16.0, + 'yd': 3456.0, + 'ft': 1152.0, + '': 1.0, # Default px +} + +crosshair_path = "m 1.0583333,3.0583334 h 4.0000001 m -2,-2.0000001 v 4.0000001 m 0.25,-2 a 0.25,0.25 0 0 1 -0.25," \ + "0.25 0.25,0.25 0 0 1 -0.25,-0.25 0.25,0.25 0 0 1 0.25,-0.25 0.25,0.25 0 0 1 0.25,0.25 z m 0.75," \ + "0 a 1,1 0 0 1 -1,1 1,1 0 0 1 -1,-1 1,1 0 0 1 1,-1 1,1 0 0 1 1,1 z m 0.5,0 a 1.5,1.5 0 0 1 -1.5," \ + "1.5 1.5,1.5 0 0 1 -1.5,-1.5 1.5,1.5 0 0 1 1.5,-1.5 1.5,1.5 0 0 1 1.5,1.5 z " + +spiral_path = "m 2.8602054,2.8871104 c 0.0099,0.108862 -0.155578,0.06596 -0.197873,0.0151 -0.11462,-0.137812 0.0156," \ + "-0.320651 0.164837,-0.376975 0.266943,-0.100751 0.549334,0.08169 0.626652,0.316559 0.113468," \ + "0.344685 -0.163181,0.687127 -0.527545,0.769055 -0.485643,0.109197 -0.954879,-0.216671 -1.055435," \ + "-0.648222 -0.126538,-0.543064 0.310663,-1.059761 0.890257,-1.161135 0.701965,-0.122775 1.363419," \ + "0.351433 1.484214,0.979887 0.142351,0.740611 -0.457969,1.433888 -1.252965,1.553212 -0.917855," \ + "0.137765 -1.7729561,-0.486089 -1.9129951,-1.311549 -0.15912096,-0.93794 0.6052021,-1.8086111 " \ + "1.6156751,-1.9452921 1.133598,-0.15333396 2.182944,0.6207011 2.341774,1.6432131 0.176331," \ + "1.135182 -0.752397,2.183635 -1.978384,2.33737 " + +star_path = "m 4.2951254,5.0575354 -1.236268,-0.682814 -1.235872,0.683612 0.235648,-1.447143 -1.0002991,-1.024496 1.3819041,-0.211573 0.617649,-1.3167881 0.618418,1.3163901 1.382026,0.210678 -0.999698,1.025144 z" + +circle_path = "m 5.0583334,3.0583334 a 2,2 0 0 1 -2,2 2,2 0 0 1 -2.0000001,-2 2,2 0 0 1 2.0000001,-2.0000001 2,2 0 0 1 2,2.0000001 z" + +square_path = "M 1.0583333,1.0583333 H 5.0583334 V 5.0583334 H 1.0583333 Z" + +chevron_path = "m 1.0583333,1.0583333 3.9999999,2 -3.9999999,1.9999999" + + +def add_reg_object(self, reg_path): + # parent = self.svg.get_current_layer() + parent = self.svg + my_reg_object = etree.SubElement(parent, inkex.addNS('path', 'svg')) + reg_path = eval(reg_path) + my_reg_object.path = reg_path + my_reg_object.attrib['id'] = 'my_reg_object' + my_reg_object.style['fill'] = 'none' + # my_reg_object.style['stroke-width'] = '0.1' + my_reg_object.style['stroke-width'] = '0.05' + my_reg_object.style['stroke'] = self.options.color_picker_reg_object + my_reg_object.transform.add_scale(self.options.reg_mark_scale) + + return my_reg_object + +def add_tick_labels(self): + my_objects = self.svg.selected.rendering_order() + parent = self.svg.get_current_layer() + + object_count = 1 + for my_object in my_objects: + text_label = etree.SubElement(parent, inkex.addNS('text', 'svg')) + text_label.text = str(object_count) + text_label.style['font-size'] = self.options.tick_text_label_font_size + text_label.attrib['id'] = 'tick_label_temp' + str(object_count) + text_label.style['text-anchor'] = 'middle' + text_label.style['dominant-baseline'] = 'middle' + + object_count += 1 + + +def create_new_group(self, prefix, mode): + group_id = str(prefix) + '_' + str(random.randrange(100000, 999999)) + new_group = self.svg.add(Group.new('#' + group_id)) + new_group.set('inkscape:groupmode', str(mode)) + new_group.attrib['id'] = group_id + + return new_group + + +def apply_translate(self, parent, my_object, my_transform): + bodge_group = create_new_group(self, 'bodge_group', 'group') + bodge_group.append(my_object) + bodge_group.transform = bodge_group.transform @ my_transform + my_inherited_object_transform = my_object.composed_transform() + parent.append(my_object) + my_object.transform = my_inherited_object_transform + bodge_group.delete() + +def random_rgb(self): + random_red = random.randrange(0, 255) + random_green = random.randrange(0, 255) + random_blue = random.randrange(0, 255) + + return f'rgb({random_red}, {random_green}, {random_blue})' + + +def query_all_bbox(self): + my_file_path = self.options.input_file + + with tempfile.NamedTemporaryFile(mode='r+', suffix='.svg') as temp_svg_file: + # Write the contents of the updated svg to a tempfile to use with command line + my_svg_string = self.svg.root.tostring().decode("utf-8") + temp_svg_file.write(my_svg_string) + temp_svg_file.read() + my_query = inkex.command.inkscape(temp_svg_file.name, '--query-all') + # Account for versions of inkey.py which return query as bytes + if type(my_query) != str: + my_query = my_query.decode("utf-8") + # --query-all produces multiline output of the following format + # path853,172.491,468.905,192.11,166.525 - as string + # ElementId, Top, Left, Width, Height + + # Make a list splitting by each new line + my_query_items = my_query.split('\n') + my_element_bbox_dict = {} + + for my_query_item in my_query_items: + # Create a comma separated list item for each line + my_element = my_query_item.split(',') + # Make a dictionary for all elements, rejected malformed elements. + if len(my_element) > 4: + my_element_bbox_dict[my_element[0]] = {} + # Create Dictionary entry in anticlockwise format + # x1 = TopLeft, x2 = BottomLeft, x3 = BottomRight, x4 = TopRight, mid_x and mid_y + + # First convert all values to float, skipping element id ( first entry ) + my_element_bbox = [float(x) for x in my_element[1:]] + + width = my_element_bbox[2] + height = my_element_bbox[3] + + x1 = my_element_bbox[0] + y1 = my_element_bbox[1] + x2 = x1 + y2 = y1 + height + x3 = x1 + width + y3 = y2 + x4 = x1 + width + y4 = y1 + mid_x = x1 + width / 2 + mid_y = y1 + height / 2 + + my_element_bbox_dict[my_element[0]].update(x1=x1, y1=y1, x2=x2, y2=y2, x3=x3, y3=y3, x4=x4, y4=y4, + mid_x=mid_x, mid_y=mid_y, width=width, height=height) + # Return dictionary + return my_element_bbox_dict + + +def reg_mark_to_corners(self, all_bbox, max_bbox, my_reg_object, my_object, cf, parent): + + reg_mark_x_shift = self.options.reg_mark_x_shift + reg_mark_y_shift = self.options.reg_mark_y_shift + + # get width and height of reg object + my_reg_object_width = all_bbox[my_reg_object.get_id()]['width'] + my_reg_object_height = all_bbox[my_reg_object.get_id()]['height'] + + my_reg_object_mid_x = all_bbox[my_reg_object.get_id()]['mid_x'] + my_reg_object_mid_y = all_bbox[my_reg_object.get_id()]['mid_y'] + + my_object_id = my_object.get_id() + + new_reg_group = create_new_group(self, 'reg_group', 'group') + # new_group.append(my_object) + + for corner_no in range(1, 5): + registration_object = my_reg_object.duplicate() + registration_object.style['stroke-width'] = '0.2' + + registration_object_original_composed_transform = registration_object.composed_transform() + + my_translate_text = str( + (max_bbox['x' + str(corner_no)] - (my_reg_object_mid_x)) / cf) + ',' + str( + (max_bbox['y' + str(corner_no)] - (my_reg_object_mid_y)) / cf) + + my_translate_text = f'Transform(\'translate({my_translate_text})\')' + + apply_translate(self, parent, registration_object, my_translate_text) + + new_reg_group.append(registration_object) + + return new_reg_group + + +def draw_ticks(self, all_bbox, my_reg_object, max_bbox,object_count, number_of_objects, my_object, cf): + parent = self.svg.get_current_layer() + + tick_color = self.options.color_picker_tick + + tick_dict = {} + + # Get the right edge of the top left registration mark + # my_element_bbox_dict[my_element[0]].update(x1=x1, y1=y1, x2=x2, y2=y2, x3=x3, y3=y3, x4=x4, y4=y4, + # mid_x=mid_x, mid_y=mid_y, width=width, height=height) + + my_reg_object_width = all_bbox[my_reg_object.get_id()]['width'] / cf + my_reg_object_height = all_bbox[my_reg_object.get_id()]['height'] / cf + # tl_re = top left right edge, tr_le = top right left edge + tl_re_x = (max_bbox['x1'] / cf + my_reg_object_width) + tl_re_y = (max_bbox['y1']) / cf + tr_le_x = (max_bbox['x4'] / cf - my_reg_object_width) + tr_le_y = (max_bbox['y4']) / cf + bl_re_x = (max_bbox['x2']) / cf + bl_re_y = (max_bbox['y2']) / cf + br_le_x = (max_bbox['x3'] / cf ) + br_le_y = (max_bbox['y3'] / cf ) + + # Get the distance between top left right edge and top right left edge + top_bar_reg_fraction = 1 # Set to 1 to prevent division by zero + left_bar_reg_fraction = 1 # Set to 1 to prevent division by zero + top_bar_length = tr_le_x - tl_re_x + + if number_of_objects > 1: + top_bar_reg_fraction = top_bar_length / (number_of_objects - 1) + + + left_bar_length = (bl_re_y - (my_reg_object_height)) - (tl_re_y + (my_reg_object_height)) + if number_of_objects > 1: + left_bar_reg_fraction = left_bar_length / (number_of_objects - 1) + + my_circle_radius = self.options.tick_circle_radius / cf + + my_circle = etree.SubElement(parent, inkex.addNS('circle', 'svg')) + # my_circle.attrib['cx'] = str(tl_re_x + my_circle_radius) + my_circle.attrib['cx'] = str(tl_re_x + (top_bar_reg_fraction * object_count)) + my_circle.attrib['cy'] = str(tl_re_y) + my_circle.attrib['r'] = str(my_circle_radius / cf) + my_circle.style['stroke'] = tick_color + if self.options.tick_color_random_checkbox == 'true': + my_circle.style['stroke'] = random_rgb(self) + + my_circle.style['stroke-width'] = '0.2' + my_circle.style['fill'] = 'none' + + if self.options.tick_top_checkbox == 'true': + object_id = my_object.get_id() + circle_top = my_circle.duplicate() + circle_top.attrib['id'] = str(object_id) + '_tick_circle_top_' + str(object_count + 1) + tick_dict['tick_top'] = circle_top + + tick_label_top = self.svg.getElementById('tick_label_temp' + str(object_count + 1)).duplicate() + tick_label_top.attrib['x'] = circle_top.attrib['cx'] + tick_label_top.attrib['y'] = circle_top.attrib['cy'] + tick_label_top.attrib['id'] = str(object_id) + '_tick_label_top_' + str(object_count + 1) + tick_dict['tick_label_top'] = tick_label_top + + if self.options.tick_left_checkbox == 'true': + circle_left = my_circle.duplicate() + circle_left.attrib['cx'] = str(bl_re_x) + circle_left.attrib['cy'] = str(tl_re_y + my_reg_object_height + (left_bar_reg_fraction * object_count)) + circle_left.attrib['id'] = str(object_id) + '_tick_circle_left_' + str(object_count + 1) + tick_dict['tick_left'] = circle_left + + tick_label_left = self.svg.getElementById('tick_label_temp' + str(object_count + 1)).duplicate() + tick_label_left.attrib['x'] = circle_left.attrib['cx'] + tick_label_left.attrib['y'] = circle_left.attrib['cy'] + tick_label_left.attrib['id'] = str(object_id) + '_tick_label_left_' + str(object_count + 1) + tick_dict['tick_label_left'] = tick_label_left + + if self.options.tick_bottom_checkbox == 'true': + circle_bottom = my_circle.duplicate() + circle_bottom.attrib['cx'] = str(tl_re_x + (top_bar_reg_fraction * object_count)) + circle_bottom.attrib['cy'] = str(bl_re_y) + circle_bottom.attrib['id'] = str(object_id) + '_tick_circle_bottom_' + str(object_count + 1) + tick_dict['tick_bottom'] = circle_bottom + + tick_label_bottom = self.svg.getElementById('tick_label_temp' + str(object_count + 1)).duplicate() + tick_label_bottom.attrib['x'] = circle_bottom.attrib['cx'] + tick_label_bottom.attrib['y'] = circle_bottom.attrib['cy'] + tick_label_bottom.attrib['id'] = str(object_id) + '_tick_label_bottom_' + str(object_count + 1) + tick_dict['tick_label_bottom'] = tick_label_bottom + + if self.options.tick_right_checkbox == 'true': + circle_right = my_circle.duplicate() + circle_right.attrib['cx'] = str(br_le_x) + circle_right.attrib['cy'] = str(br_le_y - my_reg_object_height - (left_bar_reg_fraction * object_count)) + circle_right.attrib['id'] = str(object_id) + '_tick_circle_right_' + str(object_count + 1) + tick_dict['tick_right'] = circle_right + + tick_label_right = self.svg.getElementById('tick_label_temp' + str(object_count + 1)).duplicate() + tick_label_right.attrib['x'] = circle_right.attrib['cx'] + tick_label_right.attrib['y'] = circle_right.attrib['cy'] + tick_label_right.attrib['id'] = str(object_id) + '_tick_label_right_' + str(object_count + 1) + tick_dict['tick_label_right'] = tick_label_right + + my_circle.delete() + + return tick_dict + +def get_max_bbox(self, all_bbox): + my_objects = self.svg.selected.rendering_order() + + reg_mark_x_shift = self.options.reg_mark_x_shift + reg_mark_y_shift = self.options.reg_mark_y_shift + + # Find extent of bounding box for combined selection + bbox_list_x = [] + bbox_list_y = [] + for my_object in my_objects: + my_object_id = my_object.get_id() + if my_object_id == '': + continue + # bbox_list_x.append(all_bbox[my_object_id]['x1']) + (reg_mark_x_shift * -1) + # bbox_list_x.append(all_bbox[my_object_id]['x4']) + (reg_mark_x_shift) + # bbox_list_y.append(all_bbox[my_object_id]['y1']) + (reg_mark_y_shift * -1) + # bbox_list_y.append(all_bbox[my_object_id]['y2']) + (reg_mark_y_shift) + + bbox_list_x.append(all_bbox[my_object_id]['x1'] + (reg_mark_x_shift * -1)) + bbox_list_x.append(all_bbox[my_object_id]['x4'] + (reg_mark_x_shift)) + bbox_list_y.append(all_bbox[my_object_id]['y1'] + (reg_mark_y_shift * -1)) + bbox_list_y.append(all_bbox[my_object_id]['y2'] + (reg_mark_y_shift)) + + bboxes_min_x = min(bbox_list_x) + bboxes_max_x = max(bbox_list_x) + bboxes_min_y = min(bbox_list_y) + bboxes_max_y = max(bbox_list_y) + bboxes = {'x1': bboxes_min_x, 'y1': bboxes_min_y, 'x2': bboxes_min_x, 'y2': bboxes_max_y, 'x3': bboxes_max_x, + 'y3': bboxes_max_y, 'x4': bboxes_max_x, 'y4': bboxes_min_y} + + return bboxes + + +# Create a group or layer to contain each object and marks +def group_layer_loop(self, all_bbox, max_bbox, my_reg_object, cf): + my_objects = self.svg.selected.rendering_order() + number_of_objects = len(my_objects) + + object_count = 0 + + for my_object in my_objects: + my_object_id = my_object.get_id() + master_group = create_new_group(self, my_object_id + '_group', 'group') + tick_group = create_new_group(self, my_object_id + '_ticks', 'group') + tick_labels_group = create_new_group(self, my_object_id + '_tick_labels', 'group') + new_layer = create_new_group(self, my_object_id + '_layer', 'layer') + + # Create corner reg marks + reg_mark_group = reg_mark_to_corners(self, all_bbox, max_bbox, my_reg_object, my_object, cf, master_group) + + master_group.append(reg_mark_group) + + # Create ticks + tick_dict = draw_ticks(self, all_bbox, my_reg_object, max_bbox, object_count, number_of_objects, my_object, cf) + + if 'tick_top' in tick_dict: + tick_group.append(tick_dict['tick_top']) + if 'tick_left' in tick_dict: + tick_group.append(tick_dict['tick_left']) + if 'tick_bottom' in tick_dict: + tick_group.append(tick_dict['tick_bottom']) + if 'tick_right' in tick_dict: + tick_group.append(tick_dict['tick_right']) + + if 'tick_label_top' in tick_dict: + tick_labels_group.append(tick_dict['tick_label_top']) + if 'tick_label_left' in tick_dict: + tick_labels_group.append(tick_dict['tick_label_left']) + if 'tick_label_bottom' in tick_dict: + tick_labels_group.append(tick_dict['tick_label_bottom']) + if 'tick_label_right' in tick_dict: + tick_labels_group.append(tick_dict['tick_label_right']) + + # Add object to group + master_group.append(tick_labels_group) + master_group.append(tick_group) + + if self.options.correct_layer_transform_checkbox == 'true': + my_object_composed_transform = my_object.composed_transform() + master_group.append(my_object) + my_object.transform = my_object_composed_transform + else: + master_group.append(my_object) + + # Add master group to layer for that object + new_layer.append(master_group) + + object_count += 1 + + # Remove temp text labels and reg object + my_temp_labels = self.svg.xpath("//*[contains(@id, 'tick_label_temp')]") + for item in my_temp_labels: + item.delete() + my_reg_object.delete() + +class SimpleRegistration2(inkex.EffectExtension): + + def add_arguments(self, pars): + + pars.add_argument("--simple_registration_notebook", default=0) + pars.add_argument("--color_picker_reg_object", type=inkex.colors.Color, default=0) + pars.add_argument("--reg_mark_type", default='crosshair_path') + pars.add_argument("--reg_mark_scale", type=float, default=1) + pars.add_argument("--reg_mark_x_shift", type=float, default=10) + pars.add_argument("--reg_mark_y_shift", type=float, default=10) + pars.add_argument("--correct_layer_transform_checkbox") + pars.add_argument("--color_picker_tick", type=inkex.colors.Color, default=0) + pars.add_argument("--tick_color_random_checkbox", type=str) + pars.add_argument("--tick_text_labels_checkbox", type=str) + pars.add_argument("--tick_text_label_font_size", type=float, default=0) + pars.add_argument("--tick_type", default='chevron_path') + pars.add_argument("--tick_circle_radius", type=float, default=5) + pars.add_argument("--tick_top_checkbox") + pars.add_argument("--tick_left_checkbox") + pars.add_argument("--tick_bottom_checkbox") + pars.add_argument("--tick_right_checkbox") + + def effect(self): + # Exit if nothing is selected + if len(self.svg.selected) < 1: + return + + # Get document units, and conversion factor from pixels. + found_units = self.svg.unit + # Unit conversion factor cf + cf = unit_conversions[found_units] + + # Add mark objects before command line --query-all + # This returns all bounding boxes taking stroke into account + # Also works for text bounding box, which is not possible to obtain from extension system + + my_reg_object = add_reg_object(self, self.options.reg_mark_type) + # my_side_object = add_side_object(self, self.options.tick_type) + + if self.options.tick_text_labels_checkbox == 'true': + my_labels = add_tick_labels(self) + + # Get bounding boxes for all elements + all_bbox = query_all_bbox(self) + # Get max bbox, taking into account user x and y shift + max_bbox = get_max_bbox(self, all_bbox) + + # inkex.errormsg(max_bbox) + group_layer_loop(self, all_bbox, max_bbox, my_reg_object, cf) + +if __name__ == '__main__': + SimpleRegistration2().run() diff --git a/extensions/fablabchemnitz/slider_electrodes/meta.json b/extensions/fablabchemnitz/slider_electrodes/meta.json new file mode 100644 index 0000000..c525f05 --- /dev/null +++ b/extensions/fablabchemnitz/slider_electrodes/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Slider Electrodes", + "id": "fablabchemnitz.de.slider_electrodes", + "path": "slider_electrodes", + "dependent_extensions": null, + "original_name": "Slider Electrodes", + "original_id": "org.inkscape.render.sliderelectrodes", + "license": "MIT License", + "license_url": "https://github.com/henningpohl/Inkscape-Slider-Electrode-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/slider_electrodes", + "fork_url": "https://github.com/henningpohl/Inkscape-Slider-Electrode-Generator", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Slider+Electrodes", + "inkscape_gallery_url": null, + "main_authors": [ + "github.com/henningpohl", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/slider_electrodes/slider_electrodes.inx b/extensions/fablabchemnitz/slider_electrodes/slider_electrodes.inx new file mode 100644 index 0000000..840b5b8 --- /dev/null +++ b/extensions/fablabchemnitz/slider_electrodes/slider_electrodes.inx @@ -0,0 +1,18 @@ + + + Slider Electrodes + fablabchemnitz.de.slider_electrodes + 5 + 5 + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/slider_electrodes/slider_electrodes.py b/extensions/fablabchemnitz/slider_electrodes/slider_electrodes.py new file mode 100644 index 0000000..6478101 --- /dev/null +++ b/extensions/fablabchemnitz/slider_electrodes/slider_electrodes.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +from io import StringIO +import inkex +from lxml import etree + +class SliderElectrodes(inkex.EffectExtension): + + def add_arguments(self, pars): + pars.add_argument("-c", "--count", type=int, default=5, help="Number of electrodes") + pars.add_argument("-s", "--spikes", type=int, default=5, help="Number of spikes") + + def genPathString(self, bounds, spikeWidth, first=False, last=False): + s = StringIO() + cx = bounds[0] + cy = bounds[1] + stepx = spikeWidth + stepy = (bounds[3] - bounds[1]) / (2.0 * self.options.spikes) + s.write(" M %f, %f " % (bounds[0], bounds[1])) + if first: + s.write(" L %f, %f " % (bounds[0], bounds[3])) + else: + for i in range(self.options.spikes): + s.write(" L %f, %f " % (bounds[0] + stepx, bounds[1] + (2 * i + 1) * stepy)) + s.write(" L %f, %f " % (bounds[0], bounds[1] + (2 * i + 2) * stepy)) + if last: + s.write(" L %f, %f " % (bounds[2], bounds[3])) + s.write(" L %f, %f " % (bounds[2], bounds[1])) + else: + s.write(" L %f, %f " % (bounds[2] - stepx, bounds[3])) + for i in range(self.options.spikes): + s.write(" L %f, %f " % (bounds[2], bounds[3] - (2 * i + 1) * stepy)) + s.write(" L %f, %f " % (bounds[2] - stepx, bounds[3] - (2 * i + 2) * stepy)) + s.write(" Z ") + return s.getvalue() + + def effect(self): + svg = self.document.getroot() + width = self.svg.unittouu(self.document.getroot().get('width')) + height = self.svg.unittouu(self.document.getroot().get('height')) + + group = etree.SubElement(self.svg.get_current_layer(), 'g', {inkex.addNS('label', 'inkscape') : 'Slider electrodes'}) + + eWidth = width / self.options.count + spikeWidth = 0.6 * eWidth + + for eid in range(self.options.count): + if eid == 0: + path = self.genPathString((eid * eWidth, 0, (eid + 1) * eWidth + 0.4 * spikeWidth, height), spikeWidth, first=True) + elif eid == self.options.count - 1: + path = self.genPathString((eid * eWidth - 0.4 * spikeWidth, 0, (eid + 1) * eWidth, height), spikeWidth, last=True) + else: + path = self.genPathString((eid * eWidth - 0.4 * spikeWidth, 0, (eid + 1) * eWidth + 0.4 * spikeWidth, height), spikeWidth) + e = etree.SubElement(group, inkex.addNS('path', 'svg'), {'style':str(inkex.Style({'stroke':'none','fill' :'#000000'})),'d' : path}) + +if __name__ == '__main__': + effect = SliderElectrodes().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/split_bezier/meta.json b/extensions/fablabchemnitz/split_bezier/meta.json new file mode 100644 index 0000000..87c922c --- /dev/null +++ b/extensions/fablabchemnitz/split_bezier/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Split Bezier (Subdivide Path)", + "id": "fablabchemnitz.de.split_bezier", + "path": "split_bezier", + "dependent_extensions": null, + "original_name": "Subdivide Path", + "original_id": "subdivide.svg.bezier", + "license": "GNU GPL v2", + "license_url": "https://github.com/Shriinivas/inkscapesubdivpath/blob/master/LICENSE", + "comment": "", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/split_bezier", + "fork_url": "https://github.com/Shriinivas/inkscapesubdivpath", + "documentation_url": "https://stadtfabrikanten.org/pages/viewpage.action?pageId=55018715", + "inkscape_gallery_url": null, + "main_authors": [ + "github.com/Shriinivas", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/split_bezier/split_bezier.inx b/extensions/fablabchemnitz/split_bezier/split_bezier.inx new file mode 100644 index 0000000..d8ae9b0 --- /dev/null +++ b/extensions/fablabchemnitz/split_bezier/split_bezier.inx @@ -0,0 +1,35 @@ + + + Split Bezier (Subdivide Path) + fablabchemnitz.de.split_bezier + + + 10 + + + + + + + + + + 5 + true + + + + + + + path + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/split_bezier/split_bezier.py b/extensions/fablabchemnitz/split_bezier/split_bezier.py new file mode 100644 index 0000000..016d80e --- /dev/null +++ b/extensions/fablabchemnitz/split_bezier/split_bezier.py @@ -0,0 +1,167 @@ +#!/usr/bin/env python3 + +''' +Inkscape extension to subdivide the selected bezier paths based on max length value or count + +Copyright (C) 2018 Shrinivas Kulkarni + +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. +''' + +import inkex +from inkex import bezier +from inkex.paths import CubicSuperPath +import copy +from math import ceil + +DEF_ERR_MARGIN = 0.0001 + +def getPartsFromCubicSuper(cspath): + parts = [] + for subpath in cspath: + part = [] + prevBezPt = None + for i, bezierPt in enumerate(subpath): + if(prevBezPt != None): + seg = [prevBezPt[1], prevBezPt[2], bezierPt[0], bezierPt[1]] + part.append(seg) + prevBezPt = bezierPt + parts.append(part) + return parts + +def getCubicSuperFromParts(parts): + cbsuper = [] + for part in parts: + subpath = [] + lastPt = None + pt = None + for seg in part: + if(pt == None): + ptLeft = seg[0] + pt = seg[0] + ptRight = seg[1] + subpath.append([ptLeft, pt, ptRight]) + ptLeft = seg[2] + pt = seg[3] + subpath.append([ptLeft, pt, pt]) + cbsuper.append(subpath) + return cbsuper + +def floatCmpWithMargin(float1, float2, margin = DEF_ERR_MARGIN): + return abs(float1 - float2) < margin + + +class SplitBezier(inkex.EffectExtension): + + def add_arguments(self, pars): + pars.add_argument('--maxLength', type = float, default = '10', help = 'Maximum Length of New Segments') + pars.add_argument('--unit', default = 'mm', help = 'Unit of Measurement') + pars.add_argument('--precision', type = int, default = '5', help = 'Number of significant digits') + pars.add_argument("--tab", default="sampling", help="Tab") + pars.add_argument("--separateSegs", type=inkex.Boolean, default=True) + + def effect(self): + + maxL = None + separateSegs = self.options.separateSegs + + if(self.options.unit != 'perc' and self.options.unit != 'count'): + maxL = self.options.maxLength * self.svg.unittouu('1'+self.options.unit) + + # ~ inkex.errormsg(str(maxL)) + tolerance = 10 ** (-1 * self.options.precision) + + selections = self.svg.selected + pathNodes = self.document.xpath('//svg:path',namespaces=inkex.NSS) + + paths = [(pathNode.get('id'), CubicSuperPath(pathNode.get('d'))) for pathNode in pathNodes] + + if(len(paths) > 0): + for key, cspath in paths: + parts = getPartsFromCubicSuper(cspath) + partsSplit = False + try: + for i, part in enumerate(parts): + + newSegs = [] + for j, seg in enumerate(part): + segL = bezier.bezierlength((seg[0], seg[1], seg[2], seg[3]), tolerance = tolerance) + + if(maxL != None): + divL = maxL + elif(self.options.unit == 'perc'): + divL = segL * self.options.maxLength / 100 + else: + divL = segL / ceil(self.options.maxLength) + + if(segL > divL): + + coveredL = 0 + s = seg + s1 = None + s2 = DEF_ERR_MARGIN #Just in case + + while(not floatCmpWithMargin(segL, coveredL)): + if(s == seg): + sL = segL + else: + sL = bezier.bezierlength((s[0], s[1], s[2], s[3]), tolerance = tolerance) + + if(floatCmpWithMargin(segL, coveredL + divL)): + s2 = s + break + else: + if(segL > (coveredL + divL)): + t1L = divL + else: + t1L = segL - coveredL + + t1 = bezier.beziertatlength((s[0], s[1], s[2], s[3]), l = t1L / sL , tolerance = tolerance) + s1, s2 = bezier.beziersplitatt((s[0], s[1], s[2], s[3]), t1) + coveredL += t1L + newSegs.append(s1) + s = s2 + newSegs.append(s2) + else: + newSegs.append(seg) + + if(len(newSegs) > len(part)): + parts[i] = newSegs + partsSplit = True + + if(partsSplit or separateSegs): + elem = selections[key] + if(separateSegs): + parent = elem.getparent() + idx = parent.index(elem) + parent.remove(elem) + allSegs = [seg for part in parts for seg in part] + idSuffix = 0 + for seg in allSegs: + cspath = getCubicSuperFromParts([[seg]]) + newElem = copy.copy(elem) + oldId = newElem.get('id') + newElem.set('d', CubicSuperPath(cspath)) + newElem.set('id', oldId + str(idSuffix).zfill(5)) + parent.insert(idx, newElem) + idSuffix += 1 + else: + cspath = getCubicSuperFromParts(parts) + elem.set('d', CubicSuperPath(cspath)) + except: + pass + +if __name__ == '__main__': + SplitBezier().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/sprocket/meta.json b/extensions/fablabchemnitz/sprocket/meta.json new file mode 100644 index 0000000..95067ce --- /dev/null +++ b/extensions/fablabchemnitz/sprocket/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Sprocket", + "id": "fablabchemnitz.de.sprocket", + "path": "sprocket", + "dependent_extensions": null, + "original_name": "Sprocket", + "original_id": "com.attoparsec.filter.sprockets", + "license": "GNU GPL v2", + "license_url": "https://github.com/attoparsec/inkscape-extensions/blob/master/sprockets.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/sprocket", + "fork_url": "https://github.com/attoparsec/inkscape-extensions", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Sprocket", + "inkscape_gallery_url": null, + "main_authors": [ + "github.com/attoparsec", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/sprocket/sprocket.inx b/extensions/fablabchemnitz/sprocket/sprocket.inx new file mode 100644 index 0000000..3297157 --- /dev/null +++ b/extensions/fablabchemnitz/sprocket/sprocket.inx @@ -0,0 +1,32 @@ + + + Sprocket + fablabchemnitz.de.sprocket + 24 + + + + + + + + + + + + + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/sprocket/sprocket.py b/extensions/fablabchemnitz/sprocket/sprocket.py new file mode 100644 index 0000000..9691394 --- /dev/null +++ b/extensions/fablabchemnitz/sprocket/sprocket.py @@ -0,0 +1,245 @@ +#!/usr/bin/env python3 +''' +Copyright (C) 2013 Matthew Dockrey (gfish @ cyphertext.net) + +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 + +Based on gears.py by Aaron Spike and Tavmjong Bah +''' + +import inkex +from math import * +from lxml import etree + +def rotate(p, t): + return (p[0] * cos(t) - p[1] * sin(t), p[0] * sin(t) + p[1] * cos(t)) + +def SVG_move(p, t): + pp = rotate(p, t) + return 'M ' + str(pp[0]) + ',' + str(pp[1]) + '\n' + +def SVG_line(p, t): + pp = rotate(p, t) + return 'L ' + str(pp[0]) + ',' + str(pp[1]) + '\n' + +def SVG_circle(p, r, sweep, t): + pp = rotate(p, t) + return 'A ' + str(r) + ',' + str(r) + ' 0 0,' + str(sweep) + ' ' + str(pp[0]) + ',' + str(pp[1]) + '\n' + +def SVG_curve(p, c1, c2, t): + pp = rotate(p, t) + c1p = rotate(c1, t) + c2p = rotate(c2, t) + return 'C ' + str(pp[0]) + ',' + str(pp[1]) + ' ' + str(c1p[0]) + ',' + str(c1p[1]) + ' ' + str(c2p[0]) + ',' + str(c2p[1]) + '\n' + +def SVG_curve2(p1, c11, c12, p2, c21, c22, t): + p1p = rotate(p1, t) + c11p = rotate(c11, t) + c12p = rotate(c12, t) + p2p = rotate(p2, t) + c21p = rotate(c21, t) + c22p = rotate(c22, t) + return 'C ' + str(p1p[0]) + ',' + str(p1p[1]) + ' ' + str(c11p[0]) + ',' + str(c11p[1]) + ' ' + str(c12p[0]) + ',' + str(c12p[1]) + ' ' + str(p2p[0]) + ',' + str(p2p[1]) + ' ' + str(c21p[0]) + ',' + str(c21p[1]) + ' ' + str(c22p[0]) + ',' + str(c22p[1]) + '\n' + +def SVG_close(): + return 'Z\n' + +class Sprocket(inkex.EffectExtension): + + def add_arguments(self, pars): + pars.add_argument("-t", "--teeth", type=int, default=24, help="Number of teeth") + pars.add_argument("-s", "--size", default="ANSI #40", help="Chain size (common values ANSI #35, ANSI #40, ANSI #60)") + + def get_pitch(self, size): + return self.svg.unittouu({ + 'ANSI #25': '6.35mm', + 'ANSI #35': '9.53mm', + 'ANSI #40': '12.70mm', + 'ANSI #41': '12.70mm', + 'ANSI #50': '15.88mm', + 'ANSI #60': '19.05mm', + 'ANSI #80': '25.40mm', + 'ANSI #100': '31.75mm', + 'ANSI #120': '38.10mm', + 'ANSI #140': '44.45mm', + 'ANSI #160': '50.80mm', + 'ANSI #180': '57.15mm', + 'ANSI #200': '63.50mm', + 'ANSI #240': '76.20mm' + }[size]) + + def get_roller_diameter(self, size): + return self.svg.unittouu({ + 'ANSI #25': '3.30mm', + 'ANSI #35': '5.08mm', + 'ANSI #40': '7.77mm', + 'ANSI #41': '7.92mm', + 'ANSI #50': '10.16mm', + 'ANSI #60': '11.91mm', + 'ANSI #80': '15.88mm', + 'ANSI #100': '19.05mm', + 'ANSI #120': '22.23mm', + 'ANSI #140': '25.40mm', + 'ANSI #160': '28.58mm', + 'ANSI #180': '37.08mm', + 'ANSI #200': '39.67mm', + 'ANSI #240': '47.63mm' + }[size]) + + def invertX(self, p): + return (-p[0], p[1]) + + def effect(self): + size = self.options.size + + P = self.get_pitch(size) + N = self.options.teeth + PD = P / sin(pi / N) + PR = PD / 2 + + # Equations taken from + # http://www.gearseds.com/files/design_draw_sprocket_5.pdf + # Also referenced: + # http://en.wikipedia.org/wiki/Roller_chain (of course) + # and + # Chains for Power Transmission and Material Handling: + # Design and Applications Handbook + # American Chain Association, 1982 + + Dr = self.get_roller_diameter(size) + Ds = 1.0005 * Dr + self.svg.unittouu('0.003in') + R = Ds / 2 # seating curve radius + A = radians(35 + 60 / N) + B = radians(18 - 56 / N) + ac = 0.8 * Dr + M = ac * cos(A) + T = ac * sin(A) + E = 1.3025 * Dr + self.svg.unittouu('0.0015in') # transition radius + ab = 1.4 * Dr + W = ab * cos(pi / N) + V = ab * sin(pi / N) + F = Dr * (0.8 * cos(radians(18 - 56 / N)) + 1.4 * cos(radians(17 - 64 / N)) - 1.3025) - self.svg.unittouu('0.0015in') # topping curve radius + + svg = "" + + t_inc = 2.0 * pi / float(N) + thetas = [(x * t_inc) for x in range(N)] + + for theta in thetas: + # Seating curve center + seatC = (0, -PR) + + # Transitional curve center + c = (M, -PR - T) + + # Calculate line cx, angle A from x axis + # Y = mX + b + cx_m = -tan(A) # Negative because we're in -Y space + cx_b = c[1] - cx_m * c[0] + + # Calculate intersection of cx with circle S to get point x + # http://math.stackexchange.com/questions/228841/how-do-i-calculate-the-intersections-of-a-straight-line-and-a-circle + qA = cx_m * cx_m + 1 + qB = 2 * (cx_m * cx_b - cx_m * seatC[1] - seatC[0]) + qC = seatC[1] * seatC[1] - R * R + seatC[0] * seatC[0] - 2 * cx_b * seatC[1] + cx_b * cx_b + cx_X = (-qB - sqrt(qB * qB - 4 * qA * qC)) / (2 * qA) + + # Seating curve/Transitional curve junction + x = (cx_X, cx_m * cx_X + cx_b) + + # Calculate line cy, angle B past cx + cy_m = -tan(A - B) + cy_b = c[1] - cy_m * c[0] + + # Calculate point y (E along cy from c) + # http://www.physicsforums.com/showthread.php?t=419561 + yX = c[0] - E / sqrt(1 + cy_m * cy_m) + + # Transitional curve/Tangent line junction + y = (yX, cy_m * yX + cy_b) + + # Solve for circle T with radius E which passes through x and y + # http://mathforum.org/library/drmath/view/53027.html + # http://stackoverflow.com/questions/12264841/determine-circle-center-based-on-two-points-radius-known-with-solve-optim + z = ((x[0] + y[0]) / 2, (x[1] + y[1]) / 2) + x_diff = y[0] - x[0] + y_diff = y[1] - x[1] + q = sqrt(x_diff * x_diff + y_diff * y_diff) + tX = z[0] + sqrt(E * E - (q / 2) * (q / 2)) * (x[1] - y[1]) / q + tY = z[1] + sqrt(E * E - (q / 2) * (q / 2)) * (y[0] - x[0]) / q + + # Transitional curve center + tranC = (tX, tY) + + # Tangent line -- tangent to transitional curve at point y + tanl_m = -(tranC[0] - y[0]) / (tranC[1] - y[1]) + tanl_b = -y[0] * tanl_m + y[1] + t_off = (y[0] - 10, tanl_m * (y[0] - 10) + tanl_b) + + # Topping curve center + topC = (-W, -PR + V) + + # Adjust F to force topping curve tangent to tangent line + F = abs(topC[1] - tanl_m * topC[0] - tanl_b) / sqrt(tanl_m * tanl_m + 1) * 1.0001 # Final fudge needed to overcome numerical instability + + # Find intersection point between topping curve and tangent line + ttA = tanl_m * tanl_m + 1 + ttB = 2 * (tanl_m * tanl_b - tanl_m * topC[1] - topC[0]) + ttC = topC[1] * topC[1] - F * F + topC[0] * topC[0] - 2 * tanl_b * topC[1] + tanl_b * tanl_b + tanl_X = (-ttB - sqrt(ttB * ttB - 4 * ttA * ttC)) / (2 * ttA) + + # Tagent line/Topping curve junction + tanl = (tanl_X, tanl_m * tanl_X + tanl_b) + + # Calculate tip line, angle t_inc/2 from Y axis + tip_m = -tan(pi / 2 + t_inc / 2) # Negative because we're in -Y space + tip_b = 0 + + # Calculate intersection of tip line with topping curve + tA = tip_m * tip_m + 1 + tB = 2 * (tip_m * tip_b - tip_m * topC[1] - topC[0]) + tC = topC[1] * topC[1] - F * F + topC[0] * topC[0] - 2 * tip_b * topC[1] + tip_b * tip_b + tip_X = (-tB - sqrt(tB * tB - 4 * tA * tC)) / (2 * tA) + + # Topping curve top + tip = (tip_X, tip_m * tip_X + tip_b) + + # Set initial location if needed + if (theta == 0): + svg += SVG_move(tip, theta) + + svg += SVG_circle(tanl, F, 1, theta) # Topping curve left + svg += SVG_line(y, theta) # Tangent line left + svg += SVG_circle(x, E, 0, theta) # Transitional curve left + svg += SVG_circle(self.invertX(x), R, 0, theta) # Seating curve + svg += SVG_circle(self.invertX(y), E, 0, theta) # Transitionl curve right + svg += SVG_line(self.invertX(tanl), theta) # Tangent line right + svg += SVG_circle(self.invertX(tip), F, 1, theta) # Topping curve right + + svg += SVG_close() + + # Insert as a new element + sprocket_style = { 'stroke': '#000000', + 'stroke-width': self.svg.unittouu(str(0.1) + "mm"), + 'fill': 'none' + } + g_attribs = {inkex.addNS('label','inkscape'): 'Sprocket ' + size + "-" + str(N), + 'transform': 'translate(' + str(self.svg.namedview.center[0]) + ',' + str(self.svg.namedview.center[1]) + ')', + 'style' : str(inkex.Style(sprocket_style)), + 'd' : svg } + g = etree.SubElement(self.svg.get_current_layer(), inkex.addNS('path','svg'), g_attribs) + +if __name__ == '__main__': + Sprocket().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/starshine/meta.json b/extensions/fablabchemnitz/starshine/meta.json new file mode 100644 index 0000000..1519ad8 --- /dev/null +++ b/extensions/fablabchemnitz/starshine/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Starshine", + "id": "fablabchemnitz.de.starshine", + "path": "starshine", + "dependent_extensions": null, + "original_name": "Starshine", + "original_id": "hipix.Starshine", + "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/starshine", + "fork_url": "https://github.com/curiousmaster/hipix_inkscape_extensions", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Starshine", + "inkscape_gallery_url": null, + "main_authors": [ + "github.com/curiousmaster", + "github.com/eridur-de" + ] + } +] diff --git a/extensions/fablabchemnitz/starshine/starshine.inx b/extensions/fablabchemnitz/starshine/starshine.inx new file mode 100644 index 0000000..bc482f8 --- /dev/null +++ b/extensions/fablabchemnitz/starshine/starshine.inx @@ -0,0 +1,24 @@ + + + Starshine + fablabchemnitz.de.starshine + 10 + 10 + + 350 + 350 + + 4 + 10 + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/starshine/starshine.py b/extensions/fablabchemnitz/starshine/starshine.py new file mode 100644 index 0000000..948a4dc --- /dev/null +++ b/extensions/fablabchemnitz/starshine/starshine.py @@ -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 Starshine 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 Starshine(inkex.GenerateExtension): + container_label = 'Rendered Starshine' + 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") + + + + 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 + + + # generate points: list of (x, y) pairs + + r1, r2 = checkSize(r1, r2) + R1, R2 = checkSize(R1, R2) + a1, a2 = checkSize(a1, a2) + a = 0 + + style = {'stroke': '#000000', 'fill': '#000000', 'stroke-width': str(self.svg.unittouu('1px'))} + + points = [] + + while a < 360: + dI = random.randint(r1,r2) + dO = random.randint(R1,R2) + ad = random.randint(a1,a2) + + x00 = cos(radians(a)) * dI + y00 = sin(radians(a)) * dI + + x10 = cos(radians(a+ad/2)) * dO + y10 = sin(radians(a+ad/2)) * dO + + x01 = cos(radians(a+ad)) * dI + y01 = sin(radians(a+ad)) * dI + + points.append((x00, y00)) + points.append((x10, y10)) + points.append((x01, y01)) + + a = a+ad + + path = points_to_svgd(points) + + yield inkex.PathElement(style=str(inkex.Style(style)), d=str(path)) + + +if __name__ == '__main__': + Starshine().run() + diff --git a/extensions/fablabchemnitz/sundial_declining/meta.json b/extensions/fablabchemnitz/sundial_declining/meta.json new file mode 100644 index 0000000..acb789e --- /dev/null +++ b/extensions/fablabchemnitz/sundial_declining/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Sundial Declining", + "id": "fablabchemnitz.de.sundial_declining", + "path": "sundial_declining", + "dependent_extensions": null, + "original_name": "Sundial", + "original_id": "fr.electropol.tableausimple.inkscape", + "license": "Public Domain", + "license_url": "https://inkscape.org/de/~TomasUrban/%E2%98%85sundial-declining", + "comment": "ported to Inkscape v1 by Mario Voigt", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/sundial_declining", + "fork_url": "https://inkscape.org/de/~TomasUrban/%E2%98%85sundial-declining", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Sundial+Declining", + "inkscape_gallery_url": null, + "main_authors": [ + "inkscape.org/TomasUrban", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/sundial_declining/sundial_declining.inx b/extensions/fablabchemnitz/sundial_declining/sundial_declining.inx new file mode 100644 index 0000000..4ade99b --- /dev/null +++ b/extensions/fablabchemnitz/sundial_declining/sundial_declining.inx @@ -0,0 +1,45 @@ + + + Sundial Declining + fablabchemnitz.de.sundial_declining + + + + 50 + 16 + 0 + false + + + + 40 + 40 + + 0 + + + + + + + + + + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/sundial_declining/sundial_declining.py b/extensions/fablabchemnitz/sundial_declining/sundial_declining.py new file mode 100644 index 0000000..2048bc4 --- /dev/null +++ b/extensions/fablabchemnitz/sundial_declining/sundial_declining.py @@ -0,0 +1,328 @@ +#!/usr/bin/python3 + +# -------------------------------------------------------------------------------------- +#http://astrodinamica.altervista.org/ESU/sundial/Condensed-vector-theory.pdf +#https://www.helios-sonnenuhren.de/sites/default/files/upload/the_calculation_of_declining_and_inclining_sundials_an_unusual_approach.pdf +# -------------------------------------------------------------------------------------- + +from __future__ import division +import inkex +from datetime import datetime, time, timedelta +from math import * +from lxml import etree + +class sundialDeclining(inkex.EffectExtension): + + def add_arguments(self, pars): + pars.add_argument("--latitude", type=float, dest="latitude", default="50.3515") + pars.add_argument("--longitude", type=float, dest="longitude", default="15.7512") + pars.add_argument("--timezone", type=int, dest="timezone", default="0") + pars.add_argument("--summer_time", type=inkex.Boolean, dest="summer_time", default='False') + pars.add_argument("--gnom", type=float, dest="gnom", default="30") + pars.add_argument("--decl", type=float, dest="decl", default="0") + pars.add_argument("--incl", type=float, dest="incl", default="0") + pars.add_argument("--DL", type=float, dest="DL", default="0") + pars.add_argument("--tab") + + def effect(self): + def draw_SVG_line(x1, y1, x2, y2, width, stroke, name, parent): + style = { 'stroke': stroke, 'stroke-width':str(width), 'fill': 'none' } + line_attribs = {'style':str(inkex.Style(style)), + inkex.addNS('label','inkscape'):name, + 'd':'M '+str(x1)+','+str(y1)+' L '+str(x2)+','+str(y2)} + etree.SubElement(parent, inkex.addNS('path','svg'), line_attribs ) + + def draw_SVG_circle(cx, cy, r, width, stroke, fill, name, parent): + style = { 'stroke': stroke, 'stroke-width':str(width), 'fill':fill} + circle_attribs = {'style':str(inkex.Style(style)), + inkex.addNS('label','inkscape'):name, + 'cx':str(cx), 'cy':str(cy), 'r':str(r)} + etree.SubElement(parent, inkex.addNS('circle','svg'), circle_attribs ) + + def draw_SVG_tri(x1, y1, x2, y2, x3, y3, width, stroke, name, parent): + style = { 'stroke': stroke, 'stroke-width':str(width), 'fill': 'none' } + tri_attribs = {'style':str(inkex.Style(style)), + inkex.addNS('label','inkscape'):name, + 'd':'M '+str(x1)+','+str(y1)+ + ' L '+str(x2)+','+str(y2)+ + ' L '+str(x3)+','+str(y3)+ + ' L '+str(x1)+','+str(y1)+' z'} + etree.SubElement(parent, inkex.addNS('path','svg'), tri_attribs ) + + def draw_SVG_rect(x, y, w, h, width, fill, name, parent): + style = { 'stroke': '#000000', 'stroke-width':str(width), 'fill':fill} + rect_attribs = {'style':str(inkex.Style(style)), + inkex.addNS('label','inkscape'):name, + 'x':str(x), 'y':str(y), 'width':str(w), 'height':str(h)} + etree.SubElement(parent, inkex.addNS('rect','svg'), rect_attribs ) + + def draw_SVG_polyline(DP, width, colour, fill, name, parent): + style = { 'stroke': colour, 'stroke-width': str(width), 'fill':fill} + polyline_attribs = {'style':str(inkex.Style(style)), + inkex.addNS('label','inkscape'):name, + 'd':'M' +str(DP)} + etree.SubElement(parent, inkex.addNS('path','svg'), polyline_attribs ) + + def draw_SVG_text(x, y, textvalue, font, text_size, parent): + text = etree.Element(inkex.addNS('text','svg')) + text.set('x', str(x)) + text.set('y', str(y)) + style = {'text-align' : 'center', 'font-family': str(font) ,'text-anchor': 'middle', 'alignment-baseline' : 'central', 'font-size' : str(text_size), 'vertical-align' : 'middle'} + text.set('style', str(inkex.Style(style))) + text.text = textvalue + parent.append(text) + + so = self.options + parent = self.svg.get_current_layer() + + border=5 + widthgrid = 200 + heightgrid = 150 + + # Grid + Gx=15 + Gy=9 + + # Get SVG document dimensions + svg = self.document.getroot() + width = self.svg.unittouu(svg.get('width')) + height = self.svg.unittouu(svg.attrib['height']) + + # Embed grid in group + #Put in in the centre of the current view + #t = 'translate(' + str( self.view_center[0]- width/2.0) + ',' + str( self.view_center[1]- height/2.0) + ')' + #t = 'translate(0,' + str(height) + ') rotate(-180,0,0)' + t = 'translate(' + str(width/2) + ',' + str(border) + ')' + #t = 'translate(0 ,0)' + + g_attribs = {inkex.addNS('label','inkscape'):'SunDial_Lat:' + str( so.latitude )+';Long:'+str( so.longitude ),'transform':t} + grid = etree.SubElement(self.svg.get_current_layer(), 'g', g_attribs) + + #Group for x gridlines + g_attribs = {inkex.addNS('label','inkscape'):'XGridlines'} + glx = etree.SubElement(grid, 'g', g_attribs) + + #Group for y gridlines + g_attribs = {inkex.addNS('label','inkscape'):'YGridlines'} + gly = etree.SubElement(grid, 'g', g_attribs) + + #Group for Hour lines vertical SunDial + g_attribs = {inkex.addNS('label','inkscape'):'VerticalHourLines'} + vhl = etree.SubElement(grid, 'g', g_attribs) + + #Group for Nodus vertical SunDial + g_attribs = {inkex.addNS('label','inkscape'):'Nodus'} + nod = etree.SubElement(grid, 'g', g_attribs) + + #Group for zodiac line + g_attribs = {inkex.addNS('label','inkscape'):'Line of zodiac'} + loz = etree.SubElement(grid, 'g', g_attribs) + +# ----------Grid and border------------------------------------------------------------------ + # Border + draw_SVG_rect(-width/2+border, -0, width-2*border, heightgrid, 0.8, 'none', 'Border', grid) #border rectangle + + # XGridLine + for i in range(Gx): #x divisons + distX=widthgrid/(Gx-1) + draw_SVG_line(distX*i-width/2+border, heightgrid, distX*i-width/2+border, 0, 0.1, '#0000FF', 'X'+str(i), glx) + + # YGridLine + for i in range(Gy): #y divisons + distY=heightgrid/(Gy-1) + draw_SVG_line(-width/2+border, distY*i, width/2-border, distY*i, 0.1, '#0000FF', 'Y'+str(i), gly) + +# ---------------------------------------------------------------------------- + Time = [4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20] + T = {} # + T_rad = {} # + + # Line of zodiac + if so.DL == 0: + D = [] + if so.DL == 3: + D = [-23.44, 0.0001, 23.44] + if so.DL == 7: + D = [-23.44, -20.15, -11.47, 0.0001, 11.47, 20.15, 23.44] #https://www.mysundial.ca/tsp/the_zodiac.html + + D_rad = {} + + W = so.decl*pi/180 # Declination of wall in radian https://www.mysundial.ca/tsp/vertical_declining_sundial.html + R = so.incl*pi/180 # Inclination of wall in radian + L = so.latitude*pi/180 # Latitude in radian + + #Text + textvalue='Sundials for Latitude: %s; Wall azimuth: %s; Wall inclination: %s --- (c)2019 Tomas Urban ' % (so.latitude, round(W*180/pi,2), round(R*180/pi,2)) + draw_SVG_text(0, heightgrid+4, textvalue, 'san-serif', 4, gly) + +# ----------Gnomon ---------------------------------------------------------------------------- + Xf =-so.gnom*(sin(W)*cos(L))/(cos(R)*cos(W)*cos(L)-sin(R)*sin(L)) + Zf = so.gnom*((sin(R)*cos(W)*cos(L))+(cos(R)*sin(L)))/(cos(R)*cos(W)*cos(L)-sin(R)*sin(L)) + g = asin(cos(R)*cos(W)*cos(L)-sin(R)*sin(L)) + f = atan(-sin(W)*cos(L)/(sin(R)*cos(W)*cos(L)+cos(R)*sin(L))) + P = -so.gnom/(cos(R)*cos(W)*cos(L)-sin(R)*sin(L)) + + #Text + #textvalue='Xf: %s; Zf: %s; g: %s; P: %s; f: %s' % (round(Xf,2), round(Zf,2), round(g*180/pi,2), round(P,2), round(f*180/pi,2)) + #draw_SVG_text(0, 120, textvalue, 'san-serif', 4, gly) + #textvalue='G*cos(f): %s; G*sin(f): %s; g: %s; P: %s; f: %s' % (round(so.gnom*cos(f),2), round(so.gnom*sin(f),2), round(so.gnom*180/pi,2), round(P,2), round(f*180/pi,2)) + #draw_SVG_text(0, 125, textvalue, 'san-serif', 4, gly) + + # Horizont line + draw_SVG_line(-width/2+border, Zf, width/2-border, Zf, 0.5, '#000000', 'Horizont line', nod) + + # Nodus + a = (0,0) + b = (-Xf, Zf) + c = (-Xf+so.gnom*cos(f),Zf+so.gnom*sin(f)) + draw_SVG_tri(a[0], a[1], b[0], b[1], c[0], c[1], 0.35, '#FF00FF', 'Nodus', nod) + draw_SVG_circle(-Xf,Zf, 2, 0.3, '#000000', '#0000FF', 'Nodus', nod) + +# --------- Line of zodiac ---------------------------------------------------------------------- + A = -cos(R)*sin(W) + B = -cos(R)*cos(W) + C = sin(R) + #textvalue='A: %s; B: %s; C: %s' % (A, B, C) + #draw_SVG_text(0, 125, textvalue, 'san-serif', 4, gly) + + for i in range(len(D)): + D_rad[i]= D[i]*pi/180 + DP='' + for ii in range(len(Time)): + T[ii]=(Time[ii]-12)*15 + T_rad[ii]=T[ii]*pi/180 + AzSun = atan(sin(T_rad[ii])/(sin(L)*cos(T_rad[ii]) - tan(D_rad[i])*cos(L))) + AltSun = asin(sin(D_rad[i])*sin(L) + cos(D_rad[i])*cos(L)*cos(T_rad[ii])) + + if T[ii] < 0: + if AzSun > 0: + AzSun=AzSun+pi + else: + AzSun = AzSun+2*pi + else: + if AzSun >= 0: + AzSun=AzSun + else: + AzSun = AzSun+pi + + #Text + #textvalue='AzSun: %s; AltSun: %s; T: %s' % (round(AzSun*180/pi,2), round(AltSun*180/pi,2), Time[ii]) + #draw_SVG_text(200, 220+5*ii+100*i, textvalue, 'san-serif', 4, gly) + + X0= -cos(AltSun)*sin(AzSun) + Y0= -cos(AltSun)*cos(AzSun) + Z0= sin(AltSun) + + X1=X0*cos(W)-Y0*sin(W) + Y1=X0*sin(W)+Y0*cos(W) + Z1=Z0 + + X2=X1 + Y2=Y1*cos(R)-Z1*sin(R) + Z2=Y1*sin(R)+Z1*cos(R) + + X3=so.gnom*X2/Y2 + Z3=-so.gnom*Z2/Y2 + p1= X0*A+Y0*B+Z0*C + + Xe = X3 - Xf + Ze = Z3 + Zf + + if p1 >0: + DP = DP +' '+str(Xe)+', '+str(Ze) + + # draw polyline + draw_SVG_polyline(DP, 0.5,'#FF0000', 'none', 'Zodiac line '+str(i+1), loz) + +# --------- Time Line ---------------------------------------------------------------------- + ALFA= atan(widthgrid/(2*heightgrid)) # Line for help + + for i in range(len(Time)): + T[i]=(Time[i]-12)*15 + T_rad[i]=T[i]*pi/180 + TL='' + + D = [-23.44, -20.15, -11.47, 0.0001, 11.47, 20.15, 23.44] + for ii in range(len(D)): + D_rad[ii]= D[ii]*pi/180 + TL= '0, 0' + + AzSun = atan(sin(T_rad[i])/(sin(L)*cos(T_rad[i]) - tan(D_rad[ii])*cos(L))) + AltSun = asin(sin(D_rad[ii])*sin(L) + cos(D_rad[ii])*cos(L)*cos(T_rad[i])) + + if T[i] < 0: + if AzSun > 0: + AzSun=AzSun+pi + else: + AzSun = AzSun+2*pi + else: + if AzSun >= 0: + AzSun=AzSun + else: + AzSun = AzSun+pi + + X0= -cos(AltSun)*sin(AzSun) + Y0= -cos(AltSun)*cos(AzSun) + Z0= sin(AltSun) + + X1=X0*cos(W)-Y0*sin(W) + Y1=X0*sin(W)+Y0*cos(W) + Z1=Z0 + + X2=X1 + Y2=Y1*cos(R)-Z1*sin(R) + Z2=Y1*sin(R)+Z1*cos(R) + + X3=so.gnom*X2/Y2 + Z3=so.gnom*Z2/Y2 + + Xe = X3 - Xf + Ze = -Z3 + Zf + + if Ze == 0: + Ze = 0.0001 + + SG = atan(Xe/Ze) + #Xe = so.gnom /((cos(R)/tan(AzSun-W))+(sin(R)*tan(AltSun)/sin(AzSun-W))) - Xf + #Ze = -so.gnom *((tan(R)-(tan(AltSun)/cos(AzSun-W)))/(1+(tan(R)*tan(AltSun))/cos(AzSun-W))) + Zf + p1= X0*A+Y0*B+Z0*C + + if p1 >=0: + #if SG <= pi and SG >=-pi: + TL = TL +' '+str(Xe)+', '+str(Ze) + else: + Xe=-Xe + Ze=-Ze + TL = TL +' '+str(Xe)+', '+str(Ze) + + if T[i] < 0: + if SG >= 0: + SG = SG-pi + else: + SG = SG + else: + if SG >= 0: + SG = SG + else: + SG = SG+pi + + if SG >= (-pi) and SG < -ALFA: + draw_SVG_text(-widthgrid/2+5, -widthgrid/(2*tan(SG)), str(Time[i]), 'Algerian', 8, vhl) + + if SG > (-ALFA) and SG < ALFA: + draw_SVG_text(heightgrid*tan(SG), heightgrid-5, str(Time[i]), 'Algerian',8, vhl) + + if SG > ALFA and SG <= (pi): + if Time[i] == 12: #workaround because y coordinate gets extreme values like "-816561967659768448.0000" + draw_SVG_text(0, heightgrid-5, str(Time[i]), 'Algerian',8, vhl) + else: + draw_SVG_text(widthgrid/2-8, widthgrid/(2*tan(SG)), str(Time[i]), 'Algerian',8, vhl) + #Text + #textvalue='Xe: %s; Ze: %s; T: %s; SG: %s' % (round(Xe,2), round(Ze,2), Time[i], SG*180/pi) + #draw_SVG_text(0, 220+5*i, textvalue, 'san-serif', 4, gly) + + # draw polyline + draw_SVG_polyline(TL, 0.5,'#008000', 'none', 'Time line '+str(Time[i]), loz) + +if __name__ == '__main__': + sundialDeclining().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/triangular_grid/meta.json b/extensions/fablabchemnitz/triangular_grid/meta.json new file mode 100644 index 0000000..66094d2 --- /dev/null +++ b/extensions/fablabchemnitz/triangular_grid/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Triangular Grid", + "id": "fablabchemnitz.de.triangular_grid", + "path": "triangular_grid", + "dependent_extensions": null, + "original_name": "Triangular Grid", + "original_id": "grid.triangular", + "license": "GNU GPL v2", + "license_url": "https://github.com/cds4/inkscape-grids/blob/master/grid_triangular.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/triangular_grid", + "fork_url": "https://github.com/cds4/inkscape-grids", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Triangular+Grid", + "inkscape_gallery_url": null, + "main_authors": [ + "github.com/cds4", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/triangular_grid/triangular_grid.inx b/extensions/fablabchemnitz/triangular_grid/triangular_grid.inx new file mode 100644 index 0000000..c3f56b7 --- /dev/null +++ b/extensions/fablabchemnitz/triangular_grid/triangular_grid.inx @@ -0,0 +1,49 @@ + + + Triangular Grid + fablabchemnitz.de.triangular_grid + + + + + + + + + + 100.00 + 3 + 3 + 30.0 + 2 + 5 + + + #000000ff + 3 + + + #ff0000ff + 2 + + + #ffff00ff + 2 + + + ##0000ffff + 2 + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/triangular_grid/triangular_grid.py b/extensions/fablabchemnitz/triangular_grid/triangular_grid.py new file mode 100644 index 0000000..fc2c21e --- /dev/null +++ b/extensions/fablabchemnitz/triangular_grid/triangular_grid.py @@ -0,0 +1,266 @@ +#!/usr/bin/env python 3 +''' +Copyright (C) 2013 Carl Sorensen carl.d.sorensen@gmail.com +Derived from grid_cartesion.py copyright (C) 2007 John Beard john.j.beard@gmail.com + + +##This extension allows you to draw a Triangular grid in Inkscape. +##There is a wide range of options including subdivision, subsubdivions +##and angles of the triangular axes. +##Custom line widths are also possible. +##All elements are grouped with similar elements (eg all x-subdivs) + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +''' + +import inkex +from lxml import etree +from math import * + +def draw_SVG_line(x1, y1, x2, y2, width, stroke, name, parent): + style = { 'stroke': stroke, 'stroke-width':str(width), 'fill': 'none' } + line_attribs = {'style':str(inkex.Style(style)), + inkex.addNS('label','inkscape'):name, + 'd':'M '+str(x1)+','+ str(y1) +' L '+ str(x2) + ',' + str(y2)} + etree.SubElement(parent, inkex.addNS('path','svg'), line_attribs ) + +def draw_SVG_rect(x,y,w,h, width, stroke, fill, name, parent): + style = { 'stroke': stroke, 'stroke-width':str(width), 'fill':fill} + rect_attribs = {'style':str(inkex.Style(style)), + inkex.addNS('label','inkscape'):name, + 'x':str(x), 'y':str(y), 'width':str(w), 'height':str(h)} + etree.SubElement(parent, inkex.addNS('rect','svg'), rect_attribs ) + +def colorString(pickerColor): + longcolor = int(pickerColor) & 0xFFFFFF00 + return '#' + format(longcolor >> 8, '06X') + + +class TriangularGrid(inkex.EffectExtension): + + def add_arguments(self, pars): + pars.add_argument("--tabs") + pars.add_argument("--size_unit", default="mm", help="Unit for grid size") + pars.add_argument("--y_divs", type=int, default=3, help="Major vertical divisions") + pars.add_argument("--x_divs", type=int, default=3, help="Major horizontal divisions") + pars.add_argument("--grid_angle", type=float, default=30.0, help="Angle between X axis and triangular grid lines") + pars.add_argument("--dm", type=float, default=100.0, help="Major grid division spacing") + pars.add_argument("--subdivs", type=int, default=5, help="Subdivisions per major grid division") + pars.add_argument("--subsubdivs", type=int, default=2, help="Subsubdivisions per minor grid subdivision") + pars.add_argument("--border_th", type=float, default=3.0, help="Border Line thickness") + pars.add_argument("--border_color", type=int, help="Border line color") + pars.add_argument("--major_th", type=float, default=2.0, help="Major grid division line thickness") + pars.add_argument("--major_color", type=int, help="Major grid division line color") + pars.add_argument("--subdiv_th", type=float, default=1.0, help="Minor grid subdivision line thickness") + pars.add_argument("--subdiv_color", type=int, help="Minor grid subdivision line color") + pars.add_argument("--subsubdiv_th", type=float, default=1.0, help="Subminor grid division line thickness") + pars.add_argument("--subsubdiv_color", type=int, help="Subminor grid division line color") + + def EdgePoints(self,x0, y0, theta): + # find the intersection points of the line with the extended + # grid bounding box. + # Note that y is positive DOWN, not up + theta_r = radians(theta) + r_bot = (self.ymax-y0)/sin(theta_r) + r_top = -y0/sin(theta_r) + r_left = -x0/cos(theta_r) + r_right = (self.xmax-x0)/cos(theta_r) + return [[x0+r_left*cos(theta_r),y0+r_left*sin(theta_r)], + [x0+r_right*cos(theta_r), y0+r_right*sin(theta_r)], + [x0+r_bot*cos(theta_r), y0+r_bot*sin(theta_r)], + [x0+r_top*cos(theta_r), y0+r_top*sin(theta_r)]] + + def trimmed_coords(self, x1, y1, theta): + #find the start and end coordinates for a grid line + #starting at (x1, y1) with an angle of theta + border_points = self.EdgePoints(x1, y1, theta) + left = 0 + right = 1 + top = 3 + bottom = 2 + x=0 + y=1 + if theta > 0: + if border_points[left][y] < 0: + start_x = border_points[top][x] + start_y = border_points[top][y] + else: + start_x = border_points[left][x] + start_y = border_points[left][y] + if border_points[right][y] > self.ymax: + end_x = border_points[bottom][x] + end_y = border_points[bottom][y] + else: + end_x = border_points[right][x] + end_y = border_points[right][y] + else: + if border_points[left][y] > self.ymax: + start_x = border_points[bottom][x] + start_y = border_points[bottom][y] + else: + start_x = border_points[left][x] + start_y = border_points[left][y] + if border_points[right][y] < 0: + end_x = border_points[top][x] + end_y = border_points[top][y] + else: + end_x = border_points[right][x] + end_y = border_points[right][y] + return [[start_x,start_y],[end_x, end_y]] + + def drawAngledGridLine (self, x1, y1, theta, thickness, color, + label, groupName): + end_points = self.trimmed_coords(x1, y1, theta) + x_start = end_points[0][0] + y_start = end_points[0][1] + x_end = end_points[1][0] + y_end = end_points[1][1] + + if (x_end >= 0 and x_end <= self.xmax and + y_end >= 0 and y_end <= self.ymax and + (y_start != y_end and x_start != x_end)): + draw_SVG_line(x_start, y_start, + x_end, y_end, + thickness, colorString(color), label, groupName) + + def effect(self): + + #find the pixel dimensions of the overall grid + dm = self.svg.unittouu(str(self.options.dm) + self.options.size_unit) + self.ymax = dm * self.options.y_divs #grid spacing defined along vertical + dx = dm / (2.0 * tan(radians(self.options.grid_angle))) + self.xmax = dx * self.options.x_divs + dy = dm + + # Embed grid in group + #Put in in the centre of the current view + t = 'translate(' + str( self.svg.namedview.center[0]- self.xmax/2.0) + ',' + \ + str( self.svg.namedview.center[1]- self.ymax/2.0) + ')' + g_attribs = {inkex.addNS('label','inkscape'):'Grid_Triangular:Size' + \ + str( self.options.x_divs)+'x'+str(self.options.y_divs) + + ':Angle'+str( self.options.grid_angle ), + 'transform':t } + grid = etree.SubElement(self.svg.get_current_layer(), 'g', g_attribs) + + #Group for major x gridlines + g_attribs = {inkex.addNS('label','inkscape'):'MajorXGridlines'} + majglx = etree.SubElement(grid, 'g', g_attribs) + + #Group for major positive theta gridlines + g_attribs = {inkex.addNS('label','inkscape'):'MajorPosGridlines'} + majglp = etree.SubElement(grid, 'g', g_attribs) + + #Group for major negative theta gridlines + g_attribs = {inkex.addNS('label','inkscape'):'MajorNegGridLines'} + majgln = etree.SubElement(grid, 'g', g_attribs) + + #Groups for minor gridlines + if self.options.subdivs > 1:#if there are any minor gridlines + g_attribs = {inkex.addNS('label','inkscape'):'MinorXGridlines'} + minglx = etree.SubElement(grid, 'g', g_attribs) + g_attribs = {inkex.addNS('label','inkscape'):'MinorPosGridlines'} + minglp = etree.SubElement(grid, 'g', g_attribs) + g_attribs = {inkex.addNS('label','inkscape'):'MinorNegGridlines'} + mingln = etree.SubElement(grid, 'g', g_attribs) + + #Groups for subminor gridlines + if self.options.subsubdivs > 1:#if there are any minor minor gridlines + g_attribs = {inkex.addNS('label','inkscape'):'SubMinorXGridlines'} + mminglx = etree.SubElement(grid, 'g', g_attribs) + g_attribs = {inkex.addNS('label','inkscape'):'SubMinorPosGridlines'} + mminglp = etree.SubElement(grid, 'g', g_attribs) + g_attribs = {inkex.addNS('label','inkscape'):'SubMinorNegGridlines'} + mmingln = etree.SubElement(grid, 'g', g_attribs) + + draw_SVG_rect(0, 0, self.xmax, self.ymax, + self.options.border_th, + colorString(self.options.border_color), 'none', + 'Border', grid) #border rectangle + + sd = self.options.subdivs #sub divs per div + ssd = self.options.subsubdivs #subsubdivs per subdiv + + + #DO THE HORIZONTAL DIVISONS====================================== + + for i in range(0, self.options.x_divs): #Major x divisons + if i>0: #dont draw first line (we made a proper border) + # Draw the vertical line + draw_SVG_line(dx*i, 0, + dx*i,self.ymax, + self.options.major_th, + colorString(self.options.major_color), + 'MajorDiv'+str(i), majglx) + + for j in range (0, sd): + if j>0: #not for the first loop (this loop is for the subsubdivs before the first subdiv) + draw_SVG_line(dx*(i+j/float(sd)), 0, + dx*(i+j/float(sd)), self.ymax, + self.options.subdiv_th, + colorString(self.options.subdiv_color), + 'MinorDiv'+str(i)+':'+str(j), minglx) + + for k in range (1, ssd): #subsub divs + draw_SVG_line(dx*(i+(j*ssd+k)/((float(sd)*ssd))) , 0, + dx*(i+(j*ssd+k)/((float(sd)*ssd))) , self.ymax, + self.options.subsubdiv_th, + colorString(self.options.subsubdiv_color), + 'SubminorDiv'+str(i)+':'+str(j)+':'+str(k), mminglx) + + #DO THE VERTICAL DIVISONS======================================== + for i in range(-self.options.x_divs-self.options.y_divs, + self.options.x_divs+self.options.y_divs): #Major y divisons + self.drawAngledGridLine(0, dy*i, self.options.grid_angle, + self.options.major_th, + self.options.major_color, + 'MajorYDivP'+str(i), + majglp) + self.drawAngledGridLine(0, dy*i, -self.options.grid_angle, + self.options.major_th, + self.options.major_color, + 'MajorYDivN'+str(i), + majgln) + + for j in range (0, sd): #subdivs + if j>0:#not for the first loop (this loop is for the subsubdivs before the first subdiv) + self.drawAngledGridLine(0, dy*(i+j/float(sd)), + self.options.grid_angle, + self.options.subdiv_th, + self.options.subdiv_color, + 'MinorYDivP'+str(i), + minglp) + self.drawAngledGridLine(0, dy*(i+j/float(sd)), + -self.options.grid_angle, + self.options.subdiv_th, + self.options.subdiv_color, + 'MinorYDivN'+str(i), + mingln) + for k in range (1, ssd): #subsub divs + self.drawAngledGridLine(0, dy*(i+(j*ssd+k)/((float(sd)*ssd))), + self.options.grid_angle, + self.options.subsubdiv_th, + self.options.subsubdiv_color, + 'SubMinorYDivP'+str(i), + mminglp) + self.drawAngledGridLine(0, dy*(i+(j*ssd+k)/((float(sd)*ssd))), + -self.options.grid_angle, + self.options.subsubdiv_th, + self.options.subsubdiv_color, + 'SubMinorYDivN'+str(i), + mmingln) + + +if __name__ == '__main__': + TriangularGrid().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/tuckbox/meta.json b/extensions/fablabchemnitz/tuckbox/meta.json new file mode 100644 index 0000000..0455410 --- /dev/null +++ b/extensions/fablabchemnitz/tuckbox/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Tuckbox", + "id": "fablabchemnitz.de.tuckbox", + "path": "tuckbox", + "dependent_extensions": null, + "original_name": "Create Tuckbox", + "original_id": "phillips.effect.tuckbox", + "license": "GNU GPL v2", + "license_url": "https://sourceforge.net/p/razorfoss/svn/HEAD/tree/trunk/Inkscape/TuckboxExtension/CreateTuckbox.py", + "comment": "", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/tuckbox", + "fork_url": "https://sourceforge.net/p/razorfoss/svn/HEAD/tree/trunk/Inkscape/TuckboxExtension/", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Tuckbox", + "inkscape_gallery_url": null, + "main_authors": [ + "Luke Phillips:lukerazor@hotmail.com", + "github.com/eridur-de" + ] + } +] diff --git a/extensions/fablabchemnitz/tuckbox/tuckbox.inx b/extensions/fablabchemnitz/tuckbox/tuckbox.inx new file mode 100644 index 0000000..9ca2624 --- /dev/null +++ b/extensions/fablabchemnitz/tuckbox/tuckbox.inx @@ -0,0 +1,26 @@ + + + Tuckbox + fablabchemnitz.de.tuckbox + + + + + + 1 + 63.5 + 88 + 20 + 1.5 + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/tuckbox/tuckbox.py b/extensions/fablabchemnitz/tuckbox/tuckbox.py new file mode 100644 index 0000000..ec09804 --- /dev/null +++ b/extensions/fablabchemnitz/tuckbox/tuckbox.py @@ -0,0 +1,573 @@ +#!/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 + +CUTOUT_TOP = 1 +CUTOUT_BOTTOM = 2 +CUTOUT_LEFT = 4 +CUTOUT_RIGHT = 8 + +inkex.NSS[u'cs'] = u'http://www.razorfoss.org/tuckboxextension/' + +class EffectDimensionProvider(): + def __init__(self, effect, x = 0, y = 0): + self.Effect = effect + + self.Layer = effect.svg.get_current_layer() + + self.Width = effect.options.DeckWidth + self.Height = effect.options.DeckHeight + self.Depth = effect.options.DeckDepth + self.Allowance = effect.options.DeckAllowance + + self.X = x + self.Y = y + + def MMtoUU(self, mmval): + if hasattr(self.Effect.svg, "unittouu"): + return str(self.Effect.svg.unittouu("{0}mm".format(mmval))) + else: + MM_TO_PIXELS = 3.5433071 + + return str(MM_TO_PIXELS * mmval) + + def MaximiseHeight(self): + if self.Height < self.Width: # always choose the smallest to be the "width" + temp = self.Width + self.Width = self.Height + self.Height = temp + +class BoxBase(): + def __init__(self, dimensionProvider): + #create a group + self.DimProvider = dimensionProvider + self.Group = etree.SubElement(dimensionProvider.Layer, inkex.addNS('g','svg'), {} ) + + self.Width = dimensionProvider.Width + dimensionProvider.Allowance + self.Height = dimensionProvider.Height + dimensionProvider.Allowance + self.Depth = dimensionProvider.Depth + dimensionProvider.Allowance + self.X = dimensionProvider.X + self.Y = dimensionProvider.Y + self.MinY = 0 + self.MinX = 0 + + ### init some common sizes ### + self.ThumbSize = 20 + + # tuck flap size + self.FlapOffset = 1.5 + self.FlapHeight = self.Depth + if self.Depth < 7 or self.Depth > 25: + self.FlapHeight = 20 + + # main flap size + self.MainFlapHeight = (4 * self.Depth)/3 + if self.MainFlapHeight < self.ThumbSize: + self.MainFlapHeight = 24 + + ### colour ### + self.Fill = '#ffffff' + self.StrokeWidth = self.DimProvider.MMtoUU(0.5) + + def _CreateRectangleInMillimetres(self, height, width, x, y): + style = {'stroke': '#000000', 'stroke-width': self.StrokeWidth, 'fill' : self.Fill} + attribs = {'style': str(inkex.Style(style)), 'height': self.DimProvider.MMtoUU(height), 'width': self.DimProvider.MMtoUU(width), 'x': self.DimProvider.MMtoUU(x), 'y': self.DimProvider.MMtoUU(y)} + etree.SubElement(self.Group, inkex.addNS('rect','svg'), attribs ) + + def _CreateRectangleInMillimetresWithCutouts(self, height, width, x, y, cutoutPositions): + cmds = [] + + t = self.ThumbSize + cr = (3*t)/4 # curve ratio + + # start position + cmds.append(["m", x, y]) + + # Top side + if cutoutPositions & CUTOUT_TOP == CUTOUT_TOP: + cmds.append(["h", (self.Width - t)/2]) + cmds.append(["c", 0, cr, + t, cr, + t, 0]) + cmds.append(["h", (self.Width - t)/2]) + else: + cmds.append(["h", width]) + + # Right Side + if cutoutPositions & CUTOUT_RIGHT == CUTOUT_RIGHT: + cmds.append(["v", (self.Height - t)/2]) + cmds.append(["c", -cr, 0, + -cr, t, + 0, t]) + cmds.append(["v", (self.Height - t)/2]) + else: + cmds.append(["v", height]) + + # Bottom Side + if cutoutPositions & CUTOUT_BOTTOM == CUTOUT_BOTTOM: + cmds.append(["h", -(self.Width - t)/2]) + cmds.append(["c", 0, -cr, + -t, -cr, + -t, 0]) + cmds.append(["h", -(self.Width - t)/2]) + else: + cmds.append(["h", -width]) + + # Left Side + if cutoutPositions & CUTOUT_LEFT == CUTOUT_LEFT: + cmds.append(["v", -(self.Height - t)/2]) + cmds.append(["c", cr, 0, + cr, -t, + 0, -t]) + cmds.append(["v", -(self.Height - t)/2]) + else: + cmds.append(["v", -height]) + + self._CreatePathinMillimetres(cmds) + + def _CreatePathinMillimetres(self, cmds): + pathStr = "" + for cmd in cmds: + pathStr += cmd[0] + " " + for coord in cmd[1:]: + pathStr += self.DimProvider.MMtoUU(coord) + " " + + pathStr += "z" + #raise Exception(pathStr) + + style = {'stroke': '#000000', 'stroke-width': self.StrokeWidth, 'fill' : self.Fill} + attribs = {'style': str(inkex.Style(style)), 'd': pathStr} + etree.SubElement(self.Group, inkex.addNS('path','svg'), attribs ) + +class SingleFlappedTuckBox(BoxBase): + def __init__(self, dimensionProvider): + BoxBase.__init__(self, dimensionProvider) + + def Create(self): + self.FlapOffset = 1.5 + self.FlapHeight = min(20, self.Depth) + + # Figure out some row and column values, + # note rows and cols work left to right, top to bottom, but both calculated in reverse + col5 = self.X - self.Depth + col4 = col5 - self.Width + col3 = col4 - self.Depth + col2 = col3 - self.Width + col1 = col2 - self.Depth + self.MinX = col1 + + row4 = self.Y - self.Depth + row3 = row4 - self.Height + row2 = row3 - self.Depth + row1 = row2 - self.Depth + self.MinY = row1 + + ### COLUMN 1 ### + #create left glue panel + self._CreateRectangleInMillimetres(self.Height, self.Depth, col1, row3) + + ### COLUMN 2 ### + #create box back print panel + self._CreateRectangleInMillimetresWithCutouts(self.Height, self.Width, col2, row3, CUTOUT_TOP) + + #create box bottom glue panel + self._CreateRectangleInMillimetres(self.Depth, self.Width, col2, row4) + + ### COLUMN 3 ### + #create left flap + self._CreatePathinMillimetres( + [ + ["m", col3, row3], + ["h", self.Depth], + ["l", -self.FlapOffset, -self.FlapHeight], + ["h", -(self.Depth - (2*self.FlapOffset))], + ]) + + #create left print panel + self._CreateRectangleInMillimetres(self.Height, self.Depth, col3, row3) + + #create bottom left glue panel + self._CreateRectangleInMillimetres(self.Depth, self.Depth, col3, row4) + + ### COLUMN 4 ### + #create main flap + self._CreatePathinMillimetres( + [ + ["m", col4, row2], + ["c", 0, -self.MainFlapHeight, self.Width, -self.MainFlapHeight, self.Width, 0] + ]) + + #create box top print panel + self._CreateRectangleInMillimetres(self.Depth, self.Width, col4, row2) + + #create box front print panel + self._CreateRectangleInMillimetres(self.Height, self.Width, col4, row3) + + #create box bottom print panel + self._CreateRectangleInMillimetres(self.Depth, self.Width, col4, row4) + + ### COLUMN 5 ### + #create right flap + self._CreatePathinMillimetres( + [ + ["m", col5, row3], + ["h", self.Depth], + ["l", -self.FlapOffset, -self.FlapHeight], + ["h", -(self.Depth - (2*self.FlapOffset))], + ]) + + #create right print panel + self._CreateRectangleInMillimetres(self.Height, self.Depth, col5, row3) + + #create bottom right glue panel + self._CreateRectangleInMillimetres(self.Depth, self.Depth, col5, row4) + +class DoubleFlappedTuckBox(BoxBase): + def __init__(self, dimensionProvider): + BoxBase.__init__(self, dimensionProvider) + + def Create(self): + + # Figure out some row and column values, + # note rows and cols work left to right, top to bottom + col5 = self.X - self.Depth + col4 = col5 - self.Width + col3 = col4 - self.Depth + col2 = col3 - self.Width + col1 = col2 - self.Depth + self.MinX = col1 + + row5 = self.Y - self.Depth + row4 = row5 - self.Depth + row3 = row4 - self.Height + row2 = row3 - self.Depth + row1 = row2 - self.Depth + self.MinY = row1 + + ### COLUMN 1 ### + #create left glue panel + self._CreateRectangleInMillimetres(self.Height, self.Depth, col1, row3) + + ### COLUMN 2 ### + #create box back print panel + self._CreateRectangleInMillimetresWithCutouts(self.Height, self.Width, col2, row3, CUTOUT_TOP | CUTOUT_BOTTOM) + + ### COLUMN 3 ### + #create top left flap + self._CreatePathinMillimetres( + [ + ["m", col3, row3], + ["h", self.Depth], + ["l", -self.FlapOffset, -self.FlapHeight], + ["h", -(self.Depth - (2*self.FlapOffset))], + ]) + + #create left print panel + self._CreateRectangleInMillimetres(self.Height, self.Depth, col3, row3) + + #create bottom left flap + self._CreatePathinMillimetres( + [ + ["m", col3, row4], + ["h", self.Depth], + ["l", -self.FlapOffset, self.FlapHeight], + ["h", -(self.Depth - (2*self.FlapOffset))], + ]) + + ### COLUMN 4 ### + #create top main flap + self._CreatePathinMillimetres( + [ + ["m", col4, row2], + ["c", 0, -self.MainFlapHeight, self.Width, -self.MainFlapHeight, self.Width, 0] + ]) + + #create box top print panel + self._CreateRectangleInMillimetres(self.Depth, self.Width, col4, row2) + + #create box front print panel + self._CreateRectangleInMillimetres(self.Height, self.Width, col4, row3) + + #create box bottom print panel + self._CreateRectangleInMillimetres(self.Depth, self.Width, col4, row4) + + #create bottom main flap + self._CreatePathinMillimetres( + [ + ["m", col4, row5], + ["c", 0, self.MainFlapHeight, self.Width, self.MainFlapHeight, self.Width, 0] + ]) + + ### COLUMN 5 ### + #create top right flap + self._CreatePathinMillimetres( + [ + ["m", col5, row3], + ["h", self.Depth], + ["l", -self.FlapOffset, -self.FlapHeight], + ["h", -(self.Depth - (2*self.FlapOffset))], + ]) + + #create right print panel + self._CreateRectangleInMillimetres(self.Height, self.Depth, col5, row3) + + #create bottom right flap + self._CreatePathinMillimetres( + [ + ["m", col5, row4], + ["h", self.Depth], + ["l", -self.FlapOffset, self.FlapHeight], + ["h", -(self.Depth - (2*self.FlapOffset))], + ]) + +class SlipcaseTuckBox(BoxBase): + def __init__(self, dimensionProvider): + BoxBase.__init__(self, dimensionProvider) + + def Create(self): + self.FlapOffset = 1.5 + + # Figure out some row and column values, + # note rows and cols work left to right, top to bottom + col5 = self.X - self.Depth + col4 = col5 - self.Width + col3 = col4 - self.Depth + col2 = col3 - self.Width + col1 = col2 - self.Depth + + row1 = self.Y - self.Height + self.MinY = row1 + + ### COLUMN 1 ### + #create left glue flap + self._CreatePathinMillimetres( + [ + ["m", col2, row1], + ["v", self.Height], + ["l", -(self.Depth - self.FlapOffset), -self.FlapOffset], + ["v", -(self.Height - (2*self.FlapOffset))], + ]) + + ### COLUMN 2 ### + #create box back print panel + self._CreateRectangleInMillimetres(self.Height, self.Width, col2, row1) + + ### COLUMN 3 ### + #create left print panel + self._CreateRectangleInMillimetres(self.Height, self.Depth, col3, row1) + + ### COLUMN 4 ### + #create box front print panel + self._CreateRectangleInMillimetres(self.Height, self.Width, col4, row1) + + ### COLUMN 5 ### + #create right print panel + self._CreateRectangleInMillimetres(self.Height, self.Depth, col5, row1) + +class Matchbox(BoxBase): + def __init__(self, dimensionProvider, numFlaps): + BoxBase.__init__(self, dimensionProvider) + + self.DimProvider.MaximiseHeight() + self.NumFlaps = numFlaps + + def Create(self): + if self.NumFlaps == 2: + tuckbox = DoubleFlappedTuckBox(self.DimProvider) + else: + tuckbox = SingleFlappedTuckBox(self.DimProvider) + tuckbox.Create() + + ################################# + # Create Drawer for inside the box + dimProvider = copy.copy(self.DimProvider) + dimProvider.Width -= 2 + dimProvider.Height -= 2 + dimProvider.Depth -= 2 + dimProvider.Y = tuckbox.MinY - 20 + drawer = MatcboxDrawer(dimProvider) + drawer.Create() + +class TelescopingBox(BoxBase): + def __init__(self, dimensionProvider): + BoxBase.__init__(self, dimensionProvider) + self.DimProvider.MaximiseHeight() + + def Create(self): + ################################# + # Create box top + top = MatcboxDrawer(self.DimProvider, includeFingerCutouts=True) + top.Create() + + ################################# + # Create box bottom + dimProvider = copy.copy(self.DimProvider) + dimProvider.Width -= 1 + dimProvider.Height -= 1 + dimProvider.Depth -= 1 + dimProvider.Y = top.MinY - 20 + drawer = MatcboxDrawer(dimProvider) + drawer.Create() + +class MatcboxDrawer(BoxBase): + def __init__(self, dimensionProvider, includeFingerCutouts=False): + BoxBase.__init__(self, dimensionProvider) + + self.IncludeFingerCutouts = includeFingerCutouts + + def Create(self): + + fudgeDepth = self.Depth - 2 # overlap panels should be a little smaller to avoid touching box base + # Figure out some row and column co-ord values, + # note rows and cols work left to right, top to bottom, values start at 0 and go negative + col5 = self.X - fudgeDepth + col4 = col5 - self.Depth + col3 = col4 - self.Width + col2 = col3 - self.Depth + col1 = col2 - fudgeDepth + + row5 = self.Y - fudgeDepth + row4 = row5 - self.Depth + row3 = row4 - self.Height + row2 = row3 - self.Depth + row1 = row2 - fudgeDepth + self.MinY = row1 + + ### COLUMN 1 ### + #create left overlap panel + if self.IncludeFingerCutouts: + self._CreateRectangleInMillimetresWithCutouts(self.Height, fudgeDepth, col1, row3, CUTOUT_RIGHT) + else: + self._CreateRectangleInMillimetres(self.Height, fudgeDepth, col1, row3) + + ### COLUMN 2 ### + #create top left flap + self._CreatePathinMillimetres( + [ + ["m", col2, row3], + ["h", self.Depth], + ["l", -self.FlapOffset, -self.FlapHeight], + ["h", -(self.Depth - (2*self.FlapOffset))], + ]) + + #create box left side print panel + if self.IncludeFingerCutouts: + self._CreateRectangleInMillimetresWithCutouts(self.Height, self.Depth, col2, row3, CUTOUT_LEFT) + else: + self._CreateRectangleInMillimetres(self.Height, self.Depth, col2, row3) + + #create bottom left flap + self._CreatePathinMillimetres( + [ + ["m", col2, row4], + ["h", self.Depth], + ["l", -self.FlapOffset, self.FlapHeight], + ["h", -(self.Depth - (2*self.FlapOffset))], + ]) + + ### COLUMN 3 ### + + #create top side overlap panel + self._CreateRectangleInMillimetres(fudgeDepth, self.Width, col3, row1) + + #create top box side panel + self._CreateRectangleInMillimetres(self.Depth, self.Width, col3, row2) + + #create box bottom + self._CreateRectangleInMillimetres(self.Height, self.Width, col3, row3) + + #create bottom box side panel + self._CreateRectangleInMillimetres(self.Depth, self.Width, col3, row4) + + #create bottom side overlap panel + self._CreateRectangleInMillimetres(fudgeDepth, self.Width, col3, row5) + + ### COLUMN 4 ### + #create top right flap + self._CreatePathinMillimetres( + [ + ["m", col4, row3], + ["h", self.Depth], + ["l", -self.FlapOffset, -self.FlapHeight], + ["h", -(self.Depth - (2*self.FlapOffset))], + ]) + + #create box right side print panel + if self.IncludeFingerCutouts: + self._CreateRectangleInMillimetresWithCutouts(self.Height, self.Depth, col4, row3, CUTOUT_RIGHT) + else: + self._CreateRectangleInMillimetres(self.Height, self.Depth, col4, row3) + + #create bottom right flap + self._CreatePathinMillimetres( + [ + ["m", col4, row4], + ["h", self.Depth], + ["l", -self.FlapOffset, self.FlapHeight], + ["h", -(self.Depth - (2*self.FlapOffset))], + ]) + + ### COLUMN 5 ### + #create right overlap panel + if self.IncludeFingerCutouts: + self._CreateRectangleInMillimetresWithCutouts(self.Height, fudgeDepth, col5, row3, CUTOUT_LEFT) + else: + self._CreateRectangleInMillimetres(self.Height, fudgeDepth, col5, row3) + +class Tuckbox(inkex.EffectExtension): + def __init__(self): + inkex.Effect.__init__(self) + self.arg_parser.add_argument('-t', '--type', type = str, dest = 'BoxType') + self.arg_parser.add_argument('-n', '--num_flaps', type = int, dest = 'NumFlaps') + self.arg_parser.add_argument('-w', '--deck_width', type = float, dest = 'DeckWidth') + self.arg_parser.add_argument('-r', '--deck_height', type = float, dest = 'DeckHeight') + self.arg_parser.add_argument('-d', '--deck_depth', type = float, dest = 'DeckDepth') + self.arg_parser.add_argument('-a', '--box_allowance', type = float, dest = 'DeckAllowance') + + def GetPaths(self): + paths = [] + + def effect(self): + dimProvider = EffectDimensionProvider(self) + + if self.options.BoxType == "TUCKBOX": + if self.options.NumFlaps == 2: + box = DoubleFlappedTuckBox(dimProvider) + else: + box = SingleFlappedTuckBox(dimProvider) + elif self.options.BoxType == "SLIPCASE": + box = SlipcaseTuckBox(dimProvider) + elif self.options.BoxType == "MATCHBOX": + box = Matchbox(dimProvider, self.options.NumFlaps) + elif self.options.BoxType == "TELESCOPE": + box = TelescopingBox(dimProvider) + elif self.options.BoxType == "DISH": + box = MatcboxDrawer(dimProvider) + else: + raise Exception("Box type '{0}' is undefined".format(self.options.BoxType)) + + box.Create() + +if __name__ == '__main__': + Tuckbox().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/twist/meta.json b/extensions/fablabchemnitz/twist/meta.json new file mode 100644 index 0000000..07ff8fa --- /dev/null +++ b/extensions/fablabchemnitz/twist/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Twist", + "id": "fablabchemnitz.de.twist", + "path": "twist", + "dependent_extensions": null, + "original_name": "Twist", + "original_id": "command.eggbot.contributed.twist", + "license": "GNU GPL v3", + "license_url": "https://github.com/evil-mad/EggBot/blob/master/LICENSE", + "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/twist", + "fork_url": "https://github.com/evil-mad/EggBot/tree/master/inkscape_contributed", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Twist", + "inkscape_gallery_url": null, + "main_authors": [ + "github.com/oskay", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/twist/twist.inx b/extensions/fablabchemnitz/twist/twist.inx new file mode 100644 index 0000000..c3db88b --- /dev/null +++ b/extensions/fablabchemnitz/twist/twist.inx @@ -0,0 +1,19 @@ + + + Twist + fablabchemnitz.de.twist + + 8 + 0.15 + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/twist/twist.py b/extensions/fablabchemnitz/twist/twist.py new file mode 100644 index 0000000..83474db --- /dev/null +++ b/extensions/fablabchemnitz/twist/twist.py @@ -0,0 +1,518 @@ +#!/usr/bin/env python3 + +# twist.py -- Primarily a simple example of writing an Inkscape extension +# which manipulates objects in a drawing. +# +# For a polygon with vertices V[0], V[1], V[2], ..., V[n-1] iteratively +# move each vertex V[i] by a constant factor 0 < s < 1.0 along the edge +# between V[i] and V[i+1 modulo n] for 0 <= i <= n-1. +# +# This extension operates on every selected closed path, or, if no paths +# are selected, then every closed path in the document. Since the "twisting" +# effect only concerns itself with individual paths, no effort is made to +# worry about the transforms applied to the paths. That is, it is not +# necessary to worry about tracking SVG transforms as all the work can be +# done using the untransformed coordinates of each path. + +# Written by Daniel C. Newman ( dan dot newman at mtbaldy dot us ) +# 19 October 2010 + +# 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 inkex import Transform +from inkex import bezier +from inkex.paths import Path, CubicSuperPath +from lxml import etree + +def subdivideCubicPath(sp, flat, i=1): + """ + [ Lifted from eggbot.py with impunity ] + + Break up a bezier curve into smaller curves, each of which + is approximately a straight line within a given tolerance + (the "smoothness" defined by [flat]). + + This is a modified version of cspsubdiv.cspsubdiv(): rewritten + because recursion-depth errors on complicated line segments + could occur with cspsubdiv.cspsubdiv(). + """ + + while True: + while True: + if i >= len(sp): + return + + p0 = sp[i - 1][1] + p1 = sp[i - 1][2] + p2 = sp[i][0] + p3 = sp[i][1] + + b = (p0, p1, p2, p3) + + if bezier.maxdist(b) > flat: + break + + i += 1 + + one, two = bezier.beziersplitatt(b, 0.5) + sp[i - 1][2] = one[1] + sp[i][0] = two[2] + p = [one[2], one[3], two[1]] + sp[i:1] = [p] + + +def distanceSquared(p1, p2): + """ + Pythagorean distance formula WITHOUT the square root. Since + we just want to know if the distance is less than some fixed + fudge factor, we can just square the fudge factor once and run + with it rather than compute square roots over and over. + """ + + dx = p2[0] - p1[0] + dy = p2[1] - p1[1] + + return dx * dx + dy * dy + + +class Twist(inkex.EffectExtension): + + def add_arguments(self, pars): + pars.add_argument("--nSteps", type=int, default=8, help="Number of iterations to take") + pars.add_argument("--fRatio", type=float, default=0.2, help="Some ratio") + + """ + Store each path in an associative array (dictionary) indexed + by the lxml.etree pointer for the SVG document element + containing the path. Looking up the path in the dictionary + yields a list of lists. Each of these lists is a subpath + # of the path. E.g., for the SVG path + + + + we'd have two subpaths which will be reduced to absolute + coordinates. + + subpath_1 = [ [10, 10], [10, 15], [15, 15], [15, 10], [10,10] ] + subpath_2 = [ [30, 30], [30, 60] ] + self.paths[] = [ subpath_1, subpath_2 ] + + All of the paths and their subpaths could be drawn as follows: + + for path in self.paths: + for subpath in self.paths[path]: + first = True + for vertex in subpath: + if first: + moveto( vertex[0], vertex[1] ) + first = False + else: + lineto( vertex[0], vertex[1] ) + + NOTE: drawing all the paths like the above would not in general + give the correct rendering of the document UNLESS path transforms + were also tracked and applied. + """ + + self.paths = {} + self.paths_clone_transform = {} + + def addPathVertices(self, path, node=None, transform=None, clone_transform=None): + + """ + Decompose the path data from an SVG element into individual + subpaths, each subpath consisting of absolute move to and line + to coordinates. Place these coordinates into a list of polygon + vertices. + """ + + if (not path) or (len(path) == 0): + # Nothing to do + return + + sp = Path(path) + if (not sp) or (len(sp) == 0): + # Path must have been devoid of any real content + return + + # Get a cubic super path + p = CubicSuperPath(sp) + if (not p) or (len(p) == 0): + # Probably never happens, but... + return + + # Now traverse the cubic super path + subpath_list = [] + subpath_vertices = [] + for sp in p: + if len(subpath_vertices): + # There's a prior subpath: see if it is closed and should be saved + if distanceSquared(subpath_vertices[0], subpath_vertices[-1]) < 1: + # Keep the prior subpath: it appears to be a closed path + subpath_list.append(subpath_vertices) + subpath_vertices = [] + subdivideCubicPath(sp, 0.2) + for csp in sp: + # Add this vertex to the list of vertices + subpath_vertices.append(csp[1]) + + # Handle final subpath + if len(subpath_vertices): + if distanceSquared(subpath_vertices[0], subpath_vertices[-1]) < 1: + # Path appears to be closed so let's keep it + subpath_list.append(subpath_vertices) + + # Empty path? + if not subpath_list: + return + + # Store the list of subpaths in a dictionary keyed off of the path's node pointer + self.paths[node] = subpath_list + self.paths_clone_transform[node] = clone_transform + + def recursivelyTraverseSvg(self, a_node_list, mat_current=None, parent_visibility='visible', clone_transform=None): + + """ + [ This too is largely lifted from eggbot.py ] + + Recursively walk the SVG document, building polygon vertex lists + for each graphical element we support. + + Rendered SVG elements: + , , , , , , + + Supported SVG elements: + , + + Ignored SVG elements: + , , , , , + processing directives + + All other SVG elements trigger an error (including ) + """ + + if mat_current is None: + mat_current = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]] + + for node in a_node_list: + + # Ignore invisible nodes + v = node.get('visibility', parent_visibility) + if v == 'inherit': + v = parent_visibility + if v == 'hidden' or v == 'collapse': + pass + + # First apply the current matrix transform to this node's transform + mat_new = Transform(mat_current) @ Transform(node.get("transform")) + + if node.tag in [inkex.addNS('g', 'svg'), 'g']: + self.recursivelyTraverseSvg(node, mat_new, parent_visibility=v) + + elif node.tag in [inkex.addNS('use', 'svg'), 'use']: + + # A element refers to another SVG element via an xlink:href="#blah" + # attribute. We will handle the element by doing an XPath search through + # the document, looking for the element with the matching id="blah" + # attribute. We then recursively process that element after applying + # any necessary (x,y) translation. + # + # Notes: + # 1. We ignore the height and width attributes as they do not apply to + # path-like elements, and + # 2. Even if the use element has visibility="hidden", SVG still calls + # for processing the referenced element. The referenced element is + # hidden only if its visibility is "inherit" or "hidden". + + refid = node.get(inkex.addNS('href', 'xlink')) + if not refid: + pass + + # [1:] to ignore leading '#' in reference + path = '//*[@id="{}"]'.format(refid[1:]) + refnode = node.xpath(path) + if refnode: + x = float(node.get('x', '0')) + y = float(node.get('y', '0')) + # Note: the transform has already been applied + if (x != 0) or (y != 0): + mat_new2 = composeTransform(mat_new, parseTransform('translate({:f},{:f})'.format(x, y))) + else: + mat_new2 = mat_new + v = node.get('visibility', v) + self.recursivelyTraverseSvg(refnode, mat_new2, + parent_visibility=v, clone_transform=node.get('transform')) + + elif node.tag == inkex.addNS('path', 'svg'): + path_data = node.get('d') + if path_data: + self.addPathVertices(path_data, node, mat_new, clone_transform) + + elif node.tag in [inkex.addNS('rect', 'svg'), 'rect']: + + # Manually transform + # + # + # + # into + # + # + # + # I.e., explicitly draw three sides of the rectangle and the + # fourth side implicitly + + # Create a path with the outline of the rectangle + x = float(node.get('x')) + y = float(node.get('y')) + if (not x) or (not y): + pass + w = float(node.get('width', '0')) + h = float(node.get('height', '0')) + a = [] + a.append(['M', [x, y]]) + a.append(['l', [w, 0]]) + a.append(['l', [0, h]]) + a.append(['l', [-w, 0]]) + a.append(['Z', []]) + self.addPathVertices(Path(a), node, mat_new, clone_transform) + + elif node.tag in [inkex.addNS('line', 'svg'), 'line']: + + # Convert + # + # + + x1 = float(node.get('x1')) + y1 = float(node.get('y1')) + x2 = float(node.get('x2')) + y2 = float(node.get('y2')) + if (not x1) or (not y1) or (not x2) or (not y2): + pass + a = [] + a.append(['M ', [x1, y1]]) + a.append([' L ', [x2, y2]]) + self.addPathVertices(Path(a), node, mat_new, clone_transform) + + elif node.tag in [inkex.addNS('polyline', 'svg'), 'polyline']: + + # Convert + # + # + # + # to + # + # + # + # Note: we ignore polylines with no points + + pl = node.get('points', '').strip() + if pl == '': + pass + + pa = pl.split() + d = "".join(["M " + pa[i] if i == 0 else " L " + pa[i] for i in range(0, len(pa))]) + self.addPathVertices(d, node, mat_new, clone_transform) + + elif node.tag in [inkex.addNS('polygon', 'svg'), 'polygon']: + + # Convert + # + # + # + # to + # + # + # + # Note: we ignore polygons with no points + + pl = node.get('points', '').strip() + if pl == '': + pass + + pa = pl.split() + d = "".join(["M " + pa[i] if i == 0 else " L " + pa[i] for i in range(0, len(pa))]) + d += " Z" + self.addPathVertices(d, node, mat_new, clone_transform) + + elif node.tag in [inkex.addNS('ellipse', 'svg'), 'ellipse', + inkex.addNS('circle', 'svg'), 'circle']: + + # Convert circles and ellipses to a path with two 180 degree arcs. + # In general (an ellipse), we convert + # + # + # + # to + # + # + # + # where + # + # X1 = CX - RX + # X2 = CX + RX + # + # Note: ellipses or circles with a radius attribute of value 0 are ignored + + if node.tag in [inkex.addNS('ellipse', 'svg'), 'ellipse']: + rx = float(node.get('rx', '0')) + ry = float(node.get('ry', '0')) + else: + rx = float(node.get('r', '0')) + ry = rx + if rx == 0 or ry == 0: + pass + + cx = float(node.get('cx', '0')) + cy = float(node.get('cy', '0')) + x1 = cx - rx + x2 = cx + rx + d = 'M {x1:f},{cy:f} ' \ + 'A {rx:f},{ry:f} ' \ + '0 1 0 {x2:f},{cy:f} ' \ + 'A {rx:f},{ry:f} ' \ + '0 1 0 {x1:f},{cy:f}'.format(x1=x1, + x2=x2, + rx=rx, + ry=ry, + cy=cy) + + self.addPathVertices(d, node, mat_new, clone_transform) + + elif node.tag in [inkex.addNS('pattern', 'svg'), 'pattern']: + pass + + elif node.tag in [inkex.addNS('metadata', 'svg'), 'metadata']: + pass + + elif node.tag in [inkex.addNS('defs', 'svg'), 'defs']: + pass + + elif node.tag in [inkex.addNS('namedview', 'sodipodi'), 'namedview']: + pass + + elif node.tag in [inkex.addNS('eggbot', 'svg'), 'eggbot']: + pass + + elif node.tag in [inkex.addNS('text', 'svg'), 'text']: + inkex.errormsg('Warning: unable to draw text, please convert it to a path first.') + pass + + elif not isinstance(node.tag, basestring): + pass + + else: + inkex.errormsg('Warning: unable to draw object <{}>, please convert it to a path first.'.format(node.tag)) + pass + + def joinWithNode(self, node, path, make_group=False, clone_transform=None): + + """ + Generate a SVG element containing the path data "path". + Then put this new element into a with the supplied + node. This means making a new element and making the + node a child of it with the new as a sibling. + """ + + if (not path) or (len(path) == 0): + return + + if make_group: + # Make a new SVG element whose parent is the parent of node + parent = node.getparent() + # was: if not parent: + if parent is None: + parent = self.document.getroot() + g = etree.SubElement(parent, inkex.addNS('g', 'svg')) + + # Move node to be a child of this new element + g.append(node) + + # Promote the node's transform to the new parent group + # This way, it will apply to the original paths and the + # "twisted" paths + transform = node.get('transform') + if transform: + g.set('transform', transform) + del node.attrib['transform'] + else: + g = node.getparent() + + # Now make a element which contains the twist & is a child + # of the new element + style = {'stroke': '#000000', 'fill': 'none', 'stroke-width': '1'} + line_attribs = {'style': str(inkex.Style(style)), 'd': path} + if (clone_transform is not None) and (clone_transform != ''): + line_attribs['transform'] = clone_transform + etree.SubElement(g, inkex.addNS('path', 'svg'), line_attribs) + + def twist(self, ratio): + + if not self.paths: + return + + # Now iterate over all of the polygons + for path in self.paths: + for subpath in self.paths[path]: + for i in range(len(subpath) - 1): + x = subpath[i][0] + ratio * (subpath[i + 1][0] - subpath[i][0]) + y = subpath[i][1] + ratio * (subpath[i + 1][1] - subpath[i][1]) + subpath[i] = [x, y] + subpath[-1] = subpath[0] + + def draw(self, make_group=False): + + """ + Draw the edges of the current list of vertices + """ + + if not self.paths: + return + + # Now iterate over all of the polygons + for path in self.paths: + for subpath in self.paths[path]: + pdata = '' + for vertex in subpath: + if pdata == '': + pdata = 'M {:f},{:f}'.format(vertex[0], vertex[1]) + else: + pdata += ' L {:f},{:f}'.format(vertex[0], vertex[1]) + self.joinWithNode(path, pdata, make_group, self.paths_clone_transform[path]) + + def effect(self): + + # Build a list of the vertices for the document's graphical elements + if self.options.ids: + # Traverse the selected objects + for id_ in self.options.ids: +# self.recursivelyTraverseSvg([self.svg.selected[id_]]) + self.recursivelyTraverseSvg([self.svg.getElementById(id_)]) + else: + # Traverse the entire document + self.recursivelyTraverseSvg(self.document.getroot()) + + # Now iterate over the vertices N times + for n in range(self.options.nSteps): + self.twist(self.options.fRatio) + self.draw(n == 0) + + +if __name__ == '__main__': + Twist().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/001_Deckblatt.jpg b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/001_Deckblatt.jpg new file mode 100644 index 0000000..50e0add Binary files /dev/null and b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/001_Deckblatt.jpg differ diff --git a/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/002_MagischeFormel.jpg b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/002_MagischeFormel.jpg new file mode 100644 index 0000000..36a553b Binary files /dev/null and b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/002_MagischeFormel.jpg differ diff --git a/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/003_FAQ.jpg b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/003_FAQ.jpg new file mode 100644 index 0000000..b526389 Binary files /dev/null and b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/003_FAQ.jpg differ diff --git a/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/004_Uebersicht.jpg b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/004_Uebersicht.jpg new file mode 100644 index 0000000..812ad1b Binary files /dev/null and b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/004_Uebersicht.jpg differ diff --git a/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/044_regenAufMachuPicchu.jpg b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/044_regenAufMachuPicchu.jpg new file mode 100644 index 0000000..93837d4 Binary files /dev/null and b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/044_regenAufMachuPicchu.jpg differ diff --git a/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/044_regenAufMachuPicchu_text.jpg b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/044_regenAufMachuPicchu_text.jpg new file mode 100644 index 0000000..aef7004 Binary files /dev/null and b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/044_regenAufMachuPicchu_text.jpg differ diff --git a/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/047_verrueckterRoboter.jpg b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/047_verrueckterRoboter.jpg new file mode 100644 index 0000000..a0ec5e7 Binary files /dev/null and b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/047_verrueckterRoboter.jpg differ diff --git a/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/047_verrueckterRoboter_text.jpg b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/047_verrueckterRoboter_text.jpg new file mode 100644 index 0000000..4748bd6 Binary files /dev/null and b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/047_verrueckterRoboter_text.jpg differ diff --git a/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/054_kreisrundesChaos.jpg b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/054_kreisrundesChaos.jpg new file mode 100644 index 0000000..cdd7422 Binary files /dev/null and b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/054_kreisrundesChaos.jpg differ diff --git a/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/054_kreisrundesChaos_text.jpg b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/054_kreisrundesChaos_text.jpg new file mode 100644 index 0000000..a9a427f Binary files /dev/null and b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/054_kreisrundesChaos_text.jpg differ diff --git a/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/061_dieRose.jpg b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/061_dieRose.jpg new file mode 100644 index 0000000..d638292 Binary files /dev/null and b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/061_dieRose.jpg differ diff --git a/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/061_dieRose_text.jpg b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/061_dieRose_text.jpg new file mode 100644 index 0000000..aadf1c7 Binary files /dev/null and b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/061_dieRose_text.jpg differ diff --git a/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/089_dieArbeitskatze.jpg b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/089_dieArbeitskatze.jpg new file mode 100644 index 0000000..d4812b1 Binary files /dev/null and b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/089_dieArbeitskatze.jpg differ diff --git a/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/089_dieArbeitskatze_text.jpg b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/089_dieArbeitskatze_text.jpg new file mode 100644 index 0000000..ddfa6e1 Binary files /dev/null and b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/089_dieArbeitskatze_text.jpg differ diff --git a/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/093_Labyrinth.jpg b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/093_Labyrinth.jpg new file mode 100644 index 0000000..7530768 Binary files /dev/null and b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/093_Labyrinth.jpg differ diff --git a/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/093_Labyrinth_text.jpg b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/093_Labyrinth_text.jpg new file mode 100644 index 0000000..23aabb5 Binary files /dev/null and b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/093_Labyrinth_text.jpg differ diff --git a/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/099_pilzigeSonne.jpg b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/099_pilzigeSonne.jpg new file mode 100644 index 0000000..4cd407c Binary files /dev/null and b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/099_pilzigeSonne.jpg differ diff --git a/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/099_pilzigeSonne_text.jpg b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/099_pilzigeSonne_text.jpg new file mode 100644 index 0000000..d05c46a Binary files /dev/null and b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/099_pilzigeSonne_text.jpg differ diff --git a/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/102_NachosMitGitarre.jpg b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/102_NachosMitGitarre.jpg new file mode 100644 index 0000000..4451cc7 Binary files /dev/null and b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/102_NachosMitGitarre.jpg differ diff --git a/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/102_NachosMitGitarre_text.jpg b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/102_NachosMitGitarre_text.jpg new file mode 100644 index 0000000..616fd86 Binary files /dev/null and b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/102_NachosMitGitarre_text.jpg differ diff --git a/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/119_8bitDragon.jpg b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/119_8bitDragon.jpg new file mode 100644 index 0000000..51afbd1 Binary files /dev/null and b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/119_8bitDragon.jpg differ diff --git a/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/119_8bitDragonText.jpg b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/119_8bitDragonText.jpg new file mode 100644 index 0000000..8469560 Binary files /dev/null and b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/119_8bitDragonText.jpg differ diff --git a/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/121_Fischlippe.jpg b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/121_Fischlippe.jpg new file mode 100644 index 0000000..8a66c73 Binary files /dev/null and b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/121_Fischlippe.jpg differ diff --git a/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/121_Fischlippe_text.jpg b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/121_Fischlippe_text.jpg new file mode 100644 index 0000000..99411fc Binary files /dev/null and b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/121_Fischlippe_text.jpg differ diff --git a/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/126_derDenkendeNischel.jpg b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/126_derDenkendeNischel.jpg new file mode 100644 index 0000000..2fc1c10 Binary files /dev/null and b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/126_derDenkendeNischel.jpg differ diff --git a/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/126_derDenkendeNischel_text.jpg b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/126_derDenkendeNischel_text.jpg new file mode 100644 index 0000000..53dfea8 Binary files /dev/null and b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/126_derDenkendeNischel_text.jpg differ diff --git a/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/163_theShot.jpg b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/163_theShot.jpg new file mode 100644 index 0000000..274f25e Binary files /dev/null and b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/163_theShot.jpg differ diff --git a/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/163_theShot_text.jpg b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/163_theShot_text.jpg new file mode 100644 index 0000000..29c3749 Binary files /dev/null and b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/163_theShot_text.jpg differ diff --git a/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/Set1.svg b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/Set1.svg new file mode 100644 index 0000000..3875ed7 --- /dev/null +++ b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/Set1.svg @@ -0,0 +1,3635 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/Set2.svg b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/Set2.svg new file mode 100644 index 0000000..2be0da2 --- /dev/null +++ b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/Set2.svg @@ -0,0 +1,1815 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/Set3.svg b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/Set3.svg new file mode 100644 index 0000000..4909077 --- /dev/null +++ b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/Set3.svg @@ -0,0 +1,1569 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/SonnenzeichenVektorskop.pdf b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/SonnenzeichenVektorskop.pdf new file mode 100644 index 0000000..7af9550 Binary files /dev/null and b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/SonnenzeichenVektorskop.pdf differ diff --git a/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/SonnenzeichenVektorskop.svg b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/SonnenzeichenVektorskop.svg new file mode 100644 index 0000000..428f201 --- /dev/null +++ b/extensions/fablabchemnitz/vektorkollektor/SonnenzeichenVektorskop/SonnenzeichenVektorskop.svg @@ -0,0 +1,1353 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/extensions/fablabchemnitz/vektorkollektor/meta.json b/extensions/fablabchemnitz/vektorkollektor/meta.json new file mode 100644 index 0000000..1f7e5ce --- /dev/null +++ b/extensions/fablabchemnitz/vektorkollektor/meta.json @@ -0,0 +1,22 @@ +[ + { + "name": "Vektorkollektor", + "id": "fablabchemnitz.de.vektorkollektor", + "path": "vektorkollektor", + "dependent_extensions": null, + "original_name": "Vektorkollektor", + "original_id": "fablabchemnitz.de.vektorkollektor", + "license": "GNU GPL v3", + "license_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/LICENSE", + "comment": "Created by Mario Voigt. See http://www.vektorkollektor.com", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/vektorkollektor", + "fork_url": null, + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Vektorkollektor", + "inkscape_gallery_url": null, + "main_authors": [ + "niklasroy.com/Niklas Roy", + "katihyyppa.com/Kati Hyyppä", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/vektorkollektor/vektorkollektor.inx b/extensions/fablabchemnitz/vektorkollektor/vektorkollektor.inx new file mode 100644 index 0000000..731fbb8 --- /dev/null +++ b/extensions/fablabchemnitz/vektorkollektor/vektorkollektor.inx @@ -0,0 +1,64 @@ + + + Vektorkollektor + fablabchemnitz.de.vektorkollektor + + + + SonnenzeichenVektorskop/SonnenzeichenVektorskop.svg + + + + + + http://www.vektorkollektor.com/vektordaten/vektorkollektor_optimized.js + C:\Users\ + false + + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../000_about_fablabchemnitz.svg + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/vektorkollektor/vektorkollektor.py b/extensions/fablabchemnitz/vektorkollektor/vektorkollektor.py new file mode 100644 index 0000000..13ff55d --- /dev/null +++ b/extensions/fablabchemnitz/vektorkollektor/vektorkollektor.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +''' +An extension to generate SVG files from plt files (Niklas Roy/Kai Hyyppä from Vektorkollektor) + +Made by FabLab Chemnitz / Stadtfabrikanten e.V. - Developer: Mario Voigt (year 2021) + +Vektorkollektor Team: Kati Hyyppa [http://katihyyppa.com] & Niklas Roy [https://niklasroy.com] +http://vektorkollektor.com +''' + +import urllib.request +from lxml import etree +import inkex +from inkex import PathElement, Path +import re +import os +from ast import literal_eval + +class Vektorkollektor(inkex.EffectExtension): + + def add_arguments(self, pars): + pars.add_argument("--tab") + pars.add_argument("--vk_inputfile") + pars.add_argument("--download_or_local", default='local') + pars.add_argument("--showinfo", type=inkex.Boolean, default=False) + pars.add_argument("--vk_url", default="http://www.vektorkollektor.com/vektordaten/vektorkollektor.js") + pars.add_argument("--vk_id", type=int, default=1) + + def effect(self): + + if self.options.download_or_local == "local": + if not os.path.exists(self.options.vk_inputfile): + self.msg("The input file does not exist. Please select a proper file and try again.") + exit(1) + with open(self.options.vk_inputfile, 'r') as file: + vkData = file.read() + else: + # Download the recent vektorkollektor data file and parse it + handler = urllib.request.HTTPBasicAuthHandler() + opener = urllib.request.build_opener(handler) + vkData = opener.open(self.options.vk_url).read().decode("utf-8") + urllib.request.install_opener(opener) + + try: + xP = [] #x-coordinate + yP = [] #y-coordinate + cP = [] #draw + fN = [] #original vektorkollektor .PLT file number + + for match in re.compile(r"""^var xP = .*;""", re.MULTILINE).finditer(vkData): + xP = literal_eval(match.group(0).split("var xP = ")[1].split(";")[0]) + for match in re.compile(r"""^var yP = .*;""", re.MULTILINE).finditer(vkData): + yP = literal_eval(match.group(0).split("var yP = ")[1].split(";")[0]) + for match in re.compile(r"""^var cP = .*;""", re.MULTILINE).finditer(vkData): + cP = literal_eval(match.group(0).split("var cP = ")[1].split(";")[0]) + for match in re.compile(r"""^var fN = .*;""", re.MULTILINE).finditer(vkData): + fN = literal_eval(match.group(0).split("var fN = ")[1].split(";")[0]) + + if self.options.showinfo is True: + self.msg("Input file contains {} drawings.".format(fN[-2])) #last number is always 0, so we use the second last + + vkGroup = self.document.getroot().add(inkex.Group(id="vektorkollektor-{}".format(self.options.vk_id))) + for move in range(0, len(fN)): + begin = None + end = None + if fN[move] == self.options.vk_id: + if cP[move] == 0: #pen up (reset) + begin = None + end = None + #self.msg("xP={}, yP={}, pen up".format(xP[move], yP[move])) + if cP[move] == 1:#pen down + begin = [xP[move-1], yP[move-1]] + end = [xP[move], yP[move]] + #self.msg("xP={}, yP={}, pen down".format(xP[move], yP[move])) + if begin is not None and end is not None: + newLine = PathElement(id="vkLine-{}".format(move)) + newLine.path = Path("M {},{} L {},{}".format(begin[0], begin[1], end[0], end[1])) + newLine.style = "fill:none;stroke:#000000;stroke-width:1;stroke-linecap:round" + vkGroup.append(newLine) + + if self.options.showinfo is True: + self.msg("The drawing with id {} contains {} drawable vectors.".format(self.options.vk_id, len(vkGroup))) + + + except Exception as e: + inkex.errormsg(e) + +if __name__ == "__main__": + Vektorkollektor().run() diff --git a/extensions/fablabchemnitz/vektorkollektor/vektorkollektor_optimized.js b/extensions/fablabchemnitz/vektorkollektor/vektorkollektor_optimized.js new file mode 100644 index 0000000..8cf28d5 --- /dev/null +++ b/extensions/fablabchemnitz/vektorkollektor/vektorkollektor_optimized.js @@ -0,0 +1,20 @@ +/*** +* ___ ___ _______ ___ __ _________ ________ ________ ___ __ ________ ___ ___ _______ ___ __ _________ ________ ________ +* |\ \ / /|\ ___ \ |\ \|\ \ |\___ ___\\ __ \|\ __ \|\ \|\ \ |\ __ \|\ \ |\ \ |\ ___ \ |\ \|\ \ |\___ ___\\ __ \|\ __ \ +* \ \ \ / / | \ __/|\ \ \/ /|\|___ \ \_\ \ \|\ \ \ \|\ \ \ \/ /|\ \ \|\ \ \ \ \ \ \ \ \ __/|\ \ \/ /|\|___ \ \_\ \ \|\ \ \ \|\ \ +* \ \ \/ / / \ \ \_|/_\ \ ___ \ \ \ \ \ \ \\\ \ \ _ _\ \ ___ \ \ \\\ \ \ \ \ \ \ \ \ \_|/_\ \ ___ \ \ \ \ \ \ \\\ \ \ _ _\ +* \ \ / / \ \ \_|\ \ \ \\ \ \ \ \ \ \ \ \\\ \ \ \\ \\ \ \\ \ \ \ \\\ \ \ \____\ \ \____\ \ \_|\ \ \ \\ \ \ \ \ \ \ \ \\\ \ \ \\ \| +* \ \__/ / \ \_______\ \__\\ \__\ \ \__\ \ \_______\ \__\\ _\\ \__\\ \__\ \_______\ \_______\ \_______\ \_______\ \__\\ \__\ \ \__\ \ \_______\ \__\\ _\ +* \|__|/ \|_______|\|__| \|__| \|__| \|_______|\|__|\|__|\|__| \|__|\|_______|\|_______|\|_______|\|_______|\|__| \|__| \|__| \|_______|\|__|\|__| +* +* +* This file contains a list of vector drawings in the form of coordinates. +* The vectors were collected by Kati Hyyppa [http://katihyyppa.com] & Niklas Roy [https://niklasroy.com] with the VEKTORKOLLEKTOR [http://vektorkollektor.com]. +* Legend: xP[] = x-coordinate // yP[] = y-coordinate // cP[] = draw // fN[] = original vektorkollektor .PLT file number +* +* The content of this file is in the PUBLIC DOMAIN / License: CC0 +*/ +var xP = [22,22,22,42,42,42,42,66,66,76,76,76,76,66,66,66,66,74,82,82,96,94,94,80,80,84,84,100,100,122,110,110,122,134,134,134,134,130,130,126,126,118,118,118,118,124,124,124,140,140,140,150,150,150,150,140,148,160,160,160,66,66,66,80,72,74,74,94,94,102,102,106,106,106,106,102,102,100,100,96,96,96,114,114,114,120,130,122,122,122,132,140,140,140,140,138,138,136,136,132,132,132,132,146,148,148,148,152,152,162,162,164,156,156,156,162,162,162,168,174,168,164,168,168,176,184,184,184,184,178,178,176,176,174,174,174,174,182,188,188,188,198,198,198,198,194,194,186,186,186,186,208,208,200,196,190,186,180,176,170,166,160,156,150,144,138,134,130,126,120,116,112,108,104,100,96,92,88,84,80,76,72,68,66,60,54,48,42,36,30,24,18,12,4,20,21,20,20,20,28,28,28,28,16,26,26,22,22,30,48,48,48,48,52,52,52,52,48,48,48,48,44,44,44,44,40,40,40,40,36,36,36,36,32,32,32,32,28,28,28,28,24,24,24,84,98,98,110,110,128,128,128,128,126,126,98,98,98,98,94,94,94,136,78,82,132,132,66,110,110,102,102,130,182,182,182,182,188,188,188,188,184,184,184,184,178,178,178,178,184,184,184,184,168,168,168,168,158,158,158,158,166,166,166,166,148,148,148,148,136,136,136,136,148,148,148,148,130,130,130,130,122,122,122,122,130,130,130,130,124,194,218,36,37,36,48,48,50,50,50,50,38,38,38,26,14,26,88,88,88,88,72,80,80,80,100,100,100,100,164,164,164,164,126,16,17,16,16,16,26,26,26,26,50,50,50,42,30,30,30,36,46,36,32,32,32,32,42,32,32,1,2,2,28,28,28,28,40,40,40,40,56,56,66,66,66,66,78,78,78,78,64,64,64,64,94,94,94,94,110,110,96,96,96,96,124,124,124,16,17,16,16,16,44,44,44,44,24,24,24,24,58,58,58,58,98,98,98,98,116,12,20,28,38,54,60,68,74,88,88,88,88,88,88,140,140,140,136,136,134,134,134,134,136,136,136,136,134,134,132,132,134,134,136,132,134,134,132,132,132,134,132,132,132,132,134,134,122,12,12,12,12,12,12,12,12,12,12,24,28,34,35,34,22,22,22,2,2,2,2,2,2,2,2,2,2,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100,102,104,106,108,110,112,114,116,118,120,122,124,126,128,130,132,134,136,138,140,142,144,146,148,150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180,182,184,186,188,190,192,194,196,198,200,202,204,206,208,210,212,214,216,218,12,13,12,12,12,16,16,20,20,22,22,26,26,26,26,24,24,22,22,18,18,18,18,26,26,28,44,52,52,62,62,66,66,66,66,74,74,82,82,86,86,86,86,82,82,72,72,72,72,78,78,84,84,88,88,88,88,82,88,84,86,94,94,94,94,94,94,88,86,84,84,84,84,98,98,100,100,104,104,104,104,106,106,110,110,122,122,122,122,134,124,122,122,122,122,124,124,128,128,130,130,130,130,126,122,122,122,128,128,134,134,146,146,146,146,150,150,154,154,156,156,156,156,152,110,96,94,94,94,104,104,104,104,100,100,96,96,96,102,106,106,108,108,108,108,106,106,104,104,98,98,98,98,98,98,110,110,112,112,114,114,114,114,122,122,114,114,112,112,112,112,122,122,124,124,128,128,128,128,124,124,122,124,136,136,142,142,142,142,140,140,140,140,146,146,150,150,150,150,148,148,142,142,140,140,142,142,150,150,164,164,164,164,156,156,154,154,154,154,158,158,162,162,174,174,174,174,170,170,168,168,166,166,166,166,182,182,182,182,170,170,170,170,180,180,182,182,182,182,180,180,172,172,170,170,170,170,174,180,188,188,188,188,188,188,192,192,200,200,200,200,194,194,192,192,192,192,194,194,196,196,200,200,200,194,202,202,204,204,210,210,210,210,210,210,212,212,218,202,176,12,13,12,36,36,36,36,26,26,26,26,42,42,62,62,62,62,56,56,54,54,54,54,58,58,74,80,86,94,100,106,112,120,128,136,142,150,154,162,164,174,182,190,194,204,212,212,212,212,212,212,212,212,212,212,212,212,212,208,202,198,194,24,25,24,36,36,36,36,48,2,2,2,2,2,2,2,2,2,2,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100,102,104,106,108,110,112,114,116,118,120,122,124,126,128,130,132,134,136,138,140,142,144,146,148,150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180,182,184,186,188,190,192,194,196,198,200,202,204,206,208,210,212,214,216,218,48,49,48,48,48,66,66,66,66,48,48,48,54,58,50,64,66,84,84,84,84,72,72,72,72,82,82,82,82,88,88,88,88,94,94,94,94,98,98,98,98,100,100,100,100,102,102,102,102,104,104,104,104,106,106,106,106,120,120,120,138,138,138,120,114,102,102,102,102,116,124,138,138,138,132,132,132,132,132,122,116,94,94,94,94,118,122,138,142,154,154,154,154,124,116,100,92,88,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,16,17,16,16,16,28,28,28,28,34,34,34,34,28,28,28,28,14,14,14,40,40,46,56,56,56,56,44,44,44,44,60,66,66,66,78,88,102,102,102,102,86,86,86,118,118,118,108,108,108,108,118,114,114,114,122,122,122,122,102,102,102,102,118,118,124,136,136,136,150,150,134,144,156,156,156,156,144,144,144,144,162,170,170,170,178,200,190,190,190,190,202,196,196,208,208,208,208,186,170,160,154,148,140,134,126,120,112,106,98,92,84,76,68,60,52,46,38,30,22,16,8,1,2,2,2,2,62,62,62,62,118,118,118,118,156,156,156,156,150,150,118,118,60,60,60,60,56,56,32,32,12,12,12,12,46,46,46,46,88,88,88,88,122,122,122,122,88,88,90,90,150,150,150,150,134,134,134,134,164,164,164,164,142,142,142,142,122,122,122,122,158,158,158,158,68,68,68,68,170,170,170,170,58,58,58,58,78,78,78,76,104,104,104,104,60,60,94,94,42,42,42,42,54,54,54,54,16,16,16,16,34,34,34,34,12,12,12,12,188,188,188,188,202,202,202,202,186,192,192,196,196,188,200,200,200,188,188,192,192,194,194,198,198,200,200,202,208,208,208,208,212,212,212,212,206,206,206,192,192,192,188,188,188,182,182,182,186,210,202,202,202,202,206,206,206,206,212,212,212,208,208,214,214,214,212,200,206,206,206,206,202,202,202,194,190,190,190,186,182,182,182,176,180,180,180,180,176,176,176,176,180,168,168,168,172,172,172,50,50,50,44,44,44,44,50,56,56,58,64,64,64,64,60,60,60,60,64,68,68,68,72,72,72,72,76,76,76,76,72,72,72,80,80,80,86,86,86,86,80,86,86,86,56,90,96,96,96,96,90,90,90,90,98,104,110,110,110,110,104,104,104,114,120,120,120,120,114,114,114,122,128,128,128,128,122,122,122,90,166,166,166,166,144,144,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,44,70,70,70,70,102,86,98,98,98,98,86,86,86,86,102,108,108,108,120,120,120,112,112,112,132,126,140,132,132,132,144,140,152,152,152,152,140,140,140,158,158,158,178,178,178,178,154,168,212,10,10,10,38,38,38,18,18,18,58,40,40,40,52,52,52,52,38,58,58,58,74,66,66,66,80,76,86,86,86,86,74,74,74,74,90,96,96,96,108,108,108,100,100,100,136,126,138,132,132,132,140,136,136,136,148,148,148,148,134,152,152,152,188,188,188,188,150,166,166,166,216,216,218,218,206,200,190,184,172,164,152,146,134,128,116,108,96,90,78,72,60,54,40,32,20,14,6,6,6,6,6,6,6,6,6,6,6,6,6,6,18,24,28,34,46,52,62,68,80,86,96,102,114,118,128,134,144,150,162,168,180,186,198,204,218,218,218,218,218,218,218,218,218,218,218,218,218,218,218,32,32,32,68,68,68,68,22,44,44,44,70,70,70,70,22,36,36,36,48,48,48,48,34,50,50,50,64,64,64,64,40,58,58,58,62,62,62,62,56,56,56,40,46,46,46,46,42,42,42,64,64,64,34,34,34,46,46,58,58,58,40,40,14,14,14,14,26,26,30,30,34,34,34,34,28,28,26,28,38,38,40,40,40,40,54,54,54,54,56,56,62,62,62,62,68,78,66,78,78,78,70,70,70,70,72,72,72,72,54,40,38,38,34,34,34,34,52,52,52,52,70,70,70,70,54,54,52,50,50,50,48,48,40,40,36,60,60,60,56,56,56,56,52,52,52,36,36,36,48,48,48,48,54,54,54,54,68,68,68,74,74,76,76,26,26,30,30,38,38,38,34,34,30,30,28,28,28,28,46,46,46,56,56,56,74,74,74,74,68,68,68,68,66,66,60,64,68,62,62,62,66,84,84,84,86,86,122,122,122,122,130,130,166,166,166,166,82,92,92,92,110,110,110,142,142,142,146,146,150,150,162,162,162,162,154,154,128,128,128,128,132,132,142,142,164,164,164,126,126,126,118,118,118,118,134,134,134,134,126,120,120,120,132,132,132,128,128,126,126,126,126,126,126,138,142,148,154,162,170,112,106,98,92,86,78,56,56,56,46,46,46,46,62,62,62,62,56,54,54,150,150,150,152,152,156,158,162,162,168,168,168,168,148,158,158,144,178,104,104,104,78,78,78,78,108,108,108,108,76,76,76,114,114,114,114,142,142,142,118,118,118,118,142,142,142,142,124,148,148,148,158,158,158,158,170,170,170,176,176,176,188,188,188,188,174,196,196,196,212,180,180,182,182,186,186,188,188,140,140,134,134,138,138,108,108,108,84,84,84,84,110,98,86,86,86,86,106,106,106,106,88,88,88,88,106,98,98,98,104,104,104,104,94,94,94,148,156,156,156,150,146,146,146,128,144,128,152,134,168,132,132,138,138,146,146,152,152,158,158,106,102,98,94,88,82,88,94,98,102,106,110,114,114,114,114,84,84,54,42,42,42,42,48,48,58,58,58,58,62,62,48,60,60,60,54,48,60,42,28,28,28,22,30,30,30,30,22,22,22,34,34,34,40,40,40,40,32,26,26,26,52,52,52,52,26,26,26,26,62,62,62,56,68,68,68,68,56,56,66,66,66,62,54,42,42,56,56,62,20,20,20,20,38,38,38,38,58,58,58,58,88,88,88,88,54,30,30,30,44,44,44,48,48,42,42,38,38,32,32,28,28,28,54,60,90,90,90,90,174,174,174,174,98,98,98,98,144,144,144,150,110,110,152,152,152,152,142,110,110,110,94,94,94,94,110,102,102,94,112,92,64,68,60,60,60,60,96,70,70,70,50,50,50,50,44,44,44,44,68,68,68,68,60,44,30,30,30,30,40,40,40,40,44,44,44,44,50,50,50,50,54,54,54,54,58,58,58,58,64,64,64,64,50,50,50,70,70,70,56,56,56,56,86,86,86,92,80,80,80,80,102,102,108,108,92,86,86,86,70,70,70,70,98,98,98,92,92,92,58,58,58,58,166,166,166,166,170,170,170,170,164,164,138,138,138,168,176,176,176,176,182,182,182,182,168,174,186,186,186,178,178,178,186,186,186,186,180,180,180,180,172,174,180,180,180,180,196,196,196,196,186,186,186,186,190,190,190,190,184,184,184,184,198,198,198,198,182,94,94,94,100,100,100,106,106,112,112,112,118,118,118,118,84,122,122,122,128,128,128,134,130,130,130,130,136,136,136,136,132,132,132,140,140,140,148,148,148,148,130,108,108,108,116,116,116,116,106,114,114,114,106,118,118,118,126,126,126,126,116,116,116,116,218,218,218,136,128,128,128,128,138,138,128,140,140,140,146,146,146,146,138,150,150,150,154,154,170,156,156,156,218,218,218,176,170,170,170,170,176,176,176,182,188,188,188,188,178,178,178,178,182,188,190,190,190,190,196,196,196,202,196,196,196,196,202,208,214,214,214,214,206,206,206,206,218,214,196,186,154,146,100,66,62,24,2,4,4,10,10,14,14,18,18,18,18,18,18,22,22,22,22,22,22,22,164,164,164,168,168,168,168,168,182,182,182,182,190,190,190,190,218,26,27,30,20,20,20,30,38,38,38,52,52,52,70,70,70,70,52,52,52,54,58,58,66,66,66,70,72,72,72,72,68,68,68,68,64,64,52,52,46,68,68,68,64,64,62,62,62,62,60,60,54,54,54,54,48,14,14,14,28,28,28,28,16,16,16,16,34,34,34,34,24,24,24,24,46,46,46,46,30,30,30,30,48,48,48,48,40,40,40,40,60,60,60,60,50,50,50,50,58,58,58,58,52,52,52,52,56,56,62,62,62,62,48,42,42,42,76,76,76,76,72,52,36,36,36,36,72,72,106,106,106,106,72,72,72,72,82,82,92,92,94,94,94,94,84,84,80,80,80,80,52,52,52,52,52,52,68,68,76,76,120,120,120,120,116,116,86,86,76,76,90,90,110,110,114,114,114,114,94,94,94,94,114,114,114,114,104,104,94,94,80,80,80,80,112,112,112,112,90,90,90,90,110,110,112,112,120,120,120,120,114,114,100,100,98,98,88,88,78,78,108,108,110,110,110,110,108,108,76,68,66,66,64,64,64,64,72,72,132,132,132,132,108,108,108,108,112,112,120,120,122,122,134,134,136,136,136,136,130,130,124,124,114,114,114,114,128,128,142,142,150,150,150,150,146,146,146,146,160,160,160,160,152,152,156,156,162,162,158,158,158,158,160,160,156,156,158,158,156,156,156,156,154,154,156,156,154,154,122,122,122,122,154,154,154,154,144,144,102,102,98,98,68,68,58,58,58,58,60,60,140,140,140,140,126,126,126,126,132,132,156,156,158,158,158,158,156,156,86,86,66,66,66,66,70,70,78,78,86,86,98,98,176,176,176,176,172,172,138,138,122,122,122,122,134,134,154,156,168,168,168,168,160,160,156,156,144,144,130,130,128,128,126,126,124,124,122,122,122,122,128,128,124,124,126,124,122,122,116,116,110,110,94,94,74,74,74,74,78,88,90,90,108,108,112,112,112,112,96,96,90,90,76,76,78,82,170,170,170,170,156,156,156,156,168,168,162,162,140,144,154,154,156,156,156,156,170,170,178,178,180,180,180,180,184,184,188,188,190,190,188,188,188,188,190,190,194,194,194,194,82,82,82,82,120,120,122,122,176,176,194,194,194,194,192,192,186,186,166,166,166,166,192,192,188,188,178,178,178,178,200,200,170,170,172,172,176,176,196,196,206,206,202,202,168,168,156,156,156,156,184,184,184,184,154,154,154,154,156,156,182,182,186,186,186,186,190,190,200,200,200,200,196,196,128,128,118,118,118,118,142,142,144,144,144,144,142,142,136,136,126,124,124,124,140,140,154,154,160,160,160,160,176,176,176,176,172,172,156,152,130,130,126,126,126,126,120,120,120,132,146,150,166,166,166,166,142,142,78,76,56,56,56,56,64,64,100,100,140,140,150,150,158,158,158,158,154,154,124,124,112,112,100,88,52,52,30,30,2,2,2,2,78,78,108,108,138,138,156,156,156,156,150,150,98,98,96,96,96,96,94,94,72,72,44,44,14,14,2,2,2,2,50,50,68,68,80,80,94,94,216,216,216,216,194,194,194,194,208,208,208,208,204,204,170,170,132,132,122,122,206,206,206,206,194,190,188,188,188,188,198,198,202,202,202,202,194,194,202,202,198,198,202,202,202,202,200,200,202,202,200,200,200,200,202,202,200,200,210,210,214,214,214,214,186,188,216,216,216,216,198,198,198,198,192,192,114,114,108,108,106,106,82,82,38,38,38,38,54,54,102,102,138,138,138,138,94,94,74,74,180,180,180,180,178,178,176,176,172,172,112,64,30,30,4,4,4,4,2,2,2,2,4,4,20,20,30,30,36,36,38,38,16,16,22,22,40,40,40,40,84,84,92,92,92,92,86,86,10,10,2,2,2,2,12,12,32,32,66,66,70,70,70,70,56,56,64,64,64,64,60,60,66,66,74,74,78,78,78,78,68,68,66,66,38,30,20,20,2,2,2,2,24,24,28,28,30,30,30,30,14,14,56,56,56,56,42,42,38,38,22,22,22,22,12,12,12,12,18,18,18,18,8,8,20,20,20,20,18,18,18,18,20,20,20,20,22,20,20,20,22,22,20,20,22,22,20,20,22,22,20,20,22,22,20,20,22,22,14,14,16,16,18,16,4,18,36,36,14,14,32,32,32,32,20,20,14,20,60,68,68,68,42,42,42,42,66,66,60,40,34,34,34,34,34,34,36,36,104,104,104,104,86,86,42,38,18,18,18,18,32,32,40,40,66,68,86,98,130,130,148,156,158,158,168,168,172,172,174,174,152,152,136,136,136,136,174,174,178,178,178,178,166,166,166,166,168,168,168,168,212,212,172,172,172,172,174,174,174,174,164,164,164,164,170,170,214,214,214,214,184,184,204,204,204,204,82,82,82,82,96,96,138,138,138,138,126,126,122,118,104,104,102,102,84,84,84,94,148,148,154,154,180,180,180,180,142,142,142,142,144,144,154,154,208,208,208,208,208,208,206,206,178,178,152,152,152,152,166,166,218,218,218,218,154,154,162,162,174,174,174,174,130,130,116,116,116,116,94,94,110,110,120,120,120,116,58,58,58,58,58,58,18,18,16,16,8,8,8,8,38,38,32,14,12,12,12,12,12,12,22,22,30,38,68,68,88,88,122,122,138,138,142,142,142,142,148,148,148,148,142,136,120,120,70,70,70,70,56,56,22,22,22,22,2,2,2,2,2,2,2,2,48,48,46,36,14,14,2,2,30,30,48,48,50,50,50,50,80,86,218,218,218,218,214,214,206,206,190,190,194,194,200,200,218,218,218,218,206,206,206,206,218,218,216,216,216,216,210,210,194,194,192,192,192,192,204,204,206,206,204,194,192,192,196,196,214,214,214,214,212,212,210,194,144,138,138,138,150,144,134,134,134,134,124,124,112,108,2,74,75,74,68,68,64,64,64,64,78,78,90,90,94,94,94,94,86,86,70,70,70,74,74,74,72,72,58,58,50,50,50,50,60,60,60,60,76,76,78,78,78,78,70,70,70,70,78,82,82,86,86,86,90,90,94,94,84,74,66,66,66,66,70,70,78,78,80,80,80,80,74,74,72,72,72,74,74,72,70,70,70,70,74,74,78,78,78,90,90,90,96,96,96,96,94,94,84,78,78,78,92,92,86,86,90,90,102,102,102,102,98,98,94,94,94,94,86,92,94,94,96,96,96,96,94,94,88,88,88,88,90,64,64,64,66,66,94,94,110,110,110,110,104,104,90,90,90,102,102,102,106,106,124,124,124,124,116,116,114,114,114,114,120,120,120,120,126,126,126,126,132,132,132,132,124,124,118,118,112,112,112,112,108,108,108,108,110,110,120,120,120,120,118,118,96,96,92,92,92,90,78,78,78,78,70,70,52,52,48,48,48,48,50,50,66,66,66,66,56,56,56,56,50,50,46,46,46,46,50,50,54,54,54,154,148,148,146,146,146,146,160,160,164,164,172,172,172,172,154,154,150,150,146,146,146,152,158,158,158,158,160,160,162,162,166,182,190,174,174,174,182,160,160,160,162,162,164,150,150,214,206,206,204,204,204,204,200,200,198,198,198,198,190,190,188,188,188,188,178,178,176,176,176,176,160,160,154,154,154,154,154,154,154,154,146,146,140,140,140,140,140,140,136,136,134,134,128,128,128,128,104,102,98,82,74,64,42,42,42,42,34,34,26,26,26,26,2,48,49,48,34,34,34,34,44,44,44,44,50,50,56,56,56,46,64,56,56,62,74,74,74,74,78,72,62,62,62,62,76,78,82,82,82,82,86,86,98,98,98,98,86,86,86,102,102,102,114,114,114,114,102,112,118,108,108,108,118,118,118,118,96,96,96,96,110,124,124,124,132,132,132,132,100,126,144,144,144,144,130,130,130,130,142,142,142,144,150,150,150,150,156,164,164,164,154,154,154,154,166,166,162,2,12,12,12,12,40,40,40,40,80,80,80,80,92,92,92,92,124,124,124,124,130,130,130,130,136,136,136,136,164,164,164,164,170,170,170,170,218,218,218,218,2,2,2,48,56,56,56,56,46,46,46,46,34,34,34,34,48,48,48,48,40,40,40,40,50,50,50,46,74,74,46,42,42,86,78,78,78,78,88,88,80,80,80,80,86,86,86,86,102,102,102,102,112,112,112,112,102,86,94,94,94,94,98,98,94,98,98,88,88,84,84,84,102,102,102,102,84,84,84,92,62,62,84,104,108,108,108,108,102,106,106,36,28,28,28,28,38,30,30,30,70,70,28,28,28,32,28,38,38,38,38,38,38,38,18,18,18,22,22,22,26,26,26,44,44,44,48,48,48,52,52,52,42,42,42,42,42,80,122,122,122,114,114,114,108,108,108,102,102,80,80,1,2,2,16,16,16,16,20,20,34,34,34,34,46,46,52,52,52,52,58,58,58,58,62,62,62,62,64,64,78,78,80,80,116,116,126,126,126,126,130,130,130,130,124,124,130,130,124,124,130,130,130,130,132,132,138,138,140,140,144,144,146,146,150,150,150,150,152,152,154,154,158,158,162,162,164,164,168,168,168,168,164,164,164,164,170,170,174,174,174,174,176,176,182,182,184,184,186,186,190,190,192,192,202,202,202,202,204,204,208,208,212,212,212,212,216,216,218,218,218,190,190,190,188,188,184,184,184,184,190,190,194,194,196,196,192,192,190,190,188,124,124,124,118,118,116,116,116,116,122,122,130,130,134,134,134,134,138,138,142,142,142,142,144,144,146,146,154,154,158,158,160,160,162,162,170,170,170,170,172,172,178,178,178,178,174,174,172,172,170,170,168,168,166,166,166,166,168,168,176,130,138,138,138,138,150,150,150,150,164,164,164,164,168,168,168,168,172,172,188,188,188,188,204,204,204,204,210,210,210,210,218,218,218,172,172,172,178,178,178,178,170,148,148,148,154,154,154,154,148,96,90,90,90,90,96,96,96,84,76,76,76,76,82,82,82,54,54,54,60,60,66,66,68,68,68,68,54,58,58,58,68,68,58,42,18,18,18,18,38,38,38,38,46,46,46,46,42,38,38,32,32,26,26,28,38,46,82,82,92,92,142,142,142,142,146,146,156,156,160,160,176,176,194,194,194,194,218,206,200,200,200,200,206,206,206,186,180,180,180,180,186,186,186,158,158,158,162,162,162,162,156,114,98,98,98,98,116,116,116,116,100,100,116,78,78,82,82,86,86,90,90,94,94,98,98,54,54,50,60,60,50,50,60,38,38,38,34,34,34,34,40,4,216,32,32,32,40,40,40,40,20,20,20,20,42,42,42,42,34,34,34,34,26,26,26,26,32,44,44,44,50,50,50,50,56,56,56,56,62,62,62,62,54,54,54,54,48,48,48,48,42,70,70,70,88,88,88,88,76,76,76,76,80,80,80,80,74,74,74,74,76,76,92,92,92,92,68,68,68,96,96,96,102,102,102,102,110,110,112,112,124,124,124,124,130,130,130,130,122,122,106,106,106,106,98,98,94,94,94,134,134,134,140,140,140,140,148,148,148,148,154,154,154,154,146,146,146,146,138,138,132,132,132,156,156,156,160,160,160,160,154,158,158,158,162,162,162,162,158,140,166,166,166,166,174,174,174,174,194,194,194,140,140,178,178,178,200,198,184,184,180,180,180,180,184,184,184,184,176,176,176,176,202,202,202,202,182,186,196,196,204,204,204,204,196,196,196,196,212,212,212,212,178,178,178,140,146,146,156,160,160,160,166,160,164,164,164,160,168,160,160,160,178,178,180,156,156,156,176,176,178,166,192,192,194,174,174,174,196,196,198,176,176,176,202,202,210,200,200,200,218,218,218,202,202,202,218,218,218,194,194,194,202,202,218,218,218,204,218,218,218,208,208,208,218,218,218,212,216,216,218,218,218,176,180,180,192,180,180,180,184,176,180,192,192,192,194,194,196,196,208,158,162,162,164,154,154,154,174,146,146,146,170,138,138,138,160,160,164,134,134,134,154,154,158,128,128,128,150,150,154,124,124,124,144,144,146,112,114,114,126,100,100,100,120,120,142,94,94,94,112,112,116,106,108,108,110,92,94,94,94,94,96,90,90,90,98,80,98,78,78,78,98,88,98,86,86,86,108,108,112,70,70,70,90,90,106,76,76,76,98,74,74,74,100,62,62,62,72,60,86,86,90,54,82,82,86,50,50,50,58,50,76,76,80,46,46,46,70,70,74,40,40,40,60,60,62,20,44,44,56,46,46,46,56,38,38,38,46,24,24,24,34,30,30,30,46,28,28,28,26,26,24,24,16,16,16,16,22,22,26,26,34,34,34,34,30,30,26,26,24,20,28,28,32,26,26,26,34,34,42,42,46,38,44,32,32,32,42,28,28,10,22,22,22,22,20,16,4,4,14,98,98,98,90,90,90,90,98,98,102,102,112,112,112,112,104,104,94,94,94,100,94,94,94,94,100,100,100,96,96,100,100,90,106,90,108,46,46,46,50,50,50,54,60,60,60,60,54,54,54,54,60,66,66,66,70,74,74,74,80,82,82,82,88,88,88,88,82,102,96,96,96,96,102,102,102,102,96,106,106,106,110,110,110,110,104,112,112,112,116,116,116,120,120,120,126,120,120,120,124,124,124,130,134,134,134,134,128,128,128,128,132,138,138,138,142,142,142,144,144,144,148,148,148,148,142,154,158,158,158,158,154,154,154,154,158,162,162,162,166,174,170,170,170,170,176,176,176,176,174,174,180,172,172,172,176,176,176,176,104,90,76,70,66,46,2,88,88,88,104,104,104,104,86,86,86,98,98,98,92,88,102,98,98,98,90,90,90,94,98,98,142,104,108,108,154,154,124,130,166,166,120,120,120,120,126,126,126,126,78,78,70,70,70,70,74,74,82,82,86,86,86,86,82,78,50,50,48,48,48,48,50,50,56,56,64,64,64,64,58,58,48,48,44,44,44,44,48,48,50,56,62,62,70,60,60,60,76,76,88,92,98,102,110,114,122,126,136,142,150,154,162,166,174,178,184,190,190,190,196,196,196,196,182,182,182,182,174,174,174,174,184,184,184,184,192,192,192,192,186,186,186,186,150,150,150,150,158,158,158,158,164,164,164,164,150,150,150,150,154,154,154,154,82,82,82,82,76,76,76,76,70,70,70,70,64,64,64,64,56,56,56,56,28,28,28,28,94,98,102,106,110,114,120,126,132,138,144,150,156,162,170,176,182,188,196,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,174,138,136,118,108,100,94,90,86,82,76,16,30,46,60,34,34,32,32,32,32,34,34,36,36,34,34,34,34,36,36,40,40,44,44,44,44,42,42,40,40,36,36,36,36,38,38,38,38,36,36,26,26,26,26,32,32,40,40,40,40,38,38,30,30,30,30,34,34,34,34,38,38,40,40,48,48,52,52,56,56,62,62,64,64,70,70,76,76,76,76,74,74,74,74,76,76,88,88,100,100,106,106,110,110,110,110,108,108,108,108,118,118,122,122,122,122,126,42,43,42,80,80,80,80,50,50,40,40,40,40,54,54,54,54,60,60,60,60,70,70,70,70,74,74,76,76,76,52,58,58,58,58,62,62,66,66,66,66,62,62,60,60,56,56,56,56,60,68,64,68,72,72,72,72,74,74,76,76,76,76,78,78,78,78,76,76,74,74,68,78,84,84,84,84,88,38,46,46,54,54,56,56,66,48,48,52,52,58,58,62,62,66,66,78,94,94,98,98,98,98,110,110,128,128,142,142,142,142,140,140,140,140,138,138,138,138,144,144,148,148,150,150,150,150,148,148,132,132,132,132,140,140,130,130,130,130,136,136,136,136,134,134,124,124,120,120,120,120,122,122,122,122,116,116,116,116,120,120,122,122,124,124,124,124,120,120,118,118,106,106,106,106,114,114,114,114,112,112,104,104,96,96,94,94,94,94,96,96,96,96,90,90,90,90,96,96,98,98,102,102,102,102,98,98,94,94,88,88,88,88,90,90,92,92,92,90,84,84,84,84,92,92,92,92,90,90,84,84,80,80,80,80,78,78,78,78,72,72,68,68,66,66,66,66,72,72,72,72,68,68,68,68,86,86,86,86,82,136,136,136,146,146,150,150,150,150,148,148,144,144,140,140,136,136,134,134,134,74,74,74,72,72,70,70,70,70,78,78,78,78,70,70,74,74,74,56,56,56,54,54,56,56,56,56,62,62,56,56,56,2,30,30,32,32,32,32,28,28,24,24,24,30,30,30,34,34,38,38,38,38,44,44,44,44,42,42,42,42,48,52,52,52,54,54,60,60,60,60,70,78,86,98,104,120,138,154,174,174,174,174,170,170,174,174,174,178,178,178,174,174,218,202,206,206,194,194,194,194,210,210,210,210,198,198,192,192,184,184,180,180,176,176,176,176,194,190,190,190,186,186,184,184,184,184,190,14,14,14,18,18,22,22,28,28,28,28,42,42,42,42,42,42,46,46,50,50,54,54,54,54,60,60,62,60,54,54,52,52,52,52,58,58,60,60,74,74,74,74,70,70,66,66,66,66,70,70,74,74,74,74,78,78,82,82,82,82,88,88,90,90,90,90,96,96,98,98,106,106,106,106,106,106,108,108,110,110,112,112,112,112,118,118,122,122,122,122,122,122,130,130,134,134,146,146,146,146,152,152,154,154,154,166,166,166,162,162,156,156,152,152,152,152,154,154,158,158,166,166,166,166,166,166,166,166,160,160,158,158,156,156,156,156,178,88,78,48,20,18,18,18,22,22,26,26,26,22,42,36,36,36,44,44,48,48,64,64,66,66,66,66,68,68,68,68,62,62,60,60,58,58,56,56,56,56,58,58,60,60,66,66,66,66,66,66,78,78,84,84,86,86,86,86,98,98,98,98,94,94,90,90,90,90,106,106,110,110,110,110,110,110,106,106,102,102,102,102,106,106,138,96,96,96,92,86,82,82,82,82,86,114,114,114,112,112,106,106,102,102,102,102,104,104,110,110,116,116,116,116,112,112,108,108,104,104,104,92,86,86,86,86,84,84,76,76,76,76,84,86,88,88,88,88,98,122,122,190,190,198,198,198,206,206,206,42,42,42,78,78,78,78,74,74,72,40,78,68,68,68,72,72,72,62,62,62,64,64,64,64,60,60,60,52,52,52,44,44,44,46,46,46,48,48,48,48,44,44,42,46,54,54,54,54,50,50,46,46,52,66,76,76,76,76,68,68,68,70,70,70,76,48,48,48,52,52,52,50,72,58,58,58,62,62,62,66,66,66,70,70,70,78,86,86,86,86,78,78,86,86,86,86,78,78,86,86,86,86,76,76,88,88,88,88,78,78,90,90,90,90,76,76,84,84,84,84,76,76,86,86,86,86,78,42,34,34,34,34,44,42,30,30,30,30,32,32,44,40,30,30,30,30,32,32,34,34,36,36,42,42,34,34,34,34,42,42,40,40,38,38,30,30,30,30,42,42,32,32,32,32,34,42,30,124,96,96,96,96,132,128,128,146,146,146,120,156,156,156,156,158,154,124,124,164,164,164,182,182,182,182,162,196,196,196,174,174,174,174,202,202,202,202,162,162,162,140,152,176,168,100,100,132,132,154,154,162,162,176,176,198,198,124,158,158,158,158,116,116,116,116,124,158,166,126,126,126,154,154,154,166,170,132,146,134,146,140,140,122,134,134,134,134,124,124,124,140,140,140,152,152,152,152,138,146,146,146,150,150,150,150,146,126,126,130,130,130,124,126,152,138,138,138,144,144,144,140,140,126,126,130,130,142,142,146,146,158,170,170,170,170,156,156,172,172,172,172,156,156,170,170,170,170,154,154,168,168,168,168,154,116,104,104,104,104,118,118,118,118,98,98,98,98,120,114,98,98,98,98,120,116,102,102,102,102,120,90,98,98,98,98,108,168,184,184,184,174,194,178,194,180,194,184,190,108,94,94,82,82,82,82,90,90,104,82,78,78,56,56,54,54,54,54,58,58,60,62,84,84,84,62,62,62,58,58,58,50,44,38,32,26,18,12,6,50,42,36,30,22,14,10,4,62,68,68,68,68,62,62,62,72,62,90,86,86,86,86,94,94,94,94,90,90,90,96,86,82,82,82,82,86,76,82,82,56,56,56,56,62,50,86,86,86,90,90,90,90,94,64,64,64,70,70,70,70,76,118,118,112,128,128,128,128,136,136,136,138,138,138,146,146,146,146,150,150,150,162,152,152,152,152,162,150,162,174,174,174,164,164,164,164,182,182,182,182,158,158,158,198,198,198,212,212,212,212,206,206,206,206,214,196,196,196,200,200,200,200,192,192,192,198,214,200,200,200,194,194,194,194,202,188,188,188,182,182,182,182,188,208,198,198,198,198,194,194,194,194,200,208,208,208,208,208,204,204,204,204,210,210,194,16,16,16,16,16,16,16,16,18,18,18,12,12,12,12,16,16,30,30,30,30,26,26,26,26,32,32,12,82,82,82,78,78,78,78,84,16,22,22,22,22,8,22,10,10,10,20,14,14,14,22,8,8,8,174,174,174,174,50,51,50,50,50,52,52,68,68,68,68,88,88,88,88,104,104,104,104,96,96,96,96,114,114,114,114,118,118,132,122,124,124,146,140,158,150,150,158,170,170,170,170,156,156,156,174,174,174,198,198,198,198,170,188,188,188,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,196,188,182,178,172,168,162,158,154,148,142,138,132,126,122,118,112,108,102,98,92,86,80,74,68,62,56,50,44,38,30,26,20,14,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,8,14,18,24,30,36,42,48,56,62,68,74,80,84,90,96,102,110,116,122,130,136,144,150,156,162,166,172,176,182,188,196,202,206,212,218,218,218,218,218,218,218,218,218,218,218,218,218,218,218,218,218,218,210,204,200,192,188,182,178,170,164,158,154,148,144,138,134,130,126,120,116,110,106,100,94,84,78,72,68,62,58,52,50,42,38,32,26,26,26,26,26,26,26,52,48,48,26,26,30,30,52,52,54,70,80,80,80,80,74,74,70,70,62,62,62,62,74,86,86,86,92,100,94,94,94,104,112,112,112,112,102,102,102,102,114,130,120,120,120,120,122,122,126,126,130,130,130,130,134,134,138,126,126,126,128,128,134,134,136,136,136,136,132,132,126,126,118,118,116,116,106,106,104,104,98,98,94,94,88,88,84,84,78,78,46,136,146,146,148,148,148,148,144,144,138,138,138,138,148,148,218,18,19,18,18,18,24,24,54,56,56,56,62,62,94,58,58,58,66,66,156,158,158,158,156,156,100,100,96,96,96,96,92,92,90,158,192,192,198,198,198,198,196,156,196,196,198,198,198,198,186,186,186,186,196,196,196,196,184,184,184,184,194,194,194,194,182,182,182,182,190,194,138,138,136,136,136,136,140,140,170,170,176,176,176,176,160,160,134,134,104,104,58,58,54,54,54,54,42,42,16,16,4,4,4,4,16,16,30,30,30,30,34,34,36,58,22,22,8,8,8,24,24,24,28,28,50,50,52,52,52,52,72,72,92,92,112,112,164,164,178,140,140,140,132,132,110,110,104,104,104,104,114,114,128,128,138,138,138,94,70,70,62,62,62,62,70,70,70,70,62,62,62,62,72,72,88,88,98,98,98,98,92,96,64,104,138,118,118,118,128,128,130,130,130,130,120,88,88,88,72,72,72,72,86,86,86,106,134,96,60,128,108,128,136,90,58,90,92,158,202,202,206,206,206,206,196,196,196,198,204,204,196,196,196,196,204,204,204,204,196,196,204,204,204,204,196,196,196,196,202,202,202,202,194,194,194,194,200,202,190,202,204,198,198,198,204,204,204,204,194,194,194,180,174,174,162,162,172,172,176,176,188,188,188,188,174,182,170,176,176,38,14,14,12,12,12,12,28,38,38,72,82,116,132,166,166,176,176,198,210,210,210,210,200,200,200,200,210,60,60,60,66,66,70,70,72,60,66,66,74,88,82,82,82,82,94,94,94,106,106,108,108,108,114,106,110,110,120,132,124,124,124,124,136,136,136,136,142,138,134,148,148,148,156,156,156,156,154,154,152,152,148,148,148,156,148,158,158,174,166,166,166,166,178,178,178,178,184,184,184,176,182,36,36,36,78,78,78,78,34,34,34,34,36,36,38,40,40,40,54,54,54,54,38,56,74,74,74,74,56,56,56,56,52,74,82,82,82,82,76,64,64,64,72,72,72,72,62,46,50,50,50,50,44,44,44,40,16,16,16,16,22,22,22,42,42,42,70,70,70,70,40,48,48,56,56,66,66,52,52,52,56,56,56,56,52,60,60,60,66,66,66,66,70,70,70,70,66,74,74,74,80,80,80,80,70,70,70,70,74,74,74,42,42,42,34,34,34,34,38,38,38,38,32,32,32,32,36,34,20,20,20,20,32,32,32,32,32,32,36,32,26,26,26,26,34,34,36,36,36,36,50,50,50,50,46,46,44,44,38,38,38,54,54,54,58,56,48,48,48,48,52,54,58,58,58,58,56,66,60,60,60,60,70,70,68,68,66,66,66,66,72,72,66,76,70,70,70,70,74,74,74,80,74,72,72,72,72,78,74,74,68,68,68,54,54,44,44,44,42,38,38,62,62,62,70,70,70,74,74,74,82,82,82,82,80,74,76,76,82,82,82,82,74,46,46,60,60,60,44,44,44,44,48,48,58,58,58,58,64,64,64,64,54,62,62,62,66,66,84,84,84,46,46,46,28,28,28,38,38,74,74,86,86,56,56,60,60,60,64,64,64,64,60,60,60,60,64,64,64,64,60,60,60,60,64,64,64,64,60,60,60,60,64,64,64,64,62,62,60,60,58,92,98,98,128,128,134,134,160,160,160,160,148,148,144,144,134,134,110,110,106,106,106,106,94,116,116,116,120,120,120,120,126,126,126,126,132,132,132,132,126,126,126,126,118,118,118,118,112,138,146,146,146,146,136,136,136,136,136,136,146,146,146,146,134,150,150,150,154,154,154,154,148,146,154,154,154,154,148,148,148,144,134,106,106,106,106,106,106,106,106,114,114,114,114,114,114,126,126,126,126,126,126,126,126,144,144,144,144,144,144,144,144,158,158,158,158,158,158,158,158,158,158,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,178,178,178,178,178,178,178,178,178,178,178,178,178,178,96,96,96,96,96,96,96,96,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,94,94,94,94,94,94,74,74,74,74,64,64,64,64,56,56,56,56,42,42,42,42,28,28,14,12,12,12,12,12,12,12,12,12,12,12,12,12,24,24,24,24,186,186,186,206,206,206,206,186,198,212,212,212,188,188,188,210,210,210,210,186,198,198,198,198,188,188,188,194,194,194,194,204,204,204,118,118,118,160,160,160,142,156,156,156,156,142,142,142,138,122,122,122,122,138,138,138,126,126,126,152,152,152,152,152,146,146,140,140,132,132,124,124,168,170,170,176,176,176,176,190,190,196,196,218,218,218,218,216,216,184,184,180,180,178,178,178,178,176,176,170,184,184,184,188,188,188,188,192,192,192,192,196,196,196,196,190,190,190,190,184,184,184,200,200,200,204,204,204,204,198,202,202,202,206,206,206,206,202,186,186,186,182,212,208,208,208,208,212,212,212,212,208,208,208,208,212,212,212,36,36,2,2,2,2,2,2,2,2,2,2,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100,102,104,106,108,110,112,114,116,118,120,122,124,126,128,130,132,134,136,138,140,142,144,146,148,150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180,182,184,186,188,190,192,194,196,198,200,202,204,206,208,210,212,214,216,218,1,2,40,156,40,18,28,62,62,62,62,164,168,218,168,2,68,68,68,84,84,84,84,66,66,66,66,82,82,108,108,108,108,136,136,136,136,180,180,198,198,188,188,176,176,112,112,112,112,102,102,102,74,78,78,80,80,78,78,78,78,80,78,78,78,78,78,80,80,86,86,96,108,140,140,204,204,162,162,160,160,138,138,136,130,128,128,102,102,100,102,104,104,114,114,114,114,140,140,142,140,136,136,126,126,126,126,154,154,154,154,140,140,140,6,6,6,10,10,16,16,18,18,18,18,24,24,24,24,24,24,40,40,50,50,28,16,16,16,20,20,26,26,26,26,34,34,46,46,48,48,58,58,50,50,48,48,48,48,50,50,54,54,58,58,58,58,60,60,66,98,102,116,120,136,140,156,160,178,218,178,176,174,170,170,166,164,158,156,150,148,146,140,138,134,110,110,62,62,42,42,28,28,2,40,42,118,122,122,122,122,122,122,114,114,52,52,22,22,22,22,46,46,46,60,88,88,92,92,92,92,52,42,42,42,58,58,84,84,96,96,96,96,170,198,202,202,202,202,188,188,188,188,138,138,138,138,168,168,168,168,196,196,196,196,142,142,142,142,88,60,48,48,48,48,56,56,64,64,66,66,66,48,42,42,42,42,108,108,108,108,86,86,86,86,50,50,86,100,152,152,152,152,106,128,142,142,142,142,134,108,100,100,100,100,86,86,84,84,84,84,82,66,66,66,56,56,56,56,56,56,44,44,44,44,46,46,50,50,52,52,52,52,50,50,50,50,46,46,46,46,46,1,2,2,2,2,18,18,18,18,80,80,80,74,18,12,2,2,2,2,6,16,28,28,30,30,60,60,94,94,94,94,68,68,52,60,76,76,104,104,104,104,86,86,86,92,146,146,146,146,132,132,50,50,50,50,30,30,30,30,16,16,2,16,24,24,36,36,36,36,50,50,50,50,74,74,92,92,98,98,98,98,116,116,116,116,108,108,108,108,122,122,122,122,114,114,114,114,122,122,136,136,146,146,146,146,192,192,218,218,218,218,196,196,180,180,168,168,168,168,138,138,138,138,164,164,176,176,176,176,156,156,88,88,66,66,66,66,92,98,118,118,118,118,94,94,94,94,90,90,88,88,88,88,88,88,134,134,150,150,174,174,176,176,178,178,178,178,208,208,208,208,186,186,174,186,194,194,194,194,212,212,214,214,214,214,210,210,182,182,182,182,202,202,202,202,200,200,198,198,198,198,182,182,182,182,168,168,168,168,152,152,138,138,94,94,90,90,76,76,60,60,58,58,58,58,34,24,24,24,56,56,190,190,190,190,154,154,134,134,134,134,122,122,122,122,26,26,26,26,58,58,58,58,32,32,10,10,10,10,2,2,2,2,10,10,10,10,20,20,80,80,82,82,82,82,70,70,70,70,92,94,112,112,140,140,182,182,190,190,190,190,196,196,208,208,216,216,216,216,210,24,26,26,48,48,48,48,66,66,70,70,88,88,88,88,106,106,116,116,126,126,126,126,92,92,92,92,92,92,100,100,102,98,94,22,22,22,32,32,38,38,44,44,44,44,44,44,50,50,54,54,64,64,64,76,82,82,82,90,86,84,78,84,86,90,92,80,80,62,62,62,82,98,98,98,136,136,144,144,146,146,146,146,100,32,32,32,36,36,42,42,52,52,52,52,42,42,38,38,26,26,26,26,36,36,32,38,38,38,34,34,32,32,30,32,30,30,32,34,38,38,40,38,36,36,34,34,34,34,40,40,40,40,38,86,90,90,90,90,84,84,80,80,74,74,74,74,86,86,88,88,90,90,90,90,82,82,78,78,78,78,76,76,76,76,80,76,72,82,82,34,34,34,42,42,48,48,66,66,66,66,54,54,44,44,34,26,26,26,40,40,98,98,114,114,114,150,150,150,150,150,160,160,160,160,148,148,144,166,166,178,178,178,180,180,188,188,188,180,184,184,192,200,200,200,204,204,208,208,208,194,210,166,176,166,160,170,158,158,172,172,186,178,178,178,180,180,188,188,198,198,198,198,198,164,162,148,144,138,136,132,130,128,118,112,108,108,106,106,106,106,116,116,146,146,146,146,150,150,156,156,164,164,172,172,180,180,184,184,198,198,198,198,186,186,182,182,172,172,170,170,160,160,156,156,146,146,144,144,134,134,132,130,122,122,120,120,108,108,108,108,118,118,130,130,130,152,152,152,156,156,160,160,172,172,172,172,166,166,162,162,152,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,148,146,140,138,128,126,120,118,98,92,88,86,80,68,66,64,62,60,50,46,44,42,38,34,30,28,26,24,22,18,10,2,2,2,2,2,2,16,16,26,40,40,44,44,44,44,22,22,22,22,24,24,30,30,30,30,16,16,16,16,26,26,28,36,34,36,46,46,46,46,38,38,36,36,36,36,42,42,42,42,32,32,32,32,40,40,40,40,36,28,14,14,10,10,10,10,12,12,22,22,32,32,36,36,50,50,60,60,60,60,52,52,48,48,48,48,60,60,60,60,64,64,64,64,64,86,90,90,90,90,98,98,100,108,112,112,114,114,114,114,110,110,106,110,112,114,112,106,104,104,104,104,112,112,114,120,126,126,130,130,130,130,138,138,142,142,150,150,150,150,142,142,142,142,134,134,122,122,118,118,118,118,142,142,142,142,142,142,158,158,158,158,150,150,148,148,148,148,164,164,164,164,156,156,156,156,166,166,166,166,160,160,160,160,164,160,140,140,140,140,134,134,118,118,118,110,106,106,86,86,82,82,70,70,70,86,90,76,64,70,70,70,82,82,82,82,94,94,94,94,98,98,104,104,118,118,118,118,108,148,160,160,160,160,162,162,168,168,174,174,174,174,170,170,170,170,176,176,176,176,168,172,172,180,180,180,170,170,176,176,176,176,164,164,164,164,162,174,180,180,180,180,186,186,186,186,188,188,188,188,180,180,186,186,186,186,182,182,182,178,164,164,158,158,158,158,152,152,146,146,138,138,130,130,130,130,132,132,114,114,114,114,110,110,98,98,94,94,94,94,92,92,84,84,82,82,78,78,78,92,98,98,104,110,110,110,116,116,124,124,144,144,146,146,152,152,152,152,142,142,142,164,168,168,168,168,146,146,132,132,120,120,116,116,102,102,92,92,84,84,64,64,60,60,60,60,48,48,48,48,56,56,56,56,44,44,44,44,50,50,50,50,48,36,36,24,24,24,38,38,38,38,40,40,62,62,64,64,88,88,98,112,116,116,128,128,130,130,140,140,146,146,178,178,178,178,170,170,170,170,178,2,50,50,64,64,66,66,76,76,80,80,88,88,92,92,102,102,216,218,216,196,196,196,148,148,2,2,188,190,180,180,178,178,178,178,182,182,184,184,192,192,192,154,144,144,144,144,148,148,152,152,158,158,158,158,156,144,142,114,102,102,100,100,100,100,104,108,114,114,114,114,112,66,62,62,62,62,66,66,74,74,74,74,68,68,66,66,54,24,30,30,36,36,36,36,28,28,26,26,20,20,20,20,26,4,66,68,84,84,86,86,120,120,130,132,144,154,180,190,198,152,152,152,160,120,120,64,64,64,74,74,74,40,48,48,58,58,60,60,76,76,86,86,86,86,80,80,70,70,58,58,48,48,46,46,46,46,44,44,38,44,48,48,52,52,52,42,42,206,204,70,70,70,74,74,74,74,68,68,66,66,62,62,72,72,76,76,86,86,86,86,66,66,64,64,60,60,60,60,70,70,74,74,88,88,88,88,70,70,64,64,60,60,60,60,72,72,74,74,96,130,130,132,142,178,178,178,178,168,168,148,148,148,150,150,166,168,174,174,174,174,160,160,158,158,150,150,150,150,148,148,146,146,140,140,148,148,150,150,150,150,146,146,140,140,136,136,136,136,146,146,148,148,154,154,154,154,154,154,158,158,162,162,170,170,170,170,162,162,160,160,152,152,152,152,146,146,144,144,136,136,136,136,140,66,64,22,22,22,24,24,28,28,36,36,36,36,28,28,20,20,20,20,26,32,42,32,34,34,44,22,22,6,20,8,12,12,22,14,14,14,14,14,20,20,24,30,36,36,36,36,34,34,30,30,28,28,28,44,44,44,50,74,94,110,126,86,84,84,84,84,88,88,90,90,98,98,100,100,108,108,110,110,116,116,118,118,126,126,128,128,136,104,104,104,112,112,116,116,112,112,110,76,76,76,84,84,82,62,62,56,64,56,48,48,36,46,50,50,56,56,56,56,76,72,68,68,68,68,80,80,86,86,94,94,96,96,104,104,136,136,136,136,146,146,146,146,138,138,138,138,148,148,148,148,134,134,124,124,114,114,98,98,86,86,46,46,42,42,42,42,44,44,46,46,46,46,36,36,32,32,32,32,40,40,40,40,60,60,60,60,70,70,72,100,96,96,96,96,102,102,104,104,104,122,124,124,124,124,120,120,116,116,116,116,118,118,120,120,124,124,124,124,128,132,132,142,142,142,142,142,142,152,162,172,172,172,172,172,172,172,172,172,174,174,178,178,184,184,184,184,178,178,170,170,170,184,184,184,184,192,192,192,192,192,194,194,196,204,196,196,196,206,210,210,210,210,204,204,202,202,200,200,200,200,204,204,206,170,168,168,168,176,184,184,184,194,202,202,198,198,198,198,202,202,202,202,196,50,58,50,36,36,28,28,28,28,36,36,44,44,56,38,48,36,42,164,164,164,174,192,192,192,190,164,164,164,180,180,180,180,180,178,178,178,176,176,174,192,196,196,196,196,190,190,186,186,186,186,190,190,200,144,144,62,68,78,84,66,66,72,66,64,66,72,76,76,76,20,20,20,38,40,40,40,40,50,52,52,84,84,84,84,110,100,58,106,106,106,128,142,142,142,142,142,146,146,164,174,186,186,190,190,190,190,190,190,162,160,160,164,176,34,16,16,16,16,12,12,12,12,28,38,38,50,50,50,36,50,50,50,50,58,58,58,48,48,48,48,54,54,66,48,66,72,72,72,78,78,82,82,82,82,86,86,104,104,104,104,88,88,88,100,100,108,108,108,112,112,116,116,116,116,116,116,140,140,140,148,148,148,126,154,154,146,142,140,142,162,162,168,174,186,188,184,178,178,178,178,162,162,192,194,194,194,178,178,178,178,156,156,142,142,142,142,160,160,162,162,190,182,178,178,162,162,158,158,152,152,152,158,158,158,154,154,152,152,152,152,154,154,164,164,172,172,174,174,174,174,170,170,166,166,166,178,178,178,190,184,198,190,190,190,192,192,214,158,150,150,110,110,110,110,116,116,122,122,130,130,130,130,138,138,142,142,150,150,152,152,162,162,166,166,170,170,170,170,178,178,182,182,190,190,190,190,190,190,196,196,200,200,204,204,204,204,160,152,152,152,152,152,124,122,122,122,112,124,118,118,108,120,120,120,110,110,110,120,120,150,160,162,180,180,188,188,188,178,178,178,190,182,182,182,190,190,196,176,182,182,190,184,184,184,190,190,194,146,128,128,120,120,120,120,120,120,126,126,130,130,130,130,128,128,122,122,120,162,164,164,164,164,174,174,170,170,170,170,176,176,180,180,180,180,178,178,170,28,16,16,14,14,14,14,16,16,22,22,22,22,28,36,32,32,32,32,42,30,40,40,42,42,42,54,54,54,48,48,44,44,36,36,36,36,36,36,48,48,50,50,50,50,54,54,58,58,58,58,70,70,70,70,60,60,52,52,50,50,48,48,48,48,56,56,62,62,62,78,80,78,68,68,54,54,54,54,56,56,62,62,72,68,60,56,52,56,74,66,68,68,68,68,46,62,62,62,82,84,82,62,60,60,60,60,72,12,12,6,18,18,18,18,18,24,24,50,50,50,36,50,54,54,58,52,52,52,64,56,38,66,66,66,68,68,84,84,84,94,94,94,108,108,108,132,132,132,138,138,138,138,130,146,154,154,154,154,148,148,148,152,156,140,140,126,126,126,154,154,154,22,22,22,26,26,32,32,36,36,62,62,64,64,70,70,74,74,78,78,78,68,68,56,56,48,48,40,40,34,34,28,28,80,218,22,2,106,106,106,98,98,98,98,102,102,116,116,116,116,122,120,120,120,108,108,108,114,112,110,108,104,102,122,120,120,118,118,118,118,122,122,122,130,130,130,134,134,134,134,122,152,152,152,166,166,166,166,176,152,186,186,186,186,186,186,174,176,178,174,174,154,150,152,152,196,196,196,192,196,192,192,192,192,196,196,196,196,202,202,202,202,206,206,206,206,210,208,206,206,206,206,210,210,210,210,202,202,202,156,150,150,136,158,154,158,160,160,164,164,164,164,166,164,164,164,162,162,140,140,138,138,138,138,134,198,194,194,188,188,188,188,198,198,198,198,212,212,212,212,208,208,202,202,196,196,196,196,194,176,170,170,166,166,166,166,168,168,178,178,180,180,180,180,184,184,188,184,180,180,176,166,166,176,176,176,164,164,164,164,168,164,164,164,176,176,164,168,176,176,164,168,176,176,164,176,176,176,180,180,188,188,190,190,190,190,194,172,168,168,164,164,164,164,162,162,152,152,152,152,148,6,98,44,45,44,126,126,80,80,76,76,52,52,52,52,38,38,38,38,46,124,124,124,130,130,130,122,134,134,148,148,148,148,144,144,130,130,118,118,98,98,98,98,120,122,114,122,122,122,126,126,142,142,148,148,148,148,136,136,136,38,38,38,42,38,28,28,28,28,42,42,44,38,38,38,22,22,8,8,8,8,12,12,20,20,28,28,38,38,42,42,42,42,46,46,52,52,52,52,44,44,34,32,26,26,26,26,34,34,34,38,40,76,76,76,82,74,74,74,70,208,180,180,180,174,140,176,166,166,166,166,156,156,156,156,154,154,146,146,142,176,176,176,146,176,166,166,164,164,164,164,160,160,150,150,146,146,140,198,198,184,184,182,182,182,178,178,178,178,174,174,174,174,170,170,170,170,166,166,166,166,152,152,150,186,190,196,202,186,186,186,204,204,204,44,45,2,2,2,2,2,2,2,2,2,2,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100,102,104,106,108,110,112,114,116,118,120,122,124,126,128,130,132,134,136,138,140,142,144,146,148,150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180,182,184,186,188,190,192,194,196,198,200,202,204,206,208,210,212,214,216,218,44,45,2,2,2,2,2,2,2,2,2,2,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100,102,104,106,108,110,112,114,116,118,120,122,124,126,128,130,132,134,136,138,140,142,144,146,148,150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180,182,184,186,188,190,192,194,196,198,200,202,204,206,208,210,212,214,216,218,2,2,2,196,196,206,206,206,206,182,182,182,182,122,122,88,88,82,82,82,82,32,32,2,138,138,138,132,132,92,92,82,82,4,4,2,2,2,2,18,18,18,18,22,22,42,42,66,66,84,84,148,148,182,182,188,188,188,188,198,198,198,198,182,182,182,182,170,170,166,166,156,156,140,140,138,138,132,132,130,130,130,78,78,78,72,72,58,58,56,56,56,56,42,42,42,42,30,30,30,30,12,12,12,100,100,100,110,110,112,112,156,156,206,206,206,206,198,198,186,186,178,178,174,174,166,166,162,162,152,152,134,134,124,124,88,2,26,26,26,26,38,38,44,44,44,44,36,36,24,24,2,2,2,60,146,146,146,146,148,166,168,156,152,152,140,152,152,152,140,140,126,126,112,112,70,56,68,68,86,86,86,96,96,96,78,78,54,118,118,118,120,122,122,122,128,128,136,150,148,146,142,142,142,152,152,152,152,152,152,56,60,64,70,60,68,56,56,56,62,62,62,70,70,62,62,72,74,74,78,58,66,56,64,64,72,80,88,78,78,74,74,60,62,2,2,2,2,2,2,2,2,2,2,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100,102,104,106,108,110,112,114,116,118,120,122,124,126,128,130,132,134,136,138,140,142,144,146,148,150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180,182,184,186,188,190,192,194,196,198,200,202,204,206,208,210,212,214,216,218,108,136,136,136,136,104,100,72,72,72,72,98,98,98,98,80,80,80,80,94,94,94,94,100,100,100,100,122,122,122,122,100,100,100,102,100,100,94,94,94,94,100,100,102,102,102,102,94,94,94,94,86,86,86,86,100,100,100,100,100,100,108,108,108,108,92,92,92,118,118,126,126,126,126,114,114,106,106,106,100,86,86,86,82,82,82,68,68,76,76,90,90,90,90,104,104,110,110,122,122,132,132,116,116,94,94,80,80,80,80,72,72,64,64,70,70,76,76,98,98,128,128,136,136,128,128,134,134,62,62,62,60,60,60,136,136,144,144,152,152,140,140,132,132,114,114,50,50,58,58,66,66,100,100,100,114,114,114,114,104,104,104,104,98,98,98,98,94,94,86,86,82,82,82,82,92,18,30,30,30,30,62,62,134,142,144,144,144,144,166,166,166,160,124,124,124,124,98,98,98,98,150,150,150,150,128,128,128,122,122,122,84,84,84,84,4,4,70,70,70,70,108,108,108,108,202,204,152,152,152,152,174,174,174,174,204,204,204,204,172,170,164,164,150,150,146,146,130,130,124,124,104,104,102,102,80,80,78,78,48,56,66,66,78,78,80,80,98,98,100,100,120,120,142,142,144,144,166,166,204,202,166,166,180,180,68,68,62,62,62,62,66,66,70,70,76,76,76,76,74,76,204,204,206,206,206,206,204,204,196,196,192,192,186,186,186,186,188,188,204,204,204,204,172,172,172,172,154,154,114,114,112,112,112,112,112,112,110,110,94,94,90,90,74,74,68,68,6,6,6,6,10,10,42,42,86,86,86,86,12,12,12,12,34,34,34,34,46,46,60,60,76,76,76,76,66,66,52,52,52,52,50,50,10,10,8,8,8,8,2,54,54,64,64,64,92,92,100,100,104,104,104,104,98,98,80,80,66,66,56,54,52,52,62,62,62,62,112,112,112,112,102,126,148,148,160,160,160,160,110,122,122,122,102,120,150,150,150,168,168,168,162,162,160,164,164,164,152,152,152,152,146,146,146,146,150,150,166,166,170,170,170,160,174,174,174,174,172,172,166,184,184,184,180,180,166,166,128,128,122,122,116,116,116,116,116,116,116,136,136,136,136,136,134,134,120,166,186,186,188,188,188,188,188,188,188,188,172,172,170,170,162,162,162,162,170,170,170,192,196,196,204,204,204,204,194,194,184,166,166,166,162,162,148,148,144,144,144,144,136,136,144,64,20,20,52,40,40,40,94,94,98,98,98,98,80,80,80,80,84,84,94,94,94,94,34,34,34,34,10,10,10,10,4,4,4,4,28,28,30,98,112,112,128,128,158,160,160,160,156,156,148,142,126,126,126,118,84,84,84,84,86,86,136,136,216,216,216,158,158,82,90,90,90,90,80,80,80,80,90,90,90,90,82,82,82,82,86,86,126,126,164,164,164,164,164,164,166,166,176,176,178,178,178,178,116,116,112,112,72,72,72,72,62,62,56,56,50,50,50,26,16,16,16,16,22,22,22,22,10,10,10,10,34,34,38,38,60,130,134,134,136,136,158,158,170,170,182,182,186,186,200,200,200,200,192,192,192,192,204,204,196,196,202,202,200,200,200,200,198,198,198,198,192,192,204,204,192,192,202,202,202,202,192,192,190,190,202,202,202,202,190,190,200,200,200,200,184,184,178,178,170,170,164,164,150,150,136,136,104,104,102,158,168,168,170,170,170,170,182,182,186,186,188,188,192,192,192,192,170,170,206,206,206,206,200,200,172,158,60,60,56,56,42,42,42,42,28,28,28,28,34,34,34,46,64,64,72,72,82,82,86,86,96,98,130,130,132,132,132,132,50,50,50,50,82,82,82,82,74,74,72,72,44,44,40,40,16,16,16,16,34,34,36,36,56,56,56,56,102,104,110,110,120,120,120,120,142,142,142,142,184,194,204,206,206,206,174,174,170,170,138,138,138,154,156,156,158,158,158,158,170,170,170,170,158,158,174,174,174,174,160,160,160,160,172,172,172,172,156,156,156,156,170,170,170,170,154,154,150,150,132,132,62,62,60,60,60,60,50,50,50,50,76,76,76,76,94,94,94,94,72,82,82,82,180,192,208,208,212,212,216,216,216,216,206,206,206,206,218,218,218,218,218,212,172,212,218,172,130,94,72,64,2,2,2,2,28,30,66,80,86,142,148,156,170,174,218,198,194,194,194,190,136,136,136,136,88,110,124,124,124,124,144,144,146,146,162,164,166,166,190,190,190,190,190,190,188,188,146,146,146,146,168,168,146,146,146,146,162,158,36,30,30,30,42,42,58,58,62,98,112,112,170,170,178,178,178,178,162,162,162,162,168,172,174,174,174,174,172,172,160,160,158,158,158,158,98,98,86,86,86,86,120,120,124,124,124,124,198,198,198,198,198,198,184,184,182,182,180,170,162,148,120,92,92,92,114,114,118,118,142,142,142,142,138,138,126,126,92,92,122,122,122,122,90,88,116,116,116,72,106,106,106,106,70,70,70,72,64,68,68,68,118,118,118,118,64,118,132,132,132,132,116,68,50,50,50,50,70,78,78,78,90,90,90,104,104,104,118,118,118,76,80,80,80,80,76,76,76,94,98,98,98,98,94,94,94,80,98,86,86,86,102,102,102,102,82,86,92,92,92,92,100,100,100,100,94,94,94,94,86,94,96,96,96,96,88,88,96,118,118,118,118,104,104,88,88,76,76,90,90,90,86,86,86,86,92,154,154,154,160,160,196,196,216,216,216,216,182,182,182,182,150,150,116,116,148,148,104,156,156,156,136,136,136,136,208,208,208,208,180,180,180,180,150,154,154,150,148,38,38,38,56,56,56,56,36,36,36,36,56,56,56,56,38,38,38,38,30,30,30,30,52,52,52,52,48,48,18,18,16,16,16,16,54,54,54,54,6,6,2,2,2,2,56,56,58,58,58,58,64,42,24,24,24,24,54,24,24,24,46,46,46,46,28,28,24,24,32,32,32,32,16,16,16,16,32,32,32,32,42,42,42,42,40,40,40,40,56,56,56,56,70,70,76,76,86,86,92,92,102,102,108,108,118,118,120,120,132,132,140,140,150,150,154,154,164,164,166,166,200,200,200,200,204,204,214,214,216,216,216,216,204,204,200,200,180,180,176,176,158,158,118,118,116,116,116,116,94,94,64,64,58,58,54,52,46,46,46,46,54,54,46,46,46,1,2,2,2,2,48,48,48,48,98,98,98,98,130,130,130,130,134,134,158,158,158,158,2,76,36,34,34,34,64,64,64,64,18,108,108,88,122,160,160,160,190,158,158,156,164,164,164,164,196,198,198,198,160,160,160,168,168,168,156,108,102,74,74,74,30,30,30,30,50,50,56,56,56,56,8,8,8,8,26,26,36,36,46,46,46,46,48,48,48,48,62,62,62,110,110,124,124,124,86,86,86,86,94,166,166,166,192,192,192,192,188,188,184,184,184,184,174,174,160,166,166,166,166,162,162,162,140,140,140,140,144,144,154,154,154,154,158,162,154,184,210,210,214,214,214,214,206,206,184,184,184,184,204,204,204,204,192,192,192,192,200,200,200,200,194,194,194,194,136,136,136,136,134,134,124,124,124,124,118,118,38,38,38,38,16,16,16,16,36,36,36,36,22,22,22,22,30,30,30,30,22,28,28,28,56,56,56,56,26,26,30,30,64,64,80,80,80,80,84,84,122,122,122,22,22,22,52,78,78,66,90,138,124,124,112,112,112,112,134,134,134,134,128,128,120,120,108,108,106,24,32,32,32,32,34,34,50,50,56,98,98,98,122,122,122,122,112,112,112,112,68,68,56,56,56,56,94,94,94,94,98,98,98,96,150,150,156,156,156,156,142,142,110,54,6,6,6,6,18,18,58,82,82,82,92,92,98,98,102,102,102,102,82,82,82,82,78,78,68,68,60,60,60,60,84,148,206,206,206,206,158,158,158,148,130,130,118,118,118,118,164,156,210,210,210,210,208,208,208,208,204,204,204,182,182,166,194,182,194,194,194,194,166,166,166,166,196,96,96,96,78,22,22,2,2,2,2,2,2,2,2,2,2,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100,102,104,106,108,110,112,114,116,118,120,122,124,126,128,130,132,134,136,138,140,142,144,146,148,150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180,182,184,186,188,190,192,194,196,198,200,202,204,206,208,210,212,214,216,218,2,12,12,12,12,22,22,22,22,28,28,28,38,38,38,30,30,30,30,18,18,18,18,2,2,28,28,28,28,42,42,42,42,52,52,52,194,194,194,202,202,202,202,208,208,208,208,218,218,198,198,198,198,190,190,190,190,182,182,182,170,170,170,178,178,178,178,188,188,188,188,218,190,190,190,198,198,198,198,206,206,206,206,218,218,194,194,194,194,182,182,182,182,176,176,176,162,162,162,172,172,172,172,178,178,178,178,218,32,32,32,22,22,22,22,10,10,10,10,2,2,16,16,16,16,28,28,28,28,38,38,38,52,52,52,36,36,36,36,22,22,22,22,2,84,146,146,146,146,100,100,100,100,62,62,60,62,96,96,60,96,148,146,96,96,96,96,52,52,50,50,42,42,70,54,140,140,140,140,88,88,84,84,28,28,54,218,206,206,192,192,182,182,158,158,152,152,124,124,74,74,24,24,24,24,2,2,2,2,14,14,18,18,18,18,22,22,34,34,78,78,104,104,160,160,168,168,196,196,216,218,180,180,180,180,182,182,218,140,70,70,62,62,62,62,76,76,138,138,140,140,140,2,42,42,46,46,46,46,44,44,2,76,84,84,90,90,90,90,70,70,66,66,66,66,76,76,96,96,100,100,120,120,120,120,118,118,98,98,98,98,122,122,126,126,146,146,146,146,142,142,126,126,126,126,154,2,4,2,2,2,2,2,2,2,2,2,2,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100,102,104,106,108,110,112,114,116,118,120,122,124,126,128,130,132,134,136,138,140,142,144,146,148,150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180,182,184,186,188,190,192,194,196,198,200,202,204,206,208,210,212,214,216,218,210,211,210,210,210,188,188,172,172,144,144,144,144,154,154,192,192,196,196,196,196,194,194,84,84,80,80,80,80,194,208,70,70,70,70,174,174,182,182,182,182,178,178,138,138,128,128,128,128,132,132,138,138,142,142,162,162,212,212,212,212,154,154,112,112,112,112,176,168,162,162,156,156,152,70,54,54,8,8,30,30,30,30,10,10,10,10,32,32,32,32,6,6,28,28,30,30,36,36,54,54,62,62,64,64,72,72,72,72,74,74,88,88,96,96,96,96,100,100,102,102,116,116,118,118,122,122,122,122,128,128,130,130,142,142,146,146,160,160,178,178,184,184,184,184,190,190,192,192,208,208,208,208,204,204,204,204,200,200,200,200,196,196,196,196,192,192,192,192,188,188,188,188,184,184,184,184,178,178,178,178,174,174,174,174,170,170,170,170,166,166,166,166,162,162,162,162,158,158,158,154,154,154,150,150,150,150,148,148,146,146,146,146,142,142,142,142,138,138,134,134,134,134,134,134,110,110,110,110,102,102,100,100,86,86,70,70,54,54,52,52,44,44,40,40,32,32,28,28,10,10,10,10,34,34,4,4,28,28,6,6,6,6,62,62,196,196,196,196,192,192,192,192,198,198,198,198,188,188,188,188,202,202,202,202,194,184,184,196,208,208,208,208,204,204,198,198,194,194,178,178,174,174,144,144,144,144,158,178,186,186,190,190,176,76,110,110,110,110,70,70,66,66,26,26,70,70,86,86,88,88,92,92,76,76,118,118,118,118,114,114,108,108,106,106,92,92,92,92,62,62,62,62,68,68,68,68,70,70,70,70,110,110,110,110,104,104,64,64,26,26,26,26,62,68,74,62,54,54,54,54,56,56,68,68,74,74,74,74,74,74,48,48,48,48,78,78,78,78,74,48,56,68,76,76,76,76,48,52,52,78,80,80,80,80,78,48,44,44,44,44,82,82,82,82,80,44,36,36,26,26,26,26,40,40,84,84,102,102,102,102,98,98,80,80,30,30,30,30,86,86,86,86,42,42,42,42,90,90,90,90,38,38,38,38,92,92,92,92,26,26,26,26,26,26,94,94,94,94,90,38,36,34,34,34,40,84,100,100,100,100,32,32,32,32,94,94,94,94,94,94,86,86,80,80,80,80,94,94,100,100,106,106,106,106,98,98,88,88,84,84,84,84,90,90,94,94,96,96,96,96,90,90,82,82,82,82,88,88,96,96,98,98,98,98,96,96,90,90,84,92,94,94,94,90,88,88,84,88,98,84,88,88,88,88,82,82,82,82,84,88,90,90,90,86,86,86,88,114,108,108,108,108,114,114,120,120,130,130,130,130,126,126,118,118,110,110,110,110,114,114,120,120,120,120,116,116,110,120,124,124,124,124,116,116,114,114,114,114,120,120,124,124,124,124,120,120,110,110,108,108,108,108,112,112,114,116,122,122,122,122,120,120,114,110,108,108,108,108,110,112,118,118,122,122,122,122,118,118,114,114,112,112,112,112,116,126,126,126,200,200,200,200,194,194,126,124,124,124,124,124,124,124,186,186,186,186,132,132,132,186,192,198,198,198,186,186,194,194,194,194,126,126,126,126,162,162,162,162,126,126,152,152,152,152,128,128,128,128,148,148,148,148,130,130,130,130,142,142,142,142,130,200,188,188,178,178,178,178,188,188,194,194,194,194,188,188,182,182,182,182,188,188,198,198,204,204,204,204,194,194,188,188,186,186,192,192,198,198,200,200,194,182,182,186,192,194,202,202,202,202,198,198,192,192,186,186,188,196,198,198,198,198,192,192,188,188,186,186,190,190,196,196,200,200,196,192,188,186,176,176,176,176,182,182,188,190,194,194,194,192,188,188,182,182,180,108,108,108,112,112,118,118,118,118,112,112,106,106,102,102,102,102,106,106,108,110,114,114,114,114,110,104,104,104,108,108,112,112,114,102,100,100,104,104,106,114,120,120,120,120,116,112,108,108,100,100,100,100,102,104,114,118,118,118,114,114,112,110,108,108,108,108,110,112,112,112,110,110,106,106,102,174,174,174,170,170,168,168,166,166,166,166,170,170,176,176,176,176,166,166,162,162,166,166,170,170,174,174,174,174,170,170,164,164,160,160,160,160,162,162,166,166,170,170,170,170,166,166,162,162,156,156,156,156,158,158,162,162,172,172,172,172,170,170,164,164,160,160,160,160,164,166,168,168,170,170,170,170,164,164,156,156,154,154,154,154,156,170,172,172,172,172,168,164,162,162,160,160,166,166,166,166,160,186,184,184,180,180,170,170,170,174,178,178,190,190,196,196,196,196,188,188,182,182,180,180,180,180,184,184,190,190,190,190,184,184,178,178,172,172,172,172,172,172,174,176,178,180,190,190,190,190,186,182,178,178,174,174,174,174,176,176,182,182,182,172,170,174,176,182,188,188,188,188,186,186,184,182,178,178,178,178,180,182,188,188,194,194,190,190,188,180,176,176,176,176,180,184,186,186,178,178,174,174,170,124,120,120,44,40,128,128,124,124,30,124,124,130,130,124,128,128,128,128,136,132,126,126,126,132,132,202,200,200,132,132,136,200,196,194,192,192,130,130,196,136,136,136,204,34,218,108,98,98,92,90,58,58,42,42,40,40,24,24,24,24,36,38,38,38,38,38,38,38,30,30,30,30,38,38,38,38,38,38,218,218,218,218,32,32,32,36,36,32,2,2,2,2,20,22,22,22,28,28,30,30,32,32,34,34,34,34,42,42,42,42,56,56,56,80,82,82,84,84,88,88,86,86,84,84,84,84,86,86,86,84,86,86,88,88,92,92,92,92,88,88,86,86,82,82,86,86,90,92,94,94,94,94,88,88,84,84,82,82,82,86,88,88,92,92,96,96,96,96,92,92,86,86,84,76,98,98,104,104,104,104,90,90,88,88,80,80,80,102,102,102,130,130,160,160,168,168,168,168,144,144,142,142,132,132,132,132,138,138,138,138,128,128,128,138,148,148,148,138,130,130,118,118,118,118,126,126,136,136,168,168,168,168,166,166,162,162,160,160,160,170,182,182,186,186,186,186,184,184,182,182,182,182,178,176,168,180,174,174,158,158,174,174,176,176,184,184,184,184,180,180,164,180,218,164,98,98,96,96,96,96,92,92,88,88,88,108,108,108,108,108,108,108,108,108,108,108,108,176,176,176,176,176,176,176,176,176,176,144,142,142,140,138,132,130,128,126,130,144,144,144,144,144,144,166,166,164,156,154,152,152,152,152,152,154,158,166,174,174,176,176,178,174,168,168,166,166,172,172,176,176,178,178,168,174,174,174,166,166,160,160,152,152,150,150,130,130,90,90,78,78,78,78,62,62,152,152,162,162,166,166,174,174,174,174,166,166,166,166,162,162,156,156,152,184,184,184,192,192,196,196,196,196,192,192,186,186,186,176,184,184,184,184,174,174,174,174,210,210,210,210,208,208,192,146,146,146,156,156,156,156,164,170,170,168,162,178,178,178,184,194,194,194,202,208,208,218,208,208,192,192,194,194,202,202,178,178,188,188,190,190,218,218,216,214,212,204,202,200,192,190,188,112,110,112,132,132,110,110,136,80,80,72,48,48,48,48,80,80,174,42,42,42,58,58,58,58,46,44,42,46,46,46,52,52,52,54,54,38,38,38,42,38,34,34,30,30,40,48,48,56,56,56,46,46,46,46,52,64,64,64,60,60,60,60,62,62,76,76,80,80,74,74,66,68,68,68,66,66,66,66,64,64,64,64,62,62,62,62,60,60,62,62,60,60,60,60,58,58,58,58,56,56,56,112,136,20,60,64,218,218,218,218,2,2,2,2,20,60,64,106,78,78,78,78,82,82,92,94,94,94,102,102,102,82,94,78,78,78,100,100,100,78,66,66,66,66,64,64,68,68,76,100,108,108,108,108,106,106,100,66,66,66,52,52,52,106,106,106,70,70,70,70,94,94,94,94,84,84,84,84,94,94,94,100,100,100,92,78,76,76,70,70,68,122,122,122,146,146,166,166,170,170,174,174,194,194,180,180,166,166,126,126,126,126,122,122,122,122,196,190,190,190,190,192,180,180,180,182,186,144,158,158,158,158,144,144,144,150,150,144,160,160,142,142,142,142,156,156,156,150,150,150,158,150,142,204,204,182,182,182,184,184,188,188,188,186,186,186,194,194,196,196,196,196,184,184,186,186,188,188,184,184,186,186,184,184,186,186,184,184,186,186,186,186,188,188,188,200,200,200,218,186,178,180,190,144,142,130,128,126,124,116,114,108,106,114,112,80,78,98,80,98,92,92,92,96,100,86,78,78,88,88,88,94,88,202,208,208,208,216,216,206,206,206,216,216,214,214,212,212,212,212,210,210,208,208,208,208,206,206,206,206,204,204,198,198,196,196,196,196,194,194,194,194,192,192,192,192,194,194,192,192,194,194,194,194,196,196,196,196,198,198,196,196,196,56,52,52,50,50,52,52,56,56,56,56,58,58,60,60,60,60,62,62,64,64,64,64,68,68,70,70,68,68,68,68,66,66,68,68,68,68,66,66,62,62,60,60,56,56,56,56,54,54,54,80,80,80,82,82,84,84,88,88,88,88,92,92,92,92,88,88,84,84,84,84,80,80,82,82,82,80,78,108,108,108,110,110,112,112,116,116,118,118,122,122,124,124,128,128,130,130,128,128,124,124,122,122,122,122,126,124,122,122,108,108,108,108,110,110,110,20,20,20,20,20,26,26,42,42,42,42,28,28,26,28,42,42,42,42,28,34,34,34,38,38,42,42,42,42,40,40,22,144,114,114,114,114,138,138,138,128,128,154,112,150,150,20,22,2,2,2,2,2,2,2,2,2,2,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100,102,104,106,108,110,112,114,116,118,120,122,124,126,128,130,132,134,136,138,140,142,144,146,148,150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180,182,184,186,188,190,192,194,196,198,200,202,204,206,208,210,212,214,216,218,68,84,84,84,84,68,68,68,90,90,90,104,104,104,104,90,90,90,86,86,86,94,76,76,76,102,102,102,68,68,68,72,72,74,74,78,78,80,80,88,88,90,90,96,96,98,98,104,104,106,106,110,110,112,112,112,112,122,122,132,132,146,146,146,146,142,142,138,138,136,136,136,136,134,134,126,126,124,124,124,124,120,120,116,116,112,112,102,102,100,100,94,94,86,86,84,84,78,78,76,76,76,76,74,74,68,68,64,64,64,64,70,68,68,68,68,68,80,80,96,96,116,96,96,96,46,46,42,42,42,32,48,48,48,48,20,20,20,20,30,30,30,30,42,42,62,62,68,68,68,128,128,40,40,40,42,42,46,46,52,52,52,52,42,42,42,42,40,40,38,38,36,36,36,36,52,52,52,52,44,44,42,42,38,38,38,38,36,36,32,32,32,32,28,28,24,24,24,24,22,22,20,20,16,16,16,16,14,14,8,8,8,8,4,4,2,2,2,2,6,6,22,22,30,30,30,160,160,160,170,170,170,178,178,178,148,148,148,148,132,132,136,136,152,152,192,192,192,192,176,176,176,1,4,4,42,42,42,42,56,56,144,144,170,170,170,170,178,178,178,178,158,158,54,54,34,32,32,32,56,82,64,60,86,88,88,88,62,62,62,62,74,74,74,74,58,74,88,110,110,110,114,138,138,130,128,126,108,108,108,108,134,118,118,118,118,118,138,118,104,94,94,94,126,126,126,126,126,126,112,112,112,112,130,130,130,122,122,122,138,138,100,100,68,66,66,66,70,70,90,104,112,128,132,132,156,156,156,156,146,152,160,14,18,18,56,58,96,78,32,124,124,124,110,124,138,14,36,36,38,38,38,38,32,32,30,30,30,30,34,34,52,52,58,58,58,58,56,56,52,52,44,44,44,44,56,56,58,58,68,68,68,68,68,68,74,74,78,78,84,84,84,84,94,94,94,94,94,94,100,100,102,102,112,112,112,112,114,114,122,122,122,122,114,114,122,122,126,126,126,126,128,128,142,142,142,142,142,142,140,124,112,178,178,178,166,178,188,44,30,30,18,18,18,18,12,12,10,10,18,18,20,20,20,20,34,34,42,34,34,34,36,36,38,38,46,46,46,46,42,42,38,38,38,44,44,44,46,46,48,48,56,56,56,56,56,56,60,60,64,64,70,70,70,70,86,86,88,88,88,88,90,90,94,94,94,94,98,98,94,94,92,86,84,84,84,84,90,90,94,94,98,98,98,98,96,96,90,100,100,100,102,102,108,108,112,120,140,122,142,180,180,180,176,176,158,158,154,154,154,154,168,168,172,180,180,180,190,190,194,194,194,194,202,202,202,202,192,192,168,94,92,92,74,94,98,98,102,102,102,102,98,98,98,98,104,82,98,104,108,108,112,112,112,112,116,116,116,116,112,114,114,116,126,126,126,126,136,136,136,136,146,146,146,136,136,136,146,146,146,146,138,146,154,154,164,164,176,164,160,164,166,160,160,160,162,168,168,160,170,174,174,148,182,182,184,184,184,184,172,172,178,178,178,178,174,174,186,186,190,76,62,62,62,62,54,54,54,54,58,58,62,62,66,66,66,66,88,122,122,122,138,138,140,140,154,154,144,144,132,132,128,128,132,132,136,136,146,146,148,148,148,148,144,144,134,122,120,120,118,48,64,60,60,60,54,54,38,48,48,58,58,58,52,52,48,48,38,38,38,38,42,76,76,88,80,80,72,78,78,78,86,86,88,88,88,78,82,82,90,18,18,34,34,16,38,116,116,136,136,110,142,110,110,110,124,124,128,128,132,132,132,132,144,100,110,110,120,120,120,120,108,108,94,94,82,82,82,82,98,98,114,102,112,112,114,114,114,114,108,108,100,100,94,82,76,80,80,80,82,82,92,92,96,96,96,96,94,94,76,76,74,74,74,74,80,80,98,110,110,110,102,102,98,98,86,86,86,86,98,98,112,112,112,112,126,126,128,128,128,128,126,126,108,106,106,106,110,110,128,128,130,130,130,130,128,128,114,128,138,138,138,138,136,136,92,82,66,66,66,66,76,76,80,80,96,96,96,80,80,80,92,92,108,108,108,108,98,80,74,74,72,72,72,72,84,84,118,118,118,118,108,126,126,126,142,142,142,142,110,100,50,48,48,48,50,50,62,62,62,62,50,50,50,50,68,68,80,80,82,82,82,82,56,56,56,56,70,70,74,74,74,74,76,76,116,116,116,116,118,118,138,138,140,140,140,140,136,136,110,110,108,108,108,108,96,96,76,76,68,60,60,60,70,70,70,70,56,56,64,64,64,64,54,54,54,54,76,94,150,150,150,150,146,146,134,134,132,132,132,132,126,126,108,108,108,108,110,110,134,134,134,134,152,152,152,152,150,150,136,136,136,54,54,54,40,40,38,38,38,38,40,40,50,50,52,52,52,52,54,54,66,66,66,66,108,126,158,158,158,158,138,138,138,138,158,158,158,158,154,154,146,140,134,134,158,158,158,158,158,158,158,158,156,156,68,64,58,54,44,44,44,44,26,26,14,14,14,14,56,96,96,96,116,94,74,116,116,116,148,148,158,158,160,160,148,148,146,146,144,144,114,114,112,112,112,74,74,74,28,28,24,18,18,18,28,20,20,20,28,28,72,76,76,72,78,74,26,112,154,126,126,126,130,136,136,136,142,126,126,126,132,138,138,138,146,50,42,42,34,62,62,62,56,36,36,36,30,60,60,60,66,2,218,206,206,198,198,190,190,180,180,172,172,162,162,154,154,154,142,142,142,136,136,124,124,112,102,102,102,102,108,80,80,66,66,66,78,78,78,16,16,16,34,44,44,44,56,56,56,40,40,66,66,66,74,74,74,164,164,164,218,204,204,204,192,196,180,190,188,188,176,182,168,178,174,174,162,212,212,212,204,94,92,94,102,96,104,96,88,104,104,86,86,1,2,2,2,2,6,36,36,2,14,14,14,14,18,18,34,38,38,82,78,72,42,40,18,38,38,38,14,12,14,8,16,16,16,16,26,16,8,8,8,8,14,18,24,24,24,12,12,12,16,16,16,16,20,20,22,22,30,30,34,34,34,34,28,28,2,18,18,22,36,40,22,16,18,64,64,52,32,32,32,32,40,66,68,68,80,80,82,82,82,82,88,90,90,84,74,72,72,70,38,50,42,50,54,46,46,46,56,56,56,50,46,58,54,54,48,46,66,68,70,68,66,64,60,60,60,60,60,60,58,62,62,62,58,58,56,56,52,52,52,56,58,58,58,58,56,84,94,96,96,94,82,80,80,64,64,64,42,40,54,50,50,46,46,46,42,42,40,40,40,40,38,40,42,42,42,42,38,42,44,44,44,44,40,40,40,40,44,110,110,110,112,112,114,112,110,110,110,110,110,110,96,92,96,110,116,118,118,96,92,92,92,66,30,28,28,28,66,68,68,68,68,64,52,42,42,42,52,52,52,46,36,34,34,34,36,36,42,28,32,32,38,38,38,38,52,52,52,52,58,58,16,156,130,130,130,130,118,116,108,108,96,96,96,96,122,124,122,124,166,166,166,190,194,194,196,196,196,196,180,180,180,180,186,186,186,186,182,182,182,186,188,188,188,188,162,160,160,176,180,162,162,162,160,160,162,162,164,164,162,162,160,162,164,164,164,164,162,164,166,166,168,168,164,158,158,158,152,152,144,144,134,134,134,116,64,64,48,36,20,20,20,20,100,100,106,106,114,114,114,114,68,68,22,144,202,202,218,218,218,218,204,204,204,204,136,136,126,126,126,126,134,134,138,198,218,218,218,218,208,208,204,198,144,144,142,142,142,142,170,170,176,176,176,206,208,206,170,168,170,168,142,142,140,140,140,140,150,150,174,174,174,20,126,126,126,126,114,114,114,114,30,30,20,20,4,4,2,2,2,2,10,36,36,36,8,6,6,6,24,24,24,24,24,24,110,114,110,114,122,124,122,124,126,12,12,12,18,18,52,52,114,114,114,142,206,206,218,218,218,218,218,218,218,218,216,216,208,136,62,62,8,8,8,8,56,56,98,98,120,12,30,30,54,54,68,68,88,88,90,90,114,114,120,120,148,148,156,156,184,184,188,188,216,216,218,218,218,218,214,214,214,214,204,204,194,194,188,188,186,186,180,180,176,176,170,170,154,154,138,138,136,136,134,134,130,130,128,128,126,126,124,124,122,122,122,122,120,120,120,120,156,156,156,156,124,124,124,124,146,146,148,148,176,176,176,176,144,144,118,144,144,146,146,146,164,164,170,170,180,180,184,184,196,196,196,196,182,182,182,182,162,162,158,158,152,152,86,86,86,86,90,90,98,98,98,98,120,120,126,126,158,118,112,112,104,104,104,104,118,118,118,118,118,118,108,108,108,108,112,112,112,112,104,104,104,104,108,108,108,108,104,102,102,102,98,98,92,92,92,92,110,110,112,112,112,112,146,146,148,148,176,176,182,182,194,194,194,194,184,184,184,184,206,206,206,126,160,160,162,162,170,170,172,172,186,186,188,188,202,202,204,204,218,82,70,70,42,42,42,42,58,58,64,64,88,88,88,88,76,76,76,76,80,80,80,74,74,74,90,90,90,90,60,60,54,54,54,54,50,50,14,14,22,22,22,22,38,38,44,44,68,68,68,68,66,66,38,38,38,38,56,56,72,68,68,68,70,70,72,72,72,72,72,72,26,32,36,36,36,36,76,88,188,188,204,204,204,204,186,186,172,158,134,134,132,132,132,132,132,132,150,150,164,164,168,168,168,168,152,152,106,106,80,100,106,106,106,106,98,98,88,88,70,70,70,70,64,64,62,62,32,32,18,18,2,2,2,2,8,8,8,8,14,14,30,30,32,32,32,32,46,46,46,46,54,54,94,96,164,170,212,216,210,210,204,204,204,204,216,216,204,204,192,192,192,204,206,202,202,202,182,176,108,104,34,34,34,34,38,38,42,38,36,42,42,46,46,46,54,58,58,88,116,116,124,124,124,124,124,124,138,138,156,156,156,156,152,152,152,152,150,150,152,152,150,150,152,152,148,152,156,156,156,156,156,156,142,142,140,140,118,118,118,118,86,86,86,86,84,84,84,84,90,90,90,90,96,96,110,110,128,128,130,130,132,132,132,132,132,132,32,32,26,26,10,10,10,10,14,14,24,24,24,24,28,28,28,28,26,28,26,26,42,42,42,42,30,30,34,34,38,38,38,38,12,12,12,1,2,2,2,2,22,22,32,32,72,72,184,184,214,218,218,212,188,188,182,182,182,182,64,64,62,56,56,62,78,78,78,78,74,78,142,142,148,148,148,148,160,160,160,178,178,178,178,178,178,178,164,164,138,138,4,4,4,4,2,2,2,2,24,24,24,24,30,30,62,64,106,106,106,106,200,200,202,202,202,202,196,192,106,58,58,58,58,58,58,58,172,198,198,198,174,198,218,174,134,134,134,106,90,96,108,108,126,126,126,126,98,98,98,98,86,86,58,18,18,18,52,52,52,74,74,90,90,90,100,88,88,88,98,90,98,98,98,112,112,112,130,122,114,114,128,32,32,32,8,32,32,32,48,48,48,38,20,60,60,60,60,80,80,80,92,92,92,92,84,86,86,86,94,60,50,50,50,50,62,62,62,62,52,68,76,76,76,76,68,68,68,68,72,72,74,84,84,84,94,94,94,104,104,104,114,114,114,114,114,114,100,130,124,124,124,124,142,142,152,152,152,152,144,152,154,160,160,166,166,166,166,166,170,170,178,184,184,184,184,200,190,190,190,190,200,200,200,200,190,208,154,144,120,124,124,124,134,134,134,134,142,142,142,142,154,154,154,168,168,168,178,178,178,178,168,182,182,182,192,192,192,196,204,204,204,204,194,194,190,190,190,196,196,196,202,206,206,206,218,218,218,218,218,218,204,46,46,46,46,46,46,46,46,46,46,48,170,176,170,176,176,172,168,164,60,52,32,56,52,32,30,56,60,164,168,172,178,180,182,158,158,158,150,150,150,150,156,152,146,146,146,146,154,152,150,150,150,150,158,158,158,158,150,150,150,150,160,160,160,160,150,150,150,150,162,162,162,162,134,134,134,134,150,150,150,150,150,150,138,138,138,138,150,136,136,136,130,130,130,130,124,124,124,124,128,128,136,136,138,136,130,130,130,158,158,158,158,10,10,10,18,18,18,18,26,26,26,26,36,36,36,36,100,102,102,102,110,110,110,110,116,116,116,116,124,124,124,124,132,132,132,132,140,140,140,138,192,192,192,192,198,198,198,198,206,206,206,206,206,206,20,16,6,2,2,2,2,2,2,2,2,8,8,16,20,74,120,74,74,74,140,148,150,140,140,112,122,122,114,122,132,132,132,140,140,140,140,140,130,130,82,82,76,76,76,76,80,132,132,132,130,130,76,80,80,80,80,56,82,82,82,82,88,88,88,88,106,106,106,106,54,54,54,54,80,80,80,80,80,80,84,86,86,86,108,108,108,108,106,104,104,76,76,76,84,84,84,84,90,74,80,74,66,70,58,58,58,58,72,72,72,90,100,100,100,96,92,96,102,92,86,86,86,86,120,122,120,86,84,70,36,70,74,70,70,72,74,56,36,36,36,36,64,64,64,64,52,42,42,54,54,92,120,122,122,122,122,122,118,122,122,122,92,92,92,102,102,102,102,112,112,120,120,120,84,84,84,84,84,70,70,70,70,70,34,34,34,34,38,58,120,128,120,128,138,138,138,138,18,18,18,18,58,16,20,16,8,8,8,8,10,10,20,20,20,20,26,26,30,30,32,32,42,42,46,46,52,52,52,52,58,58,64,64,64,64,56,56,60,60,64,64,72,72,72,72,80,80,86,86,88,88,88,88,84,84,84,84,86,86,92,92,96,96,98,98,108,108,110,110,116,116,116,116,110,110,110,110,118,118,124,124,136,136,150,152,152,152,144,144,144,144,142,142,136,122,118,102,106,106,106,106,104,104,106,104,104,104,108,108,106,106,108,108,98,98,100,100,104,108,110,106,104,108,110,110,110,110,110,108,106,46,50,50,48,48,48,48,46,46,46,46,44,44,44,44,46,44,52,52,52,52,50,48,46,146,146,146,152,152,152,152,158,158,158,158,166,166,166,166,160,160,160,160,158,152,148,148,148,148,142,142,142,142,148,148,144,174,178,178,178,178,168,168,168,168,172,172,172,172,176,176,176,176,172,172,172,172,178,178,184,184,184,184,182,182,182,182,178,178,178,186,186,190,190,190,192,192,196,198,198,198,204,204,204,204,212,212,212,212,210,210,208,206,208,206,196,196,196,196,190,194,194,194,194,194,192,192,188,188,188,154,148,148,144,144,144,144,150,150,154,154,154,154,146,146,146,146,146,146,146,146,156,156,156,164,164,164,170,170,170,170,178,178,180,180,180,180,168,168,168,168,164,164,164,182,182,182,190,190,190,190,196,196,198,198,198,198,202,202,202,202,210,210,210,210,192,192,188,188,180,184,186,186,186,184,180,180,180,180,180,184,184,184,178,184,204,204,182,142,142,142,164,164,164,164,150,150,146,146,146,146,152,152,152,152,156,156,160,160,160,160,144,144,140,140,140,146,148,160,154,154,150,150,144,162,156,162,164,164,164,150,142,186,186,172,206,206,206,206,204,204,166,166,162,162,162,172,168,168,168,180,180,180,188,28,28,28,32,32,98,98,104,104,104,104,104,104,24,44,38,38,34,34,34,34,40,40,44,44,50,50,50,50,44,44,40,38,34,34,32,32,32,32,36,36,36,72,72,72,76,76,82,82,90,90,90,90,86,86,76,76,72,76,68,68,68,68,74,74,74,50,50,50,80,80,80,80,64,64,64,64,62,62,56,56,56,56,48,76,86,34,46,128,128,128,150,150,150,150,128,168,168,182,178,178,172,172,172,172,166,168,174,174,174,194,194,194,214,214,214,214,192,134,134,134,134,118,150,162,178,178,178,178,162,162,162,162,176,190,190,190,208,208,208,208,208,136,136,154,154,154,150,150,150,150,142,142,136,136,140,140,140,140,142,142,146,166,166,28,28,28,32,32,32,32,42,42,42,42,88,90,90,90,102,102,102,102,108,108,108,108,100,44,44,44,46,46,76,76,82,82,82,68,68,68,78,78,78,78,74,74,74,74,82,82,82,82,74,32,32,32,42,42,42,42,36,36,36,36,46,46,46,82,82,80,80,86,76,40,40,48,34,104,104,1,2,2,2,2,34,34,62,62,68,68,146,88,160,160,160,160,192,192,192,192,186,186,174,170,166,166,150,116,116,116,116,116,132,132,134,134,134,134,140,140,140,140,146,146,146,116,116,116,116,132,132,78,78,78,58,58,50,50,50,50,132,132,176,176,176,172,168,168,140,140,76,76,64,64,64,140,140,140,140,140,38,38,28,28,28,28,36,36,116,120,120,120,164,164,164,164,130,130,46,46,32,32,32,2,2,2,66,66,102,102,140,140,142,142,142,142,132,132,130,134,140,140,154,154,160,160,174,174,174,174,186,186,204,204,204,204,198,198,94,50,2,2,2,2,4,4,66,66,82,82,112,112,128,128,148,2,2,2,62,64,64,64,92,98,114,114,114,114,216,218,218,218,46,42,42,42,46,46,82,82,86,86,118,118,120,120,148,148,154,154,198,198,200,200,204,204,204,204,192,192,140,140,108,108,108,108,106,96,68,68,2,2,2,2,4,4,6,6,18,18,102,102,106,106,106,106,138,138,144,144,168,168,172,172,176,176,176,176,194,194,194,166,46,46,2,2,2,6,110,110,124,124,124,124,120,120,54,54,52,148,148,114,74,74,74,74,18,18,18,18,86,86,86,86,48,48,48,48,112,112,112,130,134,134,142,142,142,142,92,42,42,42,92,92,102,92,116,116,116,116,112,112,98,98,48,48,40,48,48,48,50,50,56,56,58,58,70,70,72,72,74,74,76,76,78,78,80,80,82,82,86,86,88,88,90,90,92,92,102,74,74,74,70,70,70,70,50,50,50,50,58,58,58,58,66,66,66,66,86,86,86,86,90,90,90,90,86,86,74,74,74,74,94,92,92,92,92,86,82,82,82,82,64,64,64,64,76,64,54,64,78,78,78,78,76,76,52,50,48,48,44,34,48,2,26,28,54,54,56,78,78,78,44,42,42,42,96,96,96,96,96,96,96,96,74,58,38,36,36,70,70,70,80,80,96,80,74,74,62,62,62,62,70,90,94,94,100,100,104,104,94,102,102,102,96,86,86,86,72,72,88,88,128,148,148,148,106,94,94,94,148,162,162,162,160,142,136,136,134,134,124,124,124,124,150,150,150,150,154,154,184,186,186,186,184,182,158,156,134,132,132,132,84,74,72,72,62,62,62,62,94,74,74,74,72,72,56,54,24,18,14,10,2,2,2,2,2,2,32,34,34,34,28,28,22,28,38,38,38,38,66,66,66,66,72,72,96,96,98,98,98,98,100,100,126,126,126,126,150,150,150,150,136,136,134,134,134,134,138,134,84,74,74,74,108,108,108,108,108,108,148,152,152,152,146,146,144,144,144,144,126,126,106,106,106,106,82,82,34,30,30,30,82,82,82,82,82,82,82,82,120,120,120,120,142,136,128,128,128,128,114,114,114,114,104,98,96,96,96,1,2,2,2,2,18,18,18,18,24,24,28,28,30,30,30,30,26,26,18,16,16,16,18,18,20,20,26,26,26,26,20,24,82,90,90,90,142,146,66,66,54,54,50,50,30,30,26,26,26,26,26,26,142,144,144,144,114,114,110,106,82,82,80,80,60,60,58,58,46,46,42,42,34,34,32,32,32,32,32,32,28,28,28,28,34,34,38,38,38,38,48,48,48,48,56,56,56,56,64,64,64,80,80,80,74,74,70,70,66,66,66,66,70,70,72,72,72,72,72,90,90,90,78,78,78,78,94,94,94,94,90,90,86,86,86,86,92,92,106,106,104,104,104,104,102,102,100,100,100,100,102,102,106,106,106,106,104,104,108,108,110,110,120,120,116,116,114,114,114,114,116,116,122,122,124,124,124,124,120,120,112,82,82,50,50,82,44,76,76,76,72,202,166,166,166,166,198,200,200,200,200,28,38,38,44,44,50,50,60,60,64,64,72,72,76,76,86,86,88,88,98,98,102,102,114,114,126,126,136,136,138,138,148,148,150,150,160,160,164,164,176,176,178,178,190,190,196,196,210,48,94,44,44,44,58,58,58,58,60,62,60,62,80,80,80,80,66,66,66,66,96,96,96,96,90,104,104,104,94,94,94,94,104,104,104,104,118,118,118,118,132,132,132,132,118,118,118,118,112,110,104,110,112,120,120,172,172,172,164,164,164,172,172,172,198,198,198,198,198,198,198,198,198,198,170,172,200,116,116,116,102,102,102,102,114,114,114,114,122,122,122,122,134,134,134,134,126,122,132,120,120,128,126,126,120,120,120,120,114,162,162,160,154,160,182,182,182,182,190,190,186,146,146,146,158,158,158,158,148,38,38,38,38,38,56,56,58,58,58,58,34,34,34,34,34,38,54,54,54,34,34,34,34,34,34,54,56,58,58,58,68,68,70,70,70,70,68,68,60,60,56,52,44,24,18,18,14,14,14,14,30,30,30,30,28,22,18,28,28,28,14,14,14,14,14,14,32,32,172,172,182,182,182,168,156,156,106,106,80,80,54,54,40,40,34,34,26,26,18,170,178,178,180,180,180,176,172,106,106,106,96,96,96,96,100,100,106,106,112,112,112,112,104,112,116,116,124,124,124,124,122,122,108,108,104,104,104,114,116,116,116,116,126,126,126,126,116,116,116,136,136,136,134,126,134,110,92,88,92,88,86,86,86,86,90,90,106,104,98,96,88,42,42,42,44,44,174,174,174,174,174,78,78,78,84,84,88,88,88,88,90,90,96,96,96,100,100,100,104,104,108,108,108,108,112,98,114,122,122,122,128,136,136,136,146,154,154,154,162,162,162,162,162,162,158,158,158,158,162,162,164,164,164,164,162,162,160,160,160,160,162,150,142,142,130,130,130,130,132,132,140,140,140,140,130,142,130,130,130,130,118,118,114,114,114,114,118,118,136,136,136,136,122,130,132,132,134,134,134,134,126,66,56,54,56,62,62,62,52,50,52,62,70,70,70,76,70,70,66,66,66,66,74,74,76,76,76,76,74,76,76,76,78,78,82,82,82,82,80,80,76,76,76,76,80,80,86,86,86,86,90,90,92,90,88,88,88,88,90,90,94,94,96,96,96,96,94,94,96,96,94,92,90,90,90,90,86,86,86,86,88,88,90,1,2,2,2,2,64,64,74,74,94,94,134,134,136,140,140,140,182,184,184,184,176,176,142,134,134,104,104,104,52,52,40,40,40,40,56,56,62,62,62,62,82,84,84,84,92,92,96,96,96,98,108,108,118,118,124,126,122,122,182,182,182,182,172,182,182,182,90,90,88,88,88,88,136,136,136,136,138,138,148,148,148,148,150,150,200,200,204,204,204,204,180,158,82,68,46,46,18,18,18,24,40,42,42,42,62,62,62,62,98,98,66,98,100,100,100,100,104,104,128,128,128,128,68,68,2,98,140,140,154,154,156,154,90,90,90,90,130,130,130,130,94,94,94,130,156,156,172,172,172,172,146,146,144,146,166,164,152,152,152,152,180,180,180,180,184,184,188,184,132,132,132,132,190,30,56,56,56,56,74,74,74,74,102,102,102,74,134,134,134,134,96,134,218,74,2,28,28,28,34,56,56,56,74,74,74,56,56,56,42,42,38,38,38,38,32,32,22,22,14,14,14,14,44,44,92,92,106,106,140,140,188,188,190,190,190,190,158,158,24,24,22,22,22,22,22,22,34,134,134,134,152,152,152,150,154,154,198,198,198,198,172,172,154,154,154,154,156,156,204,206,172,22,22,22,30,30,30,30,30,36,36,36,36,36,46,46,48,48,48,56,56,56,56,56,62,62,66,66,60,60,54,54,54,54,62,76,76,76,88,86,76,78,86,106,106,106,114,108,114,122,122,122,122,130,130,130,130,130,136,136,138,138,138,138,134,134,130,130,134,134,138,156,148,148,148,148,156,148,156,166,166,166,166,126,126,124,114,114,112,112,110,110,110,110,126,126,128,128,130,130,126,126,84,84,80,80,80,80,78,78,76,76,70,70,70,70,76,76,66,66,56,56,48,48,54,54,58,58,58,58,54,54,46,46,44,44,44,44,30,30,26,26,26,26,34,34,36,36,30,30,14,14,8,8,8,8,14,14,26,26,36,36,28,28,24,24,16,16,16,16,76,76,78,78,78,78,76,76,66,66,60,60,60,60,82,84,84,84,80,80,78,78,74,74,74,74,80,80,92,92,98,98,108,110,118,38,38,38,66,66,66,66,38,84,84,84,106,106,106,106,84,124,124,124,140,140,140,140,130,130,130,130,46,46,46,46,38,38,38,124,124,124,32,38,38,38,138,138,138,72,72,72,82,50,50,50,106,106,106,94,94,94,90,90,90,90,86,86,86,86,82,82,82,82,68,68,68,68,64,64,64,64,60,60,60,60,56,56,56,74,74,48,48,56,56,92,92,98,98,98,98,92,92,56,56,48,48,48,48,56,56,56,56,64,64,98,98,64,32,32,32,80,86,86,88,88,62,62,60,60,32,98,136,136,136,136,110,110,110,110,54,54,50,70,80,86,110,70,60,140,140,140,138,40,40,40,136,136,136,134,38,42,42,42,90,94,146,100,126,126,126,126,100,100,100,48,70,72,72,72,44,44,44,56,56,46,72,104,128,104,96,112,112,72,72,72,100,100,100,88,98,110,110,110,126,126,126,78,110,110,110,110,78,78,78,92,92,106,108,106,72,116,112,112,112,116,116,116,112,112,114,114,116,116,116,116,118,118,114,114,114,86,110,22,22,22,26,26,26,26,116,116,118,118,118,118,184,184,184,184,202,184,184,184,202,200,200,184,186,118,118,118,184,182,116,184,182,184,134,134,120,120,114,114,114,114,20,26,26,26,102,100,100,62,70,70,82,82,88,88,88,54,54,54,52,52,40,40,30,100,100,100,82,70,66,66,62,62,20,98,98,98,104,104,104,104,96,102,118,118,124,124,138,104,130,130,130,130,138,2,8,8,8,8,20,20,20,20,30,30,36,36,36,36,38,38,52,52,52,52,72,72,80,80,84,84,92,92,102,102,102,102,106,106,106,106,120,120,120,120,132,132,158,158,158,158,178,178,178,178,192,192,192,192,210,210,210,210,218,6,114,114,114,114,2,2,2,6,16,16,16,16,6,6,6,8,14,14,14,2,218,180,180,180,164,164,162,162,162,162,160,160,68,68,68,68,46,46,46,76,86,86,86,86,74,74,74,78,78,84,72,138,154,156,154,156,156,148,156,148,138,138,136,136,136,136,136,136,136,136,136,136,136,146,146,134,158,132,132,132,132,132,132,132,112,112,112,134,128,122,126,126,126,126,120,120,120,120,122,174,174,174,172,172,166,166,166,166,170,174,178,178,178,178,218,192,192,186,200,154,68,68,68,68,82,82,132,132,150,150,150,2,218,180,170,160,154,146,138,128,122,102,92,86,78,72,62,56,48,40,36,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,42,44,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,128,128,128,138,138,144,144,144,144,126,126,126,126,130,130,130,130,130,130,130,116,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,22,22,22,56,56,56,56,82,82,82,82,118,118,118,118,16,16,16,16,42,14,40,54,54,54,72,72,72,72,68,68,54,54,54,106,106,110,146,150,150,134,100,134,150,140,178,178,190,190,190,190,178,178,144,144,144,150,200,200,200,190,144,124,62,78,78,78,78,50,50,50,26,26,26,26,42,42,42,42,34,34,32,54,78,78,82,82,82,82,48,48,46,46,46,46,54,84,86,86,110,110,120,120,126,126,128,128,146,160,170,170,176,176,192,192,192,12,12,12,36,36,36,36,52,52,52,52,74,74,74,74,92,92,92,92,112,112,112,112,114,114,122,110,120,122,136,132,126,138,138,138,160,160,160,160,160,160,160,160,160,170,178,180,178,170,160,158,160,180,184,184,184,184,158,158,158,158,162,162,174,162,186,186,186,186,198,186,186,186,176,126,122,122,122,122,146,146,146,146,126,68,84,84,90,90,100,96,90,36,36,2,3,2,148,148,218,218,218,206,202,202,136,136,2,14,14,14,16,16,34,34,42,42,42,82,82,82,102,102,108,108,108,108,112,144,144,144,146,146,160,160,178,178,178,134,124,112,102,92,82,68,60,50,40,32,18,56,66,74,84,92,106,2,126,126,126,92,92,92,92,68,68,68,68,38,38,38,38,42,42,2,70,72,72,72,70,70,2,70,2,70,2,2,40,40,52,54,40,54,78,72,70,70,58,72,2,52,64,64,88,88,88,88,82,82,66,66,54,54,54,54,74,74,80,80,92,92,94,118,100,100,98,98,98,98,108,108,112,112,128,128,128,128,128,128,124,124,114,114,114,114,116,140,142,154,154,154,164,164,174,174,174,174,162,162,146,194,194,180,212,180,166,166,162,162,162,162,180,180,188,188,196,196,196,196,208,208,208,208,206,206,204,204,190,190,176,176,174,174,174,100,100,100,120,120,98,100,122,132,132,132,158,160,160,160,186,186,210,200,170,30,30,30,50,50,54,54,54,54,42,42,28,42,52,52,58,58,58,58,50,50,26,26,26,30,30,64,64,64,64,68,68,68,78,78,78,78,92,92,94,94,94,94,96,96,108,108,108,68,50,50,38,38,38,38,56,56,56,56,74,74,74,74,74,74,88,88,88,88,88,88,88,88,88,88,90,90,100,100,100,100,114,114,116,116,120,128,128,126,146,146,146,146,148,148,158,158,170,170,176,176,184,184,184,184,180,180,176,176,172,172,172,172,170,170,164,164,158,158,158,158,152,150,142,138,138,138,128,128,126,126,126,126,116,116,114,114,106,106,106,76,84,84,84,84,78,78,76,76,74,74,74,94,100,100,100,100,94,94,88,88,88,88,94,40,60,80,76,94,90,126,126,126,136,136,138,138,138,138,132,132,126,130,138,138,138,160,160,160,170,170,170,170,158,68,68,68,76,76,78,78,78,78,96,96,96,96,116,116,116,116,66,66,66,72,72,72,86,82,82,92,92,92,108,104,104,114,136,136,136,136,114,126,126,126,130,130,130,130,128,128,128,138,150,150,150,150,136,126,126,126,132,132,132,116,128,128,128,66,44,44,40,40,40,40,68,48,48,48,54,54,54,40,20,44,38,38,28,28,24,24,24,24,16,16,16,46,46,46,58,58,58,70,82,82,82,82,66,72,78,78,78,74,74,98,112,112,112,112,90,90,90,96,96,96,106,106,106,106,96,86,82,82,82,82,90,90,90,90,84,68,106,106,106,84,84,84,90,90,90,90,84,90,90,82,82,34,34,34,34,34,160,160,160,160,142,142,48,44,28,28,18,18,14,14,2,2,2,2,2,2,2,2,4,4,4,4,28,28,110,110,110,110,110,106,58,58,6,6,6,6,108,110,114,114,216,216,104,104,72,72,58,52,52,52,72,72,76,80,112,112,138,138,168,172,192,192,192,192,218,218,218,86,146,148,148,148,78,78,68,68,68,72,144,144,144,144,70,70,70,80,84,86,138,138,138,134,92,90,72,40,30,30,28,28,28,28,34,34,46,46,52,52,52,52,42,42,38,34,30,30,30,30,48,48,48,48,60,56,50,50,64,60,46,46,46,46,36,36,36,36,52,52,52,52,134,134,134,134,148,148,148,148,148,148,162,162,162,162,162,162,176,176,176,176,194,194,194,194,176,176,176,170,158,158,158,158,130,130,130,128,98,96,60,48,20,20,12,12,12,12,22,22,12,12,12,12,20,20,20,20,12,12,16,16,26,26,26,26,16,16,16,16,30,30,30,30,18,18,18,18,28,28,28,28,20,24,206,206,206,206,202,202,190,190,186,182,10,10,10,10,2,2,2,2,14,14,4,4,4,4,14,14,4,4,4,4,12,12,12,12,4,4,4,4,12,12,12,12,4,4,4,4,14,14,14,14,4,4,4,4,14,14,14,14,6,6,6,6,22,22,22,1,4,4,20,20,20,20,18,18,16,16,16,16,16,16,26,26,34,34,40,40,40,40,22,22,22,22,46,46,46,46,22,22,22,22,50,50,50,50,34,34,34,34,42,42,62,62,76,76,76,76,68,68,68,68,76,76,92,92,92,92,84,84,84,84,96,96,96,96,106,106,106,106,94,94,94,94,108,108,108,108,90,90,90,90,104,104,104,104,96,96,20,4,2,118,118,118,124,124,130,130,130,130,108,108,108,108,126,126,126,126,112,112,126,126,126,126,104,104,104,104,122,122,122,122,92,92,92,92,118,104,100,100,100,100,124,124,140,140,140,140,130,130,130,130,136,136,136,136,140,140,140,140,134,134,134,134,148,148,148,148,134,134,134,134,152,152,152,152,140,140,140,140,128,128,108,108,108,108,100,100,100,164,210,210,218,166,164,164,146,164,166,166,168,168,168,168,180,180,196,196,196,196,148,152,168,168,178,1,2,2,2,2,42,42,42,42,82,82,82,82,34,34,34,38,74,74,74,74,82,82,90,90,90,90,102,102,102,102,110,144,144,144,156,156,156,156,138,138,138,138,152,152,152,152,148,164,164,164,148,148,148,148,168,168,168,168,162,162,162,120,120,120,96,96,96,102,132,132,136,136,136,168,170,170,188,188,190,188,180,180,180,180,192,192,180,180,180,180,180,180,180,112,112,112,98,98,98,98,98,98,98,94,44,44,44,44,32,32,32,32,32,58,74,74,76,76,76,76,72,72,28,28,28,28,10,10,10,14,34,34,34,34,54,54,66,66,86,86,88,88,90,90,90,90,96,96,134,134,148,148,152,152,168,168,172,172,186,186,198,198,200,200,200,200,206,206,208,20,20,20,20,20,28,28,30,30,30,30,42,42,42,42,54,54,54,54,44,44,18,18,18,56,56,56,124,126,150,150,150,150,166,166,166,166,176,176,176,176,188,188,188,188,186,186,174,174,174,174,168,168,162,162,148,148,148,100,100,100,92,92,92,92,112,112,112,112,120,120,120,120,96,38,36,36,36,36,46,46,156,156,156,156,172,172,172,172,166,166,166,166,52,52,52,52,38,36,34,1,4,4,8,36,36,36,28,28,18,18,14,14,14,14,22,22,28,28,44,44,44,44,32,32,28,28,20,136,130,130,120,120,114,114,108,108,108,108,120,120,124,124,136,136,136,136,130,120,118,140,174,174,174,174,146,146,58,58,32,32,6,6,6,6,12,48,100,34,68,68,80,80,82,82,82,106,142,142,164,164,104,104,104,104,108,60,60,60,70,70,70,72,80,80,90,70,70,70,82,62,62,56,56,56,38,94,94,94,100,100,100,100,106,106,106,106,100,100,100,100,94,94,94,94,86,86,86,86,92,92,92,66,66,66,86,86,104,104,128,128,128,128,98,98,78,78,60,60,60,60,70,78,72,72,66,66,66,66,76,76,80,80,82,82,82,82,74,70,74,70,64,106,100,100,96,96,96,96,106,106,108,108,112,112,112,112,108,108,104,106,106,106,110,110,116,116,130,130,130,130,118,68,68,68,62,62,60,60,46,46,46,46,48,48,50,50,50,50,62,70,70,70,74,74,80,80,80,76,70,70,70,70,76,76,76,102,100,100,98,98,98,98,106,112,114,114,114,108,104,104,110,88,88,88,90,90,94,94,96,96,96,96,90,90,88,92,92,92,90,90,82,90,92,92,98,92,92,92,94,94,104,116,120,120,124,124,124,124,114,114,110,110,104,104,102,66,66,66,62,62,60,60,56,56,56,56,62,26,26,20,30,30,36,36,36,36,34,34,22,26,34,34,34,34,32,32,18,44,44,38,54,44,52,40,56,112,112,112,108,108,94,94,86,86,86,86,100,100,112,112,118,118,118,118,104,122,124,124,130,130,130,130,138,138,138,138,148,144,144,144,152,152,152,152,158,158,158,128,134,134,142,142,148,148,158,160,160,160,176,176,180,180,194,194,194,194,190,190,190,180,180,26,26,22,22,22,40,40,40,40,38,38,20,68,68,68,70,70,70,70,62,62,58,58,44,44,44,44,52,52,52,52,58,58,68,66,72,72,74,74,74,74,70,70,70,70,78,78,78,78,68,48,48,48,44,44,30,30,24,24,24,24,36,36,46,46,46,46,42,42,42,60,58,58,52,52,52,52,58,58,58,58,60,60,72,72,72,72,68,68,54,64,64,64,80,86,86,96,96,96,98,98,104,104,104,104,112,112,112,112,124,124,124,124,122,122,122,124,124,124,124,130,130,130,132,132,144,128,148,128,140,174,174,174,178,178,178,178,170,170,164,164,154,154,154,154,174,174,172,172,168,168,156,156,152,12,40,40,40,40,16,16,16,16,42,42,42,68,52,52,52,22,22,22,64,64,64,64,102,102,102,102,126,126,126,126,100,100,100,100,70,70,70,70,52,52,74,74,74,74,40,40,40,40,26,26,24,24,24,12,56,56,56,56,26,26,26,26,98,98,98,98,130,130,130,130,152,152,152,152,120,120,120,120,76,76,76,76,114,114,114,114,86,86,86,86,94,94,126,126,126,126,140,140,140,140,174,174,174,174,196,196,196,196,152,152,152,152,124,124,124,124,140,140,140,140,176,176,176,176,208,208,182,182,180,180,180,180,182,182,198,198,204,204,204,204,188,128,126,120,94,94,94,94,118,118,118,118,90,60,54,54,54,54,48,48,48,48,42,42,42,42,6,6,6,6,10,10,10,10,20,20,20,20,26,26,26,26,36,36,36,36,44,44,44,44,88,88,88,88,100,100,100,100,106,106,106,106,110,110,110,110,114,114,114,114,110,110,110,110,114,114,114,114,118,118,118,118,68,68,68,68,64,64,64,64,60,60,60,60,56,56,56,56,48,48,48,48,66,66,66,66,50,78,78,78,92,92,92,92,76,76,88,90,90,90,76,76,76,44,24,24,24,24,34,34,34,50,64,64,64,64,50,50,50,158,170,170,170,170,168,168,156,156,156,156,156,166,166,170,192,174,192,174,192,166,166,156,156,156,138,156,138,154,136,154,154,158,158,158,170,182,182,182,198,182,198,1,4,4,14,14,38,38,38,38,6,6,4,4,4,4,26,26,30,30,46,46,48,48,64,64,64,64,60,60,56,56,20,20,20,20,34,34,36,36,60,60,78,78,78,78,90,90,98,98,128,128,154,154,156,156,174,174,174,174,160,160,174,174,178,178,188,188,190,190,200,200,188,188,188,188,202,202,186,186,186,186,200,200,188,188,188,188,198,198,198,198,186,186,174,174,160,160,160,160,174,174,158,158,158,158,172,172,138,138,136,136,116,116,114,114,94,94,92,92,62,62,60,60,50,50,48,48,40,40,40,40,78,78,78,78,52,52,52,52,64,64,64,64,74,74,76,76,76,76,88,88,88,88,100,100,100,100,92,92,92,92,110,110,122,122,142,142,142,142,152,152,164,18,18,18,38,48,48,48,68,68,68,68,42,76,78,78,84,84,84,84,98,98,104,104,110,110,110,122,122,122,142,142,118,118,146,46,46,72,62,62,54,54,54,54,60,60,64,64,72,72,72,72,60,138,138,138,148,148,154,154,168,168,168,168,134,136,136,136,128,128,120,120,104,104,104,104,134,38,38,38,62,68,68,68,88,88,88,88,84,84,64,64,64,92,92,92,114,114,120,120,146,152,152,152,172,172,152,152,170,192,28,86,108,108,108,108,104,104,90,90,80,80,80,80,86,86,86,86,128,128,130,130,130,130,74,74,70,70,70,70,88,96,96,96,96,96,100,100,112,112,124,124,124,124,116,100,100,112,112,106,116,106,100,108,104,104,104,104,98,98,98,116,116,100,100,100,104,104,124,124,124,102,84,84,84,84,98,84,76,76,76,76,82,82,88,80,76,76,76,76,82,82,82,82,76,76,76,84,88,88,88,88,88,88,88,76,68,68,68,68,68,80,80,78,76,74,74,78,82,80,80,80,74,74,74,74,82,82,82,82,78,78,72,76,76,76,82,82,82,82,76,80,74,74,74,78,78,120,142,142,144,144,144,144,126,146,142,142,142,142,152,152,152,152,148,148,150,150,152,150,146,110,110,110,122,122,122,116,116,122,126,126,130,130,130,124,120,120,120,110,110,64,62,62,60,60,60,60,60,60,50,50,48,48,48,48,56,48,44,44,44,44,38,38,34,34,36,36,38,38,38,38,34,34,32,24,20,20,20,20,24,106,106,106,110,110,112,112,114,114,114,114,116,116,118,118,120,120,120,120,114,104,104,104,110,110,110,110,118,122,122,122,114,112,112,112,102,102,102,102,94,94,88,88,88,88,84,84,80,80,74,80,84,84,86,70,68,100,104,104,104,104,118,104,88,78,76,76,82,82,88,88,94,94,102,104,104,108,108,108,112,116,112,112,110,106,104,104,104,104,108,108,106,106,110,108,108,108,110,110,112,110,78,84,84,88,122,122,112,94,86,102,104,86,70,84,98,98,100,100,98,98,98,98,104,104,104,104,102,100,100,100,102,102,106,106,106,106,104,102,100,106,106,106,102,102,102,102,94,94,94,94,92,92,92,92,90,90,90,90,76,76,76,76,72,72,72,72,64,64,64,64,58,58,58,58,54,54,54,54,52,52,52,52,50,46,40,40,36,36,36,36,34,34,34,34,32,32,32,32,24,24,24,104,104,104,108,108,108,108,102,102,102,102,100,100,100,100,98,98,98,98,102,102,102,102,104,104,104,104,102,102,102,102,100,100,100,100,102,102,102,102,104,104,104,104,100,100,100,102,102,102,112,112,114,114,138,138,138,138,132,132,132,132,138,138,138,110,110,110,108,108,108,108,106,106,106,106,104,104,104,104,106,106,108,106,104,104,100,100,100,100,106,106,106,106,102,102,102,102,110,110,108,108,104,104,104,104,106,106,106,106,110,110,110,110,106,106,106,106,108,108,108,108,104,104,104,104,106,106,106,106,104,104,104,104,108,108,108,108,112,112,112,112,106,106,104,104,104,104,110,110,110,114,114,114,112,112,112,112,110,110,110,110,108,108,108,106,106,106,106,106,108,108,108,108,108,108,104,104,104,104,106,106,110,110,112,112,114,114,114,114,118,118,124,124,128,128,128,128,132,132,132,132,126,126,126,126,130,130,130,130,136,136,136,136,134,102,102,102,102,102,94,94,94,94,92,92,92,92,84,84,80,80,68,68,68,68,60,60,60,60,48,48,48,48,40,40,40,40,34,104,104,104,112,112,120,120,120,120,128,128,128,128,138,138,138,138,142,142,142,142,148,148,148,148,156,156,156,156,164,164,164,164,170,170,178,178,178,178,176,176,176,116,116,116,118,118,118,118,126,126,126,126,134,134,134,142,148,148,148,148,154,154,154,154,160,160,168,172,172,110,110,110,110,110,116,116,116,116,122,122,122,122,124,124,124,124,126,126,126,126,128,128,128,128,130,130,130,130,132,132,132,132,134,134,136,136,136,106,106,106,98,98,98,98,90,90,90,90,86,86,86,86,78,78,78,78,74,74,74,74,70,70,64,64,62,62,62,120,120,120,130,130,130,130,136,136,136,136,146,146,146,146,156,156,156,156,160,160,166,166,166,86,86,86,90,90,92,92,92,92,84,84,72,72,42,42,38,38,32,32,32,24,24,24,34,34,36,36,36,36,46,46,46,46,56,56,56,56,52,52,30,30,26,26,26,26,16,16,8,8,8,8,14,14,16,14,12,12,8,8,8,8,20,68,84,84,88,88,88,88,94,94,96,96,96,96,92,92,84,92,104,104,112,112,112,112,108,108,100,100,98,98,98,98,94,94,92,92,88,88,88,88,90,88,86,86,84,84,66,66,62,62,62,62,72,72,72,68,66,82,86,132,132,132,132,132,156,156,158,158,168,168,168,168,170,170,172,172,172,172,168,168,158,168,176,176,182,182,182,182,172,172,172,172,170,170,164,164,160,160,160,160,158,158,146,146,144,144,124,124,120,120,120,120,122,122,124,124,128,128,128,84,84,84,90,90,96,96,106,106,106,106,96,96,96,96,112,112,108,108,80,80,78,78,78,78,76,76,56,56,52,52,46,46,46,46,36,36,36,36,38,38,46,46,50,50,50,50,50,50,36,40,50,50,52,52,54,54,54,54,56,56,62,62,72,72,70,70,68,70,78,78,80,80,80,116,84,84,76,76,76,76,84,84,74,74,66,66,64,64,64,64,68,68,72,72,72,72,70,70,58,70,110,110,110,110,112,112,102,112,130,130,130,130,126,126,120,120,112,112,112,112,110,108,104,72,64,64,60,60,60,60,72,72,106,106,110,110,110,110,94,94,66,66,58,58,58,58,46,46,46,46,54,54,64,104,150,150,100,100,144,94,94,94,126,126,128,80,78,78,78,54,52,52,52,52,22,22,20,20,20,20,60,60,56,56,10,8,56,30,30,30,34,34,94,94,108,108,108,108,92,92,82,82,56,56,42,42,28,28,28,98,98,98,112,112,150,150,162,162,164,164,164,164,118,118,106,106,106,140,140,140,114,114,114,114,188,188,188,188,190,190,190,190,146,146,146,146,140,92,44,32,94,2,8,8,18,18,42,42,50,50,78,78,94,94,106,106,110,110,112,112,116,116,124,124,132,132,142,142,146,146,148,148,152,152,154,154,162,162,162,162,176,176,176,176,182,182,194,194,218,180,202,202,202,202,210,210,218,176,192,192,192,180,174,160,188,188,188,188,162,162,196,196,196,196,218,54,68,68,68,68,92,92,92,92,102,102,108,108,108,108,120,120,126,126,136,136,144,144,146,146,146,146,148,148,152,152,158,158,170,170,174,174,180,180,180,180,182,182,192,192,202,202,208,208,208,208,218,54,20,20,20,20,2,2,60,60,60,60,72,72,2,18,18,48,48,94,120,120,120,120,112,112,112,112,96,96,96,96,88,88,88,88,100,100,100,100,94,94,94,110,110,110,118,118,118,118,124,124,124,124,118,106,106,106,112,106,100,100,100,100,104,88,82,82,82,82,88,94,94,100,88,100,100,100,108,108,108,110,110,110,118,122,118,134,134,134,128,128,116,126,136,122,122,122,122,122,138,108,108,108,100,108,118,80,134,134,134,134,78,78,78,92,92,100,108,108,108,108,96,96,96,96,100,116,116,104,104,94,114,114,124,124,134,134,134,134,130,130,120,120,82,82,76,76,76,76,62,62,62,62,122,98,104,104,104,104,94,94,94,100,104,104,104,98,98,98,102,68,56,56,56,44,18,18,18,18,44,44,44,26,26,32,32,32,38,38,38,38,30,140,132,132,126,126,126,126,138,138,144,144,152,152,152,152,138,138,138,138,140,140,140,68,62,62,46,46,46,46,60,60,76,76,86,86,86,86,72,72,70,142,142,142,154,154,162,142,124,124,114,142,98,98,72,66,76,76,78,132,132,132,110,110,72,96,96,96,92,96,100,96,96,96,90,96,100,90,56,140,148,148,160,160,160,160,154,154,142,162,152,152,152,152,162,162,170,170,170,170,160,160,158,158,158,150,84,82,82,82,78,88,88,88,82,158,158,168,168,168,162,68,68,68,40,40,28,28,20,68,38,38,32,32,16,16,14,72,72,72,88,88,104,104,104,104,96,96,96,96,100,100,100,72,72,214,210,210,200,200,192,192,182,182,180,180,168,168,158,158,148,148,146,146,136,136,120,120,110,110,108,108,100,68,62,62,54,54,42,42,30,30,28,28,20,20,14,14,2,2,2,148,148,148,170,170,170,170,146,148,168,170,148,20,20,16,28,28,28,28,16,16,16,16,30,20,20,20,26,26,26,22,22,26,26,14,14,14,16,16,18,18,20,20,22,22,24,24,24,24,22,22,18,18,16,16,16,16,18,18,20,20,24,24,26,26,30,30,30,30,28,28,28,28,30,30,32,32,34,34,38,38,44,44,44,44,38,38,38,38,46,46,46,46,44,44,38,38,38,38,48,48,52,52,54,54,56,56,56,56,52,52,50,50,48,48,44,44,44,44,42,42,40,40,36,36,36,36,38,38,38,38,36,36,30,30,30,30,36,36,36,36,34,34,30,30,28,28,28,18,14,14,12,12,12,12,16,16,16,16,10,10,8,8,8,8,6,6,6,6,20,26,38,38,100,100,218,8,20,30,50,50,74,74,92,92,108,108,126,126,152,8,22,30,72,72,80,80,82,82,92,92,92,92,90,90,88,88,86,86,84,84,84,84,92,92,92,92,90,90,94,94,96,96,102,102,102,102,104,104,106,106,110,110,112,112,120,120,120,120,114,114,114,114,118,118,120,120,124,124,124,124,122,122,122,122,128,128,130,130,138,138,146,146,148,148,148,148,144,144,144,144,150,150,152,152,158,158,160,160,166,166,166,166,168,168,170,170,176,176,176,180,180,182,182,186,186,186,188,188,194,194,204,204,218,76,90,116,126,150,196,192,194,194,206,206,218,218,192,192,192,160,160,160,172,176,180,184,188,194,198,204,208,156,156,156,156,156,156,156,156,156,156,156,156,156,156,152,152,152,162,162,164,164,176,180,184,188,192,196,200,154,154,154,154,154,154,150,146,140,136,132,128,124,120,116,112,106,102,96,92,86,82,78,74,152,152,146,142,140,138,126,122,118,114,8,18,28,40,40,64,64,114,114,138,138,150,164,186,68,68,68,78,78,80,80,94,94,110,110,110,110,102,102,102,114,114,114,106,106,86,76,76,76,80,80,80,80,74,174,174,174,184,184,184,184,174,174,184,184,174,174,184,20,20,20,46,46,46,46,56,46,18,14,14,14,8,14,26,50,50,48,46,48,52,50,50,128,122,122,120,98,104,104,106,106,106,126,126,126,120,126,136,136,136,136,90,90,84,84,84,84,100,136,140,140,154,154,154,154,160,160,160,122,122,122,126,126,132,132,142,142,144,144,144,160,160,160,166,166,166,166,152,152,152,152,162,146,142,146,146,146,136,136,136,136,146,122,100,100,94,94,94,94,126,126,132,132,132,132,130,130,116,110,110,110,116,120,120,120,126,132,132,132,148,132,142,142,148,132,140,140,154,96,102,102,102,102,96,96,96,120,120,120,126,126,126,126,118,100,100,100,104,104,104,104,98,108,114,114,114,114,108,108,108,120,120,120,126,126,126,126,120,92,96,96,96,96,92,92,92,130,130,130,134,134,134,134,128,32,34,32,30,36,32,42,44,48,48,44,46,40,42,2,218,58,58,58,66,60,56,60,60,60,60,60,74,74,78,78,82,82,82,82,84,84,92,92,92,92,92,92,98,108,108,108,96,110,110,110,122,102,116,126,126,126,126,126,112,126,140,126,126,158,142,142,142,142,142,142,158,142,142,142,142,164,164,164,178,178,178,178,178,190,196,196,196,196,192,192,192,192,192,192,198,198,198,198,196,192,190,198,200,200,200,200,196,198,198,196,196,196,192,196,198,194,194,32,20,20,20,20,28,28,36,36,36,36,34,30,24,24,10,10,10,10,30,30,38,38,38,38,36,28,26,26,26,44,44,44,44,44,70,68,68,68,64,64,58,68,68,68,68,78,88,82,82,82,94,94,104,100,104,104,104,104,104,104,82,116,116,116,132,132,132,132,146,146,146,146,164,166,166,12,12,12,34,8,30,38,38,38,66,68,68,68,36,84,84,84,102,102,102,96,82,96,106,108,108,108,106,106,98,98,98,98,80,122,138,140,140,118,118,118,142,154,156,156,170,154,154,178,178,178,152,10,10,8,28,8,28,28,28,28,8,6,8,26,26,50,50,50,72,72,72,72,48,88,88,88,106,106,106,106,114,114,114,140,140,140,162,162,162,162,142,138,136,12,52,52,52,52,12,12,12,58,98,98,98,98,58,58,58,102,140,140,140,140,102,102,102,140,170,144,144,144,212,212,212,212,162,162,162,148,148,160,160,160,148,148,160,160,160,148,148,140,172,206,206,196,196,188,188,184,184,184,190,196,192,192,192,192,212,212,212,212,204,150,156,156,156,156,148,148,148,160,166,166,166,166,160,160,160,172,172,172,184,184,184,184,172,190,198,198,198,198,188,188,188,200,208,208,208,208,200,200,200,156,160,162,164,164,168,168,168,168,170,170,172,184,186,186,190,190,208,208,208,208,210,210,210,210,214,214,214,214,208,208,208,208,210,210,210,210,212,212,212,212,204,204,204,204,202,202,202,202,192,192,192,192,184,184,170,170,164,164,164,164,160,160,148,148,138,138,66,66,58,58,52,52,44,44,40,40,32,32,30,30,26,26,24,24,18,18,18,18,26,26,26,26,16,16,16,16,24,24,24,24,28,28,40,40,50,50,62,62,70,70,76,76,84,84,92,92,100,100,104,104,110,110,124,124,124,124,130,130,134,134,138,138,146,146,152,152,164,164,170,170,170,170,174,174,184,184,186,186,188,188,190,190,200,200,200,200,200,200,196,196,196,196,194,194,194,194,192,192,192,192,184,184,180,180,176,176,170,170,164,164,164,164,158,158,146,146,142,142,142,142,138,138,134,196,196,196,188,188,188,188,190,190,188,188,188,188,180,180,180,180,176,176,164,164,158,158,158,158,154,154,150,150,150,160,156,156,154,154,152,152,150,150,150,150,154,154,154,154,148,148,136,136,132,132,132,132,128,128,120,120,116,116,116,116,110,110,108,108,106,106,106,132,122,122,118,118,106,106,104,104,104,104,94,94,90,90,90,90,84,84,80,80,80,80,76,76,70,70,70,70,64,64,62,62,62,62,58,58,52,52,52,52,42,42,38,38,38,38,36,36,32,96,96,96,90,90,88,88,88,88,82,82,78,78,78,78,72,72,54,50,62,94,106,136,148,14,24,24,24,24,12,12,12,44,52,52,52,52,40,40,40,62,72,72,72,72,62,62,62,92,98,98,98,98,90,90,90,106,114,114,114,114,106,106,106,134,140,140,140,140,134,134,134,134,134,108,108,92,92,62,62,48,48,16,16,12,52,60,98,104,136,136,102,98,58,52,10,38,38,46,46,94,94,84,84,130,130,120,120,8,12,24,40,52,148,156,160,166,168,170,212,212,4,52,52,52,56,56,88,88,50,56,56,56,60,60,60,60,56,56,56,56,60,66,66,66,70,70,70,70,66,66,66,66,70,74,74,74,58,58,58,58,58,70,70,70,50,50,50,50,38,38,38,38,32,32,32,32,58,58,58,58,62,62,62,62,88,88,88,88,106,106,106,106,100,100,100,100,70,70,70,70,50,54,54,54,48,48,48,48,56,56,56,56,50,104,104,98,98,98,104,100,100,38,38,34,34,28,36,36,36,36,22,22,22,22,34,34,34,34,28,64,64,64,74,74,74,74,80,80,80,80,84,84,88,88,88,82,82,86,86,68,68,72,72,76,56,56,56,56,76,76,76,84,84,84,100,100,100,100,86,112,110,110,98,46,56,76,76,76,118,118,118,118,122,122,122,122,30,30,30,30,36,36,36,36,50,42,42,42,52,52,52,96,96,96,106,106,106,134,134,134,116,116,116,116,134,134,134,134,132,132,112,112,108,108,108,142,142,142,144,144,160,148,148,148,162,162,162,162,162,162,174,174,174,174,160,182,182,172,190,196,212,194,194,194,202,202,202,192,192,192,208,188,202,136,158,158,158,158,140,140,140,166,166,166,170,170,178,178,180,178,164,172,172,172,182,128,128,128,138,138,162,162,162,162,144,144,128,128,128,168,168,168,168,190,186,186,176,176,174,174,174,174,186,186,186,176,176,176,168,168,196,196,198,176,188,188,196,202,202,202,202,194,182,174,164,154,144,136,126,28,62,62,62,62,18,18,18,18,66,122,160,160,160,160,104,104,104,104,148,122,104,104,104,62,62,104,152,154,162,148,162,28,16,20,64,30,30,30,46,46,46,44,44,122,122,122,140,140,140,140,122,74,74,74,80,80,80,80,74,74,74,86,92,92,92,92,86,86,86,106,150,150,150,150,108,108,108,54,22,22,22,22,62,62,62,62,50,58,58,58,70,70,92,92,92,92,96,92,98,98,100,100,102,102,108,108,108,108,56,56,56,56,60,60,106,106,106,106,110,110,112,112,112,106,112,112,112,112,112,112,106,108,134,134,136,136,136,108,110,112,116,116,124,124,128,128,138,138,144,146,146,146,136,148,144,148,158,112,128,128,128,128,148,148,148,148,132,72,92,58,104,106,112,106,56,16,16,16,30,30,30,28,20,18,20,36,36,36,44,46,44,38,36,36,40,40,46,36,40,52,52,52,64,64,64,62,54,68,68,68,68,68,76,76,82,82,82,82,72,72,78,78,80,80,80,80,70,92,92,92,96,96,116,110,110,130,122,122,116,116,116,116,122,122,128,128,138,138,138,138,134,134,118,144,144,144,156,156,156,172,166,166,158,158,158,158,168,168,170,170,172,172,172,172,168,168,166,176,176,176,172,172,160,160,154,154,154,154,162,162,172,172,184,184,184,184,180,180,172,158,156,156,144,162,162,172,172,172,184,184,192,192,198,186,200,184,184,184,194,172,172,172,178,164,154,154,144,154,136,156,148,148,140,58,58,58,66,66,66,66,58,86,78,78,78,78,90,90,90,90,86,90,90,70,70,70,74,74,74,74,70,46,46,46,84,84,84,84,44,50,50,50,54,54,54,56,56,56,60,60,60,62,62,62,66,66,66,68,68,68,74,70,70,74,74,78,78,78,82,82,82,24,24,24,22,22,22,22,26,26,30,30,38,38,38,38,32,32,26,52,52,52,48,48,44,44,44,44,46,46,50,50,52,52,52,52,52,52,50,50,46,56,62,62,70,70,70,80,86,86,90,90,90,90,90,100,108,108,110,110,110,110,100,100,100,100,106,106,112,132,126,126,124,124,124,124,128,128,132,136,136,136,140,140,142,142,144,144,144,152,148,148,148,148,152,148,152,156,156,156,158,158,164,164,164,164,164,170,170,170,174,174,176,176,176,176,176,182,182,186,186,186,190,186,180,194,200,200,192,192,200,126,126,126,120,126,120,120,122,122,120,122,126,126,122,158,158,164,164,164,172,176,176,176,186,186,186,184,178,192,192,192,200,200,200,210,204,204,204,204,210,204,208,158,158,158,160,160,158,158,156,156,154,154,158,156,154,22,62,62,62,62,58,58,24,24,14,14,14,30,8,22,10,146,190,190,190,190,146,146,146,146,146,146,146,164,164,164,176,176,180,180,180,180,178,178,178,178,178,178,174,168,160,168,174,46,46,46,24,22,22,22,48,48,48,48,44,54,54,54,54,54,54,54,26,26,26,26,22,22,20,22,50,54,50,154,162,162,162,162,186,186,190,190,190,190,162,152,154,158,164,164,160,160,160,160,164,160,158,158,152,82,82,82,90,90,90,90,80,80,80,110,110,110,124,124,124,124,112,112,110,110,110,110,116,116,110,108,108,112,106,112,116,146,126,122,122,122,116,106,104,104,104,104,108,154,154,154,152,152,88,88,80,80,80,154,78,100,100,100,110,110,110,128,128,128,140,140,146,146,146,154,148,148,138,138,134,134,124,124,120,120,110,110,106,106,96,96,92,92,82,82,80,80,70,18,18,18,30,30,66,66,72,72,72,72,56,56,16,16,16,16,20,64,64,64,48,48,70,64,60,64,104,104,104,104,88,88,88,88,68,88,90,86,86,86,86,104,104,104,130,130,130,130,134,134,140,140,142,142,156,156,174,174,174,174,172,172,140,140,178,178,178,178,196,196,196,196,122,122,122,122,104,174,200,200,200,200,180,200,218,218,218,218,196,154,154,154,172,20,68,68,68,68,36,36,36,36,18,18,18,30,62,46,46,46,100,100,100,100,76,76,76,76,70,70,70,70,64,76,70,70,70,70,140,140,140,140,114,114,114,114,100,114,114,114,188,188,188,188,140,130,178,178,178,178,178,62,62,62,76,76,80,80,90,90,114,80,80,80,98,118,118,118,124,124,124,124,144,144,152,152,156,156,156,156,174,174,174,184,212,212,212,212,182,182,182,182,210,12,40,40,40,40,36,40,40,40,36,40,48,48,54,54,60,54,54,54,50,50,36,34,88,88,88,88,86,86,68,68,66,66,66,66,70,70,108,108,112,112,112,112,48,48,48,48,56,56,128,128,128,128,124,124,124,124,132,132,132,132,168,168,168,168,70,70,70,70,92,92,96,96,96,96,74,74,74,74,126,126,126,126,130,130,176,176,180,180,180,180,182,182,192,192,194,194,194,194,190,190,14,14,14,14,108,108,108,108,106,106,36,36,34,34,34,34,38,38,48,48,68,68,70,70,70,70,70,70,80,80,84,84,116,116,126,126,140,140,146,146,150,150,194,194,198,198,198,198,148,148,148,148,154,154,158,158,178,178,182,182,182,182,180,180,148,148,146,146,146,146,178,178,180,180,180,180,182,182,188,188,194,194,194,194,196,196,214,214,218,218,218,218,210,210,118,118,114,114,114,114,112,112,10,10,4,4,4,4,24,24,30,30,30,30,14,14,14,14,34,42,42,48,48,30,30,30,36,36,48,48,60,60,60,10,10,20,20,8,8,8,10,10,14,26,26,38,38,38,56,56,56,56,36,42,42,52,52,48,48,42,42,42,56,54,54,42,42,42,50,50,50,50,42,42,54,54,54,54,50,56,56,56,58,58,64,64,64,64,68,68,68,72,72,72,88,70,76,76,90,90,90,112,114,108,108,108,94,110,108,112,112,112,114,114,114,98,102,102,112,112,114,122,130,130,130,130,140,140,140,140,152,152,152,152,144,144,144,144,134,134,134,134,122,122,122,162,162,162,162,162,162,162,162,162,174,174,174,174,160,172,172,12,24,24,24,24,34,34,34,34,50,50,50,50,62,62,62,62,78,78,78,78,12,12,12,22,22,22,22,22,62,60,60,50,50,50,58,58,58,58,50,54,54,54,60,60,66,66,66,66,58,46,56,56,56,56,46,46,46,46,46,46,44,44,48,48,48,48,50,52,52,52,60,120,120,120,138,138,114,114,114,114,132,132,132,132,128,128,122,122,122,122,114,114,114,114,120,110,104,104,100,100,100,100,116,116,118,118,140,140,140,140,134,134,126,126,118,118,114,114,110,110,110,110,116,116,144,144,146,146,146,146,138,138,138,138,148,148,152,152,154,154,154,154,150,154,160,160,162,162,162,162,156,156,156,156,154,154,154,154,168,168,168,168,164,164,164,164,172,172,174,174,174,174,180,180,182,182,182,182,174,174,174,174,180,180,184,184,184,184,178,178,178,178,178,178,144,144,144,144,150,144,140,140,140,140,130,130,130,130,126,126,114,114,110,110,106,106,106,106,114,114,114,114,110,110,110,110,116,116,116,116,114,114,106,106,104,104,96,96,96,96,90,90,86,86,86,86,90,90,92,92,92,92,88,92,98,98,98,98,96,96,90,90,88,88,88,88,80,80,78,78,78,78,86,86,86,86,84,84,78,78,76,76,76,76,78,78,82,82,82,82,78,78,76,76,68,68,68,68,74,74,82,82,86,86,86,86,92,92,96,96,96,96,90,90,90,90,96,96,96,96,102,102,102,102,114,114,114,114,106,106,106,106,98,98,98,98,90,98,94,94,90,90,90,90,104,110,112,112,112,112,118,118,118,114,124,124,124,124,122,122,118,118,118,118,118,118,116,116,112,112,112,112,102,102,114,114,120,120,130,130,138,138,138,138,134,134,134,134,144,144,144,144,140,140,140,140,142,142,148,148,148,148,146,146,146,146,144,146,148,148,148,148,138,138,138,138,144,144,144,144,132,132,132,132,130,130,128,128,126,126,126,126,130,130,130,130,132,144,150,150,150,150,148,148,148,148,154,154,154,154,150,150,150,150,156,156,156,156,164,164,164,164,158,162,164,164,164,164,168,168,172,172,174,174,174,174,180,180,180,180,174,154,150,150,148,148,148,148,158,158,158,158,154,154,154,154,148,154,158,164,174,174,174,174,162,162,162,162,168,168,168,168,164,164,164,174,174,174,162,162,162,154,154,154,146,146,146,146,162,162,162,162,158,158,158,158,160,158,152,140,140,140,174,174,174,28,28,28,40,40,40,40,28,46,46,46,46,46,58,58,44,66,66,66,80,80,80,80,66,90,90,90,90,90,102,98,98,98,92,98,104,104,104,104,90,90,90,90,102,102,102,102,90,90,90,104,100,104,110,110,110,110,100,100,100,108,108,110,114,114,114,114,102,106,106,106,106,106,84,84,84,84,100,150,150,150,164,150,148,148,148,148,158,164,164,164,174,174,174,174,174,190,182,182,182,182,192,200,200,200,210,204,204,204,196,200,200,200,200,148,148,148,154,154,154,154,162,162,162,162,170,170,170,170,162,184,174,176,182,182,182,182,170,194,194,194,194,172,186,186,186,186,170,170,186,210,200,200,200,200,190,202,192,192,192,192,192,192,208,208,208,192,196,196,202,202,202,202,210,210,210,210,196,192,190,190,184,184,184,184,194,194,202,202,214,214,214,214,210,210,204,188,192,192,196,196,196,196,210,196,184,184,184,184,186,186,194,194,194,194,208,208,204,204,186,186,186,84,84,84,96,96,96,118,106,106,106,106,116,116,116,116,106,124,124,124,138,138,138,22,22,22,22,22,38,38,38,38,20,20,20,20,26,26,36,36,38,20,20,20,30,30,30,30,30,22,22,22,22,22,30,22,30,8,8,8,8,8,16,8,6,6,6,6,18,24,24,24,50,50,50,50,22,22,22,22,36,32,32,32,38,38,38,38,32,32,32,40,52,38,38,38,48,48,48,48,62,62,62,62,70,70,70,70,78,78,80,80,82,82,82,82,84,84,148,148,164,164,194,194,212,212,204,204,196,196,170,170,152,152,78,78,78,78,74,74,40,40,40,40,34,36,62,56,56,56,58,58,60,60,60,106,104,104,100,100,100,100,128,128,170,170,170,170,146,146,146,146,168,168,168,168,140,140,140,140,152,152,152,152,138,138,138,138,128,128,128,128,120,120,118,118,118,118,114,104,104,92,92,92,82,148,148,148,148,148,134,134,134,138,138,82,82,86,86,94,94,94,92,150,150,150,146,52,52,52,58,58,58,58,52,52,52,56,56,62,68,82,82,82,148,100,100,100,98,98,92,92,92,92,112,112,166,166,166,46,52,86,86,86,86,86,92,92,92,136,136,138,140,140,140,150,150,150,156,166,166,166,172,172,172,182,182,182,188,188,188,100,96,96,96,92,88,88,88,1,2,2,2,6,6,2,26,16,16,32,20,20,20,20,30,30,30,36,36,32,42,42,42,42,34,42,42,42,32,46,46,46,56,56,44,44,60,60,60,56,78,78,78,66,48,86,86,86,96,96,96,96,84,96,98,98,88,88,88,88,84,100,100,100,110,102,114,102,100,100,100,100,116,116,124,124,124,124,132,132,132,106,106,2,20,20,20,20,38,58,100,100,128,128,128,128,124,124,120,120,118,118,116,116,116,116,72,72,72,72,74,74,96,96,96,96,92,92,74,74,64,64,52,52,48,48,48,72,72,72,82,82,82,92,94,110,170,170,170,170,30,30,30,30,22,22,22,22,32,32,32,32,74,94,110,122,122,122,118,118,30,30,46,80,80,80,82,82,94,94,102,102,108,108,108,108,122,122,122,122,108,108,108,108,120,120,120,120,112,112,148,148,148,148,112,112,112,112,148,148,148,148,112,112,112,112,146,146,146,188,188,188,188,122,176,176,176,176,98,96,96,96,122,114,114,178,178,180,180,178,178,178,180,180,182,182,180,176,170,170,170,170,174,174,174,174,174,16,16,16,20,20,22,38,36,36,26,52,50,50,44,44,44,44,46,46,54,54,58,58,58,58,48,48,46,46,32,32,32,32,44,44,46,46,66,66,66,66,50,50,38,38,20,20,20,20,32,90,90,108,108,122,116,116,116,116,118,110,106,106,96,110,110,110,102,122,130,130,132,132,132,126,128,128,128,84,84,84,80,154,162,152,152,152,156,156,166,166,166,168,168,168,170,170,170,170,180,180,180,180,162,160,160,160,172,172,182,182,198,198,198,198,186,186,160,160,156,156,156,156,146,146,146,146,150,150,156,180,174,174,166,166,164,184,184,184,186,186,196,186,198,178,162,172,172,172,178,178,178,196,196,196,202,202,204,204,204,198,206,206,204,184,184,184,188,188,190,190,190,190,186,186,180,178,172,200,196,202,210,200,200,176,176,178,178,178,178,178,154,154,128,128,128,128,136,136,136,178,174,174,166,208,194,176,176,176,170,178,186,186,186,186,166,166,166,166,174,166,166,166,178,86,42,88,88,88,26,136,136,136,196,196,134,128,128,102,102,68,66,92,68,66,36,82,82,82,42,42,42,68,68,68,82,52,52,52,72,72,72,86,38,58,58,58,70,70,70,142,186,182,196,182,138,148,148,148,186,186,186,168,168,168,148,164,164,180,180,176,176,176,164,104,124,122,106,122,128,128,128,98,98,98,106,106,106,120,120,120,132,132,132,110,110,120,120,98,96,86,84,82,84,136,138,86,142,98,124,110,128,110,88,128,140,124,124,124,128,128,126,128,138,138,138,138,142,138,130,130,116,98,94,94,88,88,88,88,88,88,100,100,100,100,110,110,110,110,112,112,112,112,128,128,128,128,146,146,150,150,170,170,172,172,200,200,202,202,208,208,210,210,218,218,218,100,90,90,88,88,84,84,84,84,70,70,68,68,56,56,46,46,18,18,16,16,12,12,10,10,2,2,2,136,136,136,138,138,138,138,144,144,148,148,150,150,150,150,140,140,128,98,104,104,104,104,98,98,98,112,122,122,122,98,102,90,96,96,96,108,116,116,124,124,120,120,114,114,112,160,160,160,160,164,158,164,164,66,66,66,72,72,72,44,44,42,52,52,54,44,52,44,44,44,46,46,46,46,94,94,108,108,164,160,160,98,98,98,90,90,86,86,86,86,90,54,88,88,88,88,54,54,54,60,80,68,68,116,144,144,144,144,110,110,110,110,116,118,136,126,126,118,76,82,82,88,88,94,94,100,100,106,106,112,112,78,78,78,96,118,118,118,108,108,108,108,106,106,94,76,122,94,48,106,150,42,42,42,42,42,58,58,58,58,68,68,68,68,76,76,76,76,84,84,92,92,94,94,94,94,104,104,104,104,116,116,116,116,122,122,122,122,132,132,132,132,136,136,136,136,146,146,146,146,160,160,160,12,12,32,26,26,26,26,12,12,22,22,30,12,12,12,12,12,26,12,12,12,28,20,20,20,10,20,32,30,30,30,36,42,42,42,26,42,46,52,52,52,62,52,60,54,64,200,180,180,180,180,192,192,192,192,180,184,184,184,188,188,194,194,176,176,176,176,192,192,192,192,180,182,182,182,172,182,192,170,170,170,182,172,182,170,182,192,192,192,192,192,202,202,202,202,212,212,212,212,212,192,186,186,186,194,194,194,208,208,208,208,214,214,214,208,208,124,124,72,72,102,102,92,92,86,86,194,194,194,160,194,208,208,208,208,148,148,148,148,162,176,176,170,164,164,162,162,162,162,166,170,184,184,184,184,180,180,176,176,166,158,158,158,192,192,192,192,184,160,154,158,158,122,112,112,106,106,106,106,110,110,116,116,128,128,128,128,122,122,118,102,88,88,88,88,86,86,78,78,78,78,68,110,102,102,98,98,98,98,96,96,92,92,86,86,86,86,78,78,78,78,72,72,72,72,104,104,116,116,118,118,104,104,98,98,94,94,94,94,72,72,64,64,32,32,32,32,38,38,38,38,34,34,22,22,10,10,10,10,14,14,22,22,42,42,42,42,32,32,32,32,8,8,8,8,16,16,12,12,12,12,16,16,16,16,10,10,10,10,14,14,22,22,38,38,42,42,48,48,48,48,38,38,38,38,46,46,58,58,66,66,66,66,56,56,56,56,62,62,70,70,74,74,74,74,82,44,44,44,52,52,58,64,64,46,42,58,66,66,68,68,56,56,56,56,54,54,46,46,46,42,34,34,34,34,68,68,72,72,72,64,64,64,76,32,30,30,22,44,46,46,56,56,60,60,70,72,32,48,48,48,44,48,54,50,44,50,54,58,64,64,64,64,56,56,56,56,50,54,64,120,130,130,130,130,116,116,114,114,102,102,102,102,112,112,114,114,128,128,128,128,124,124,110,110,114,114,120,124,124,118,114,114,110,110,110,110,116,116,118,118,120,120,120,120,114,114,112,112,108,108,108,108,110,110,112,112,114,114,114,114,110,110,108,108,104,104,104,104,106,106,108,108,114,114,114,114,116,116,118,118,122,122,122,122,118,118,110,110,106,164,164,168,168,168,168,172,172,176,176,182,182,178,178,178,178,182,162,164,180,176,162,162,188,172,172,218,192,174,116,114,76,30,2,32,32,32,2,18,6,136,136,140,140,140,140,136,136,136,136,136,110,110,108,108,106,106,106,106,130,130,148,148,168,168,168,168,164,164,158,158,152,152,152,152,142,142,142,142,152,152,152,152,148,148,142,142,142,142,138,134,134,134,122,122,120,120,118,118,118,118,140,92,92,92,88,88,72,72,68,68,68,68,72,72,88,88,92,100,104,104,114,114,120,120,124,124,124,114,110,110,104,104,98,98,98,98,100,164,166,166,178,178,184,184,192,192,192,192,188,188,182,182,172,172,168,168,164,196,206,206,216,216,218,218,218,218,216,216,208,194,194,194,196,196,202,142,134,134,134,134,138,138,144,144,148,148,148,148,142,142,138,138,136,110,104,104,98,98,98,98,104,104,108,108,116,116,116,116,112,112,110,152,152,152,138,138,122,122,116,116,116,116,114,154,154,154,138,138,120,120,114,114,114,114,106,142,142,142,144,144,144,144,142,142,142,142,140,106,110,108,108,106,106,106,104,104,106,106,106,106,106,106,102,102,102,102,104,104,106,106,108,108,110,110,110,110,110,110,106,106,104,106,106,72,72,86,86,86,72,96,96,96,110,110,110,110,92,92,92,96,96,96,96,120,120,120,120,120,124,124,124,128,128,128,128,132,134,132,128,134,136,136,136,140,140,140,140,140,156,156,156,156,140,140,140,64,64,64,78,86,86,86,86,94,94,94,100,100,100,106,106,106,106,106,112,112,112,116,116,116,130,130,130,130,130,130,126,126,112,112,112,112,116,14,6,6,2,2,2,4,6,6,6,6,2,2,2,2,2,2,34,26,26,26,24,24,22,22,20,20,18,18,16,16,16,16,20,20,50,44,44,44,42,42,42,42,40,40,38,38,36,36,38,38,38,38,28,28,28,28,26,26,24,24,22,22,20,20,18,18,16,16,8,8,8,8,14,14,22,22,32,32,32,36,36,36,38,38,28,28,28,28,32,32,60,60,60,60,54,54,70,70,74,70,70,70,72,72,60,60,58,58,52,52,44,44,44,44,40,38,32,32,30,30,30,30,16,6,2,2,2,2,54,34,30,34,40,40,44,44,52,52,54,56,58,56,54,48,48,78,78,78,68,68,68,68,78,78,78,78,76,74,42,40,38,38,34,34,34,30,28,28,28,28,30,30,34,34,34,34,44,98,138,98,72,72,72,72,142,140,140,140,136,140,140,140,162,162,164,164,164,164,136,136,106,106,70,70,70,70,76,76,78,166,116,116,116,122,122,122,122,82,82,82,82,114,106,114,124,124,124,124,108,108,108,122,118,122,124,124,118,118,118,118,124,124,118,118,118,118,142,142,142,142,88,88,88,88,108,116,122,122,122,122,128,128,128,128,120,120,120,120,116,116,116,116,120,116,114,114,114,114,106,106,106,106,114,114,114,94,94,94,102,102,102,102,92,96,96,100,104,100,98,98,98,98,102,92,92,92,104,104,120,120,128,128,128,90,96,96,96,96,92,92,92,88,88,88,76,76,76,76,88,88,88,76,76,76,72,72,72,72,52,52,52,52,78,78,78,78,72,72,70,24,24,24,38,38,38,54,54,58,76,60,78,78,56,98,98,98,106,106,114,96,96,106,106,126,126,118,138,150,150,150,170,172,172,172,148,188,188,188,202,202,202,202,186,192,200,200,200,78,78,78,82,82,92,92,102,102,102,102,104,104,116,116,116,116,108,136,132,132,116,134,136,136,152,146,120,166,186,36,36,36,52,52,54,78,78,78,98,94,86,86,78,74,112,140,140,140,148,148,152,152,154,154,154,154,162,162,164,164,164,164,168,168,170,170,170,38,74,188,138,88,74,114,118,118,178,112,112,112,62,62,60,60,48,38,18,18,18,18,38,38,42,42,50,50,52,186,186,186,206,206,202,202,198,198,198,198,190,190,184,184,184,184,180,180,174,138,132,132,122,122,122,122,114,114,112,112,94,94,94,94,98,98,98,98,88,86,86,34,34,34,22,22,22,22,28,28,28,28,18,18,18,18,22,22,22,22,32,32,32,32,22,22,22,22,32,32,34,34,46,46,54,54,58,58,60,60,64,64,66,66,72,72,74,74,80,80,94,94,108,108,110,110,120,120,122,122,130,130,132,132,142,142,168,168,176,176,176,176,168,168,168,168,176,176,176,176,166,166,166,166,172,172,172,172,164,164,160,18,38,38,52,52,54,54,56,56,56,56,48,48,48,48,52,52,54,54,64,64,64,64,66,66,78,78,82,82,82,82,84,84,90,90,94,94,96,96,100,100,100,100,108,108,108,108,116,116,118,118,124,124,124,124,120,120,120,120,128,128,132,132,148,148,148,148,152,152,160,160,162,162,172,172,176,176,176,176,178,178,180,180,196,196,198,198,202,202,202,202,190,218,190,18,18,18,18,18,38,78,80,80,92,92,92,92,74,74,74,74,78,78,80,80,102,102,102,102,90,90,90,90,106,106,108,108,182,182,182,182,170,170,164,164,136,136,110,110,82,82,38,38,22,22,44,44,56,56,58,58,62,62,62,62,52,52,52,52,56,216,216,216,208,208,204,204,202,202,202,202,210,210,210,210,204,204,190,190,188,188,188,188,200,200,200,200,186,186,190,190,196,196,200,200,200,200,194,194,194,194,200,200,202,202,202,202,208,208,208,208,200,200,200,88,66,66,60,60,20,20,12,12,12,12,20,20,20,20,16,16,12,12,2,2,2,2,24,24,24,24,20,20,10,10,4,4,4,4,14,14,14,14,28,28,28,28,38,198,202,202,202,202,194,194,192,192,184,184,184,184,192,192,192,192,190,190,184,184,182,182,182,182,174,174,172,172,172,172,182,182,178,178,170,170,168,168,162,162,156,156,156,156,158,158,160,34,34,34,50,50,50,50,38,38,34,34,30,30,30,30,38,38,38,38,36,36,32,42,42,42,44,44,54,54,46,46,42,42,44,44,54,56,56,56,70,70,70,70,64,64,54,54,54,54,46,46,46,46,52,52,56,56,58,66,66,66,68,68,78,78,78,78,68,68,62,62,62,62,80,12,202,202,202,202,188,188,186,186,186,186,30,26,22,30,26,22,10,10,10,10,20,10,10,10,2,2,2,48,44,44,24,24,170,170,170,170,190,190,68,68,56,56,54,54,54,54,68,54,44,44,44,76,76,76,76,76,72,72,68,68,66,66,66,66,66,66,96,96,102,102,102,102,100,100,94,94,94,94,96,96,96,122,122,122,120,120,118,118,118,118,114,114,114,114,118,118,120,120,134,134,134,134,128,146,152,152,152,152,156,156,156,156,154,154,148,148,142,142,142,142,144,166,166,166,170,170,182,190,190,190,198,198,198,198,180,180,176,176,166,166,166,180,172,172,154,154,154,154,192,192,204,204,180,116,136,136,136,136,112,112,112,112,98,98,98,46,46,46,60,60,74,74,74,74,74,74,78,78,110,110,110,76,76,76,88,88,88,88,104,84,84,84,86,86,98,98,100,100,100,100,90,90,82,82,82,82,82,82,74,82,82,82,94,94,94,94,80,90,90,106,106,106,118,118,118,124,124,124,124,124,130,130,130,130,136,136,136,144,144,148,148,148,148,148,162,162,162,162,146,160,160,170,170,176,176,176,188,176,170,182,182,182,174,174,174,174,176,176,188,188,194,194,194,194,192,198,198,198,210,210,196,196,216,218,216,136,136,136,166,166,166,166,162,162,132,132,142,142,154,154,158,158,170,170,178,178,186,186,186,186,184,184,170,170,146,146,136,136,132,132,132,132,134,134,136,136,134,134,132,132,134,132,130,130,132,132,134,132,132,132,130,130,130,130,132,130,128,128,126,126,128,128,126,126,124,124,126,126,124,124,122,122,124,124,122,122,120,120,122,122,120,120,120,122,122,22,22,22,44,44,56,56,74,74,78,78,100,100,102,102,124,124,130,130,150,150,158,158,178,178,182,182,182,182,172,172,166,166,152,152,152,152,168,168,174,174,186,186,186,186,180,180,146,146,138,138,126,126,122,122,122,122,134,134,140,140,152,152,138,138,110,110,104,104,104,104,122,122,124,124,144,144,144,144,140,140,124,114,112,112,94,94,76,76,70,70,70,70,78,78,84,84,98,98,98,98,94,94,40,40,26,26,26,26,38,38,42,42,58,58,60,60,62,62,62,46,42,42,42,42,52,52,58,58,70,70,74,74,90,90,94,94,106,106,108,108,116,116,116,116,108,108,116,116,120,120,124,128,128,128,130,140,144,22,23,22,32,32,32,32,40,40,40,30,46,50,50,50,56,72,60,60,60,60,68,68,68,68,74,64,64,64,72,72,72,72,54,82,92,92,92,92,82,82,82,102,110,110,110,110,102,102,102,102,110,128,130,128,120,120,120,120,128,128,128,128,118,134,134,134,134,36,36,36,64,64,64,64,94,94,110,110,134,134,170,170,170,170,134,134,132,132,132,132,96,96,96,96,148,148,174,174,190,190,190,190,80,80,78,78,78,78,72,72,24,24,12,12,12,12,26,26,26,26,34,34,28,28,52,52,52,52,82,82,82,82,94,94,122,122,126,126,126,126,158,158,184,184,184,184,174,174,168,168,150,150,134,134,126,20,21,20,20,20,54,54,54,54,90,90,90,90,114,114,84,84,122,122,122,122,138,138,138,134,150,150,178,166,166,176,176,176,188,188,188,188,176,194,194,194,204,204,204,204,192,194,218,218,218,218,26,26,26,26,48,48,48,48,16,34,34,34,78,78,196,196,218,20,134,134,134,134,118,118,118,126,128,128,218,218,218,92,148,148,182,182,218,218,218,142,142,142,210,182,182,144,144,144,144,144,144,114,114,114,112,112,72,72,68,68,68,68,112,112,112,112,104,122,122,78,78,78,92,92,92,92,92,92,100,100,100,100,108,108,108,110,128,128,134,134,134,134,126,126,108,108,90,90,90,94,110,110,116,116,116,116,106,106,106,106,98,98,62,62,44,44,44,44,54,54,68,68,74,74,74,74,80,80,82,124,140,140,144,144,144,144,136,136,108,106,96,96,94,94,94,94,102,102,134,134,136,136,136,136,158,158,164,164,164,164,160,160,116,116,160,160,164,164,164,164,158,158,132,132,132,132,126,126,84,84,76,76,76,76,80,80,90,90,96,96,96,10,10,10,16,16,18,18,20,20,20,20,14,14,14,14,18,18,22,22,28,28,28,34,34,34,58,58,62,64,84,84,102,102,102,102,28,52,52,52,38,38,38,38,26,24,34,34,36,36,36,36,22,22,22,22,42,42,36,50,50,50,40,40,40,40,54,54,54,54,40,40,40,50,40,40,40,40,54,54,64,64,64,64,54,54,66,66,66,66,56,56,68,68,64,64,50,50,50,50,60,60,60,60,48,48,48,48,62,62,74,74,74,74,86,86,86,86,70,70,70,70,78,78,78,78,74,74,86,86,86,86,90,90,98,98,100,100,100,100,98,98,86,84,84,84,88,88,90,90,90,98,32,34,94,14,44,98,120,36,42,42,34,28,42,42,32,116,116,116,126,126,130,130,142,142,142,142,110,116,116,126,126,126,114,114,110,110,110,110,118,118,118,118,124,124,124,124,132,132,132,132,140,140,140,140,146,144,138,146,146,146,134,134,134,142,142,146,146,124,124,130,130,130,126,112,112,116,116,128,128,178,178,178,182,182,184,184,184,184,184,184,194,194,194,194,174,174,174,174,174,174,190,190,192,192,192,120,120,138,138,136,136,152,136,48,49,48,48,48,18,18,18,18,56,56,56,66,66,66,78,78,78,88,88,88,108,108,108,108,108,108,86,88,108,108,108,114,114,114,122,122,122,122,132,132,132,136,136,136,144,144,144,152,152,152,152,162,162,162,156,156,170,176,198,198,174,174,174,174,196,196,196,192,176,20,20,2,10,10,20,20,24,24,36,36,36,36,40,40,66,66,66,66,76,76,76,76,100,100,100,100,114,114,120,120,120,120,124,124,124,124,132,132,138,138,138,138,152,152,152,152,166,166,166,166,174,174,184,184,184,184,218,218,218,218,210,210,210,210,218,204,204,204,196,196,196,196,208,188,188,188,192,192,192,192,186,186,192,192,192,192,186,186,186,194,194,194,206,206,206,206,194,210,210,210,216,216,216,216,208,202,202,196,210,196,210,164,140,140,140,140,140,140,140,140,140,146,146,146,146,146,146,146,146,150,150,150,150,150,150,150,150,156,156,156,156,156,156,156,156,162,162,162,162,162,162,162,162,102,114,114,114,114,102,102,102,102,102,102,120,120,120,120,102,102,102,102,120,120,120,120,102,102,106,110,116,120,126,96,90,84,78,78,82,86,90,94,100,100,96,92,86,80,74,110,114,120,124,40,40,40,44,44,44,44,40,48,48,48,52,52,52,52,48,56,56,56,60,60,60,60,56,64,68,68,64,64,64,58,58,58,54,54,54,54,58,50,46,46,46,46,50,50,50,44,44,44,40,40,40,40,44,44,40,40,40,40,44,44,44,48,48,48,52,52,52,52,48,56,60,60,60,60,56,56,56,64,64,64,68,68,68,68,62,56,60,60,60,60,56,56,56,66,66,66,70,70,70,70,66,70,70,44,44,44,48,48,48,46,46,46,50,50,50,50,56,42,42,42,50,50,50,50,42,42,42,42,44,40,40,40,46,46,46,46,40,60,56,56,56,56,52,52,52,52,48,48,48,56,56,56,54,54,50,50,50,56,56,4,8,8,8,8,4,4,4,12,12,12,18,18,18,18,10,22,22,22,28,28,28,28,20,28,22,18,16,16,10,8,2,36,40,44,52,56,62,34,40,40,40,40,34,34,34,46,46,46,52,52,52,52,44,60,60,60,66,66,66,66,60,8,14,14,14,14,20,20,20,20,26,26,26,28,34,34,34,34,42,42,42,42,50,50,50,50,58,58,58,58,64,64,64,64,70,152,152,152,146,146,146,146,152,152,158,158,160,160,160,160,156,156,150,150,156,152,152,104,104,104,108,108,108,62,62,88,88,88,88,66,66,66,66,112,112,150,150,138,138,138,130,130,130,150,150,160,160,160,152,174,204,200,172,92,92,138,138,60,60,60,76,76,76,76,90,90,124,124,130,130,130,130,144,144,144,60,68,68,68,68,74,74,74,74,82,82,82,82,88,88,88,88,90,90,96,96,96,96,102,102,102,102,110,110,110,110,116,116,116,116,120,120,120,120,124,124,124,124,130,130,130,130,134,134,134,134,144,72,92,92,92,92,76,72,72,82,86,86,86,86,82,82,82,112,112,112,138,112,140,130,130,130,134,134,134,134,130,84,100,122,134,134,134,134,122,98,84,84,84,84,98,124,134,88,88,92,92,96,96,130,130,124,124,92,88,84,78,74,68,112,116,120,124,128,132,80,80,80,88,88,80,128,128,128,138,138,128,88,88,174,174,174,176,176,182,182,182,182,188,188,192,196,196,196,198,198,204,204,204,204,210,210,212,172,172,176,176,180,168,168,168,182,182,182,180,168,170,170,176,176,176,176,180,180,184,184,184,174,174,174,174,174,184,184,184,184,186,186,192,192,192,192,182,172,172,172,168,168,168,168,172,172,172,174,174,174,178,178,178,178,174,132,76,108,108,108,104,104,104,76,136,110,116,118,126,130,136,92,86,80,74,70,62,18,18,22,22,22,14,14,14,14,24,24,24,24,22,20,20,20,24,24,24,14,14,14,18,18,18,26,22,26,36,36,36,36,22,16,4,4,4,4,18,20,20,20,30,30,30,32,32,32,20,20,14,14,8,8,8,8,8,8,16,14,6,6,6,76,72,72,70,70,70,70,72,72,72,72,66,66,66,66,70,70,74,74,74,74,68,68,66,66,62,62,60,60,54,54,54,54,62,62,62,62,66,66,72,72,74,74,78,78,78,78,74,74,74,74,80,80,82,82,86,86,86,86,88,88,90,90,100,100,102,102,108,108,110,110,114,114,116,116,122,122,122,122,126,126,128,128,130,130,130,130,134,134,136,136,138,138,138,138,134,134,134,134,142,142,144,144,148,148,148,148,146,146,142,142,142,142,150,150,150,150,146,146,138,138,138,138,132,132,132,132,134,82,82,82,84,84,88,88,90,90,94,94,98,98,98,98,96,96,100,100,106,106,106,106,108,108,114,114,114,114,116,116,122,122,122,122,126,126,126,86,120,120,120,78,78,78,80,80,82,82,86,86,88,88,88,88,90,90,94,94,94,94,96,100,102,102,106,106,108,108,110,110,114,116,118,118,122,122,122,122,128,128,128,128,130,130,132,132,132,132,136,136,136,136,130,130,128,128,128,128,126,126,120,120,120,120,118,118,114,114,112,112,110,110,108,108,108,108,106,106,102,102,98,98,98,98,96,96,92,92,88,88,88,88,90,90,90,90,88,88,84,84,80,80,78,78,78,94,94,94,106,106,106,106,92,108,108,108,120,120,120,120,90,106,106,106,114,68,68,68,64,64,62,62,60,60,60,60,62,62,62,62,54,54,54,54,64,64,64,64,66,66,70,70,76,76,80,80,80,80,82,82,86,86,90,90,100,100,102,102,106,106,106,106,102,102,104,104,106,106,110,110,110,110,108,108,108,108,112,112,116,116,116,116,118,118,126,126,132,132,132,132,134,134,140,140,144,144,144,144,142,142,138,138,138,138,148,148,148,148,142,142,142,142,146,146,146,146,140,66,2,2,218,216,146,218,218,218,144,66,66,2,2,2,2,2,14,14,16,48,48,48,58,20,20,20,30,30,30,22,24,24,40,40,42,18,18,42,42,42,54,54,54,42,38,42,58,62,62,62,76,76,76,76,58,68,68,68,84,80,80,80,90,110,110,110,112,112,126,126,126,126,138,138,138,142,142,142,152,152,152,152,138,154,154,154,164,164,164,164,154,160,160,160,170,170,170,176,212,180,180,180,216,146,174,174,174,174,214,214,178,178,178,178,216,216,178,178,178,178,214,214,180,180,180,180,218,218,176,176,176,176,216,216,216,216,176,184,204,170,144,144,172,172,172,144,170,170,170,170,150,150,172,172,172,172,152,156,168,168,168,168,162,166,174,52,34,34,34,34,64,60,38,38,38,38,52,52,40,40,40,40,60,56,38,38,38,38,56,62,36,36,36,36,64,56,38,38,38,38,46,36,36,36,2,8,30,30,30,30,2,2,30,30,30,30,6,6,28,28,28,28,8,4,30,30,30,30,6,6,30,30,30,30,6,6,18,22,28,36,42,46,52,56,60,64,76,84,132,144,152,156,166,174,186,194,198,204,218,202,202,202,216,216,216,216,202,184,168,168,168,168,186,186,186,154,138,138,138,138,154,154,154,126,116,116,116,116,130,130,130,130,126,112,112,112,94,94,94,94,112,112,112,86,76,76,76,76,86,86,86,70,70,70,56,56,56,56,70,50,50,50,32,32,32,32,52,24,24,24,12,12,12,12,28,18,50,34,36,36,60,46,66,64,64,64,74,148,150,150,194,172,172,172,146,138,138,138,156,134,122,108,108,92,92,86,86,86,76,18,18,18,42,42,42,42,14,18,34,34,34,50,50,50,50,58,58,58,74,70,70,70,78,78,94,94,94,94,72,72,70,70,70,98,98,98,112,98,114,98,106,122,122,122,136,136,136,136,118,130,130,130,140,140,140,82,82,82,96,96,96,96,100,100,104,104,104,110,110,110,124,124,124,124,108,130,130,130,136,136,136,136,148,148,148,148,148,24,24,24,42,42,42,42,58,58,36,40,54,66,66,66,86,86,86,96,124,108,108,134,118,118,118,118,122,122,136,128,114,140,140,140,166,166,166,12,12,12,22,22,22,22,8,26,26,26,38,38,38,52,42,42,42,42,52,52,52,52,40,66,66,66,70,70,78,78,84,84,98,98,98,62,64,64,80,80,84,84,96,102,102,102,116,116,116,134,122,122,122,122,138,130,118,138,138,138,146,146,146,146,152,152,152,152,152,152,162,162,162,168,168,168,174,174,174,174,180,180,180,180,180,186,186,186,186,192,206,198,198,204,218,218,204,204,204,204,212,212,218,212,198,184,184,184,194,194,198,198,210,210,210,210,182,182,176,176,154,154,154,154,158,158,158,158,168,168,170,170,186,166,166,166,170,170,170,170,164,164,164,194,194,194,200,200,200,200,196,196,196,176,176,176,178,178,186,186,192,192,192,166,170,194,202,178,178,178,186,186,186,186,176,182,182,182,192,192,192,192,184,184,184,184,206,206,216,216,216,216,210,210,206,190,198,198,204,160,166,166,166,166,172,172,172,194,194,202,202,202,206,206,210,210,214,210,214,214,218,214,218,210,210,210,218,202,202,202,208,198,198,188,190,190,196,170,164,162,160,160,154,154,146,150,142,154,144,154,154,154,146,162,162,168,168,174,174,182,182,188,188,188,180,16,16,22,22,62,62,10,10,84,84,120,110,18,18,2,2,2,2,2,2,2,2,2,2,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100,102,104,106,108,110,112,114,116,118,120,122,124,126,128,130,132,134,136,138,140,142,144,146,148,150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180,182,184,186,188,190,192,194,196,198,200,202,204,206,208,210,212,214,216,218,18,19,18,26,26,30,30,30,30,24,24,24,24,38,38,38,38,26,26,26,26,44,44,46,46,56,56,56,56,48,48,52,52,62,62,64,64,64,64,54,54,54,54,58,58,64,64,84,84,84,84,74,74,88,88,88,88,78,78,78,78,90,90,98,98,102,102,108,108,110,110,110,110,102,102,102,102,106,106,114,114,116,116,116,116,108,108,106,106,106,106,114,114,118,118,120,120,120,120,110,110,110,110,116,116,120,120,122,122,126,126,140,140,142,142,154,154,154,154,142,142,142,142,148,148,154,154,156,156,158,158,164,164,164,164,160,160,152,152,148,148,148,148,158,158,158,158,156,156,148,148,146,146,146,146,152,152,152,152,146,146,136,136,132,132,132,132,140,140,140,140,132,132,130,130,114,114,112,112,98,98,94,94,92,92,92,92,98,98,98,98,94,94,88,88,74,74,72,72,68,68,68,68,72,72,78,78,78,78,66,66,60,60,56,56,56,56,52,52,48,48,44,44,44,44,52,52,52,52,48,48,42,42,32,32,30,30,24,24,24,24,28,28,34,34,34,34,24,24,26,26,34,50,58,58,58,58,54,54,54,54,68,68,70,70,70,70,74,74,80,80,88,88,90,90,92,92,92,92,98,98,100,100,112,112,114,114,122,122,126,126,126,126,118,118,132,132,132,132,126,126,126,126,128,128,132,132,136,136,136,136,132,132,132,132,140,140,140,140,146,146,156,156,156,156,166,166,168,168,168,168,164,164,166,166,168,168,178,178,180,180,180,180,182,182,184,184,188,188,188,188,178,178,178,178,176,176,172,172,170,170,170,170,168,168,166,166,160,160,160,160,174,174,174,174,168,168,168,168,182,182,174,174,166,166,166,166,174,174,174,174,172,172,170,170,166,166,164,164,160,160,160,160,168,168,168,168,164,164,162,162,156,156,150,150,150,150,158,158,158,158,154,154,150,150,146,146,144,144,130,130,128,128,126,126,126,126,124,124,122,122,110,110,108,108,100,100,98,98,88,88,84,84,80,80,80,80,86,86,82,82,76,76,62,62,58,58,56,56,56,56,60,60,64,64,52,52,48,48,40,40,40,40,34,34,32,32,30,30,30,30,38,38,24,24,24,24,30,30,34,34,34,34,14,14,14,14,18,18,24,26,26,26,16,16,16,16,20,20,24,24,26,26,12,12,12,12,22,22,10,10,20,20,20,20,12,12,12,12,20,20,20,20,14,14,14,14,16,16,20,20,26,30,38,38,40,40,48,48,48,48,38,38,34,38,52,48,42,42,42,42,54,54,56,56,56,56,50,50,48,48,50,50,54,54,58,58,58,58,54,54,54,54,62,62,64,64,70,70,76,76,80,80,80,80,62,62,70,70,76,76,78,78,78,78,72,72,72,72,88,88,88,88,84,84,82,82,78,78,96,96,86,86,86,86,92,92,94,94,106,106,106,106,98,98,98,98,106,106,106,106,100,100,100,100,108,108,108,108,94,94,94,94,96,96,104,104,110,110,110,110,108,108,106,106,104,104,104,108,116,116,108,108,108,108,112,112,116,116,122,122,114,114,118,118,126,126,130,130,130,130,126,126,120,120,118,118,118,118,122,122,124,124,134,134,136,136,136,136,142,142,144,144,146,146,146,146,148,148,150,150,162,162,164,164,168,168,168,168,172,172,176,176,188,188,190,190,202,202,202,202,194,194,194,194,204,204,204,204,200,200,196,196,192,192,184,184,184,184,194,194,194,194,188,188,182,182,180,180,180,180,186,186,190,190,190,190,188,188,184,184,180,180,180,182,198,198,198,198,194,194,192,192,188,188,186,186,180,180,180,180,194,194,194,194,188,188,186,186,184,184,184,184,182,182,180,180,172,172,172,172,180,180,180,180,174,174,168,168,166,166,160,160,160,160,166,166,166,166,158,158,154,154,154,154,152,152,146,146,136,136,134,134,130,130,132,132,128,128,116,116,114,114,110,110,110,110,112,112,112,112,110,110,100,100,86,86,82,82,84,84,84,84,80,80,74,74,64,64,62,62,56,56,56,56,54,54,48,48,34,34,30,30,30,30,32,32,30,30,18,18,10,10,10,10,20,20,10,10,10,10,16,16,6,6,6,6,14,14,6,6,6,6,14,14,14,14,6,6,6,6,14,14,14,14,6,6,6,6,12,12,12,12,4,4,4,4,12,12,12,12,2,2,2,2,12,12,12,12,2,2,2,2,6,6,10,10,12,12,12,12,14,14,18,18,28,28,30,30,30,30,32,32,34,34,44,44,50,50,56,56,58,58,64,64,70,76,82,82,84,84,84,84,90,90,92,92,102,102,108,108,118,118,120,120,130,130,136,136,144,144,146,146,154,154,162,162,170,170,172,172,180,180,188,188,190,190,190,190,192,192,194,194,200,200,206,206,214,214,214,214,206,206,206,206,214,214,214,214,212,212,206,206,206,206,216,216,216,216,202,202,202,202,210,210,210,210,208,208,206,206,200,200,198,198,194,194,194,194,206,206,198,198,198,198,210,210,210,210,204,204,202,202,198,198,196,196,196,196,208,208,208,208,204,204,202,202,198,198,196,196,194,194,194,194,192,192,192,192,190,190,188,188,188,188,190,190,190,190,176,176,174,174,170,170,152,152,150,150,142,142,140,140,132,132,128,128,122,122,118,118,112,112,106,106,96,96,94,94,84,84,82,82,74,74,70,70,62,62,60,60,52,52,50,50,42,42,28,28,22,22,12,12,6,6,6,6,2,2,2,42,44,44,50,50,52,52,54,54,54,54,62,62,64,64,74,74,76,76,86,86,90,90,98,100,102,102,106,106,106,106,110,110,120,120,132,132,134,134,142,142,142,142,146,146,150,150,156,156,156,156,166,166,168,168,184,186,198,198,198,198,208,208,208,208,198,198,198,198,196,196,194,194,186,186,184,184,174,174,172,172,162,162,160,214,212,212,208,208,208,208,210,210,218,218,218,218,210,210,214,214,218,218,218,218,210,210,210,210,218,218,218,218,210,210,210,210,218,218,218,218,212,212,218,218,218,218,214,214,218,218,218,218,212,212,212,212,218,218,218,58,70,56,60,60,76,128,128,128,136,136,136,136,144,144,144,144,140,134,134,134,130,130,126,32,32,32,42,42,42,42,46,46,46,46,54,54,54,62,62,58,58,58,62,62,62,62,56,70,70,70,64,64,80,22,26,26,54,54,54,68,58,58,58,58,68,68,56,76,76,76,88,80,80,80,98,98,98,86,112,106,106,106,120,120,120,120,104,126,126,126,138,138,138,138,124,130,130,130,146,60,60,60,68,68,82,70,70,70,90,88,88,88,98,98,98,98,86,104,104,104,114,110,110,110,120,124,116,116,116,116,130,126,114,134,134,134,138,138,144,144,144,138,140,140,160,154,154,148,168,162,162,162,174,174,174,174,160,182,182,182,196,196,196,196,182,190,190,190,206,206,206,98,114,114,114,114,96,96,96,100,114,98,98,132,132,132,120,120,120,120,130,130,130,130,118,136,136,136,142,142,142,142,148,148,148,154,154,154,168,168,168,168,172,168,154,154,154,154,170,176,176,182,196,188,188,188,178,178,178,12,18,24,34,40,50,58,70,78,90,26,36,42,54,60,72,82,94,102,114,94,102,152,152,152,218,162,162,162,182,182,182,182,160,198,218,198,196,196,196,196,218,194,218,184,180,180,168,184,154,172,172,172,164,164,162,168,162,162,162,202,202,208,208,208,216,216,218,212,216,216,218,218,218,206,206,206,218,218,218,208,214,170,170,170,186,172,182,176,176,176,184,184,196,196,210,210,210,174,174,210,210,164,164,172,172,180,180,188,188,198,198,206,206,212,212,148,134,148,126,146,132,148,130,146,134,158,158,26,26,26,36,36,36,36,46,46,46,52,52,52,52,58,74,64,64,64,56,30,34,34,68,68,68,86,74,74,74,74,86,80,72,90,90,90,106,94,94,94,108,108,108,112,112,102,122,118,130,130,130,130,118,118,118,138,138,138,150,150,150,150,138,146,164,164,164,56,56,56,62,62,74,74,74,64,64,64,82,82,82,82,96,96,96,96,80,112,102,102,102,110,110,110,120,120,120,120,132,132,118,118,126,138,138,138,150,142,142,142,154,154,154,158,158,148,170,166,178,178,178,178,166,166,166,186,186,186,200,200,200,200,186,192,192,192,212,96,96,96,114,114,114,114,94,94,94,114,94,120,120,120,134,134,134,134,118,118,140,140,140,140,140,140,148,148,148,148,156,156,156,156,174,174,174,174,178,174,162,162,162,162,174,182,182,190,204,198,198,198,190,190,190,120,126,170,176,90,82,2,42,42,52,52,82,82,92,92,122,122,136,136,166,166,180,180,180,180,208,208,218,150,150,150,168,168,174,174,174,174,188,188,188,188,192,192,196,196,196,158,158,158,166,166,166,166,172,172,172,178,178,178,182,182,182,182,188,60,64,64,88,88,94,94,94,94,96,96,96,96,98,98,98,98,112,112,112,76,76,76,82,82,82,82,88,88,88,88,92,92,92,92,86,86,86,98,108,98,98,98,104,104,104,104,108,108,108,30,30,30,6,6,4,4,4,4,8,8,8,8,2,2,2,2,10,10,14,14,14,14,18,18,18,18,24,24,24,24,30,14,10,10,10,148,152,152,200,200,218,218,202,202,202,202,188,188,188,188,198,198,198,206,206,206,198,198,198,198,214,214,214,214,210,210,210,210,218,218,210,210,210,210,216,216,216,160,166,166,166,166,172,172,172,172,182,182,182,182,190,190,190,186,186,186,192,66,62,62,2,2,2,6,6,6,42,42,28,26,26,26,18,18,18,18,12,12,12,12,6,6,6,6,18,18,18,18,10,10,10,10,14,46,46,46,52,6,10,10,10,10,14,14,14,64,64,64,68,68,76,76,92,92,92,92,106,106,120,120,120,120,142,142,144,144,150,150,150,150,138,138,118,118,116,116,112,112,94,94,82,82,68,68,64,64,60,96,90,90,86,86,86,86,98,98,100,100,104,104,94,94,88,96,96,96,104,104,106,106,108,108,108,108,98,98,90,90,90,90,96,108,110,110,116,116,116,116,112,112,108,108,106,106,116,116,112,112,108,108,108,108,114,114,114,114,100,100,98,98,94,94,94,94,96,96,88,88,86,86,94,94,80,80,86,86,86,86,80,80,92,92,90,98,98,98,106,106,94,94,94,94,102,102,102,102,98,108,108,108,108,108,116,116,118,118,118,118,106,106,102,98,100,100,110,110,96,96,96,96,98,98,102,102,110,110,110,110,108,26,34,34,36,36,44,44,46,46,52,52,60,60,62,62,70,70,72,72,80,80,90,90,96,96,98,98,106,106,116,116,118,118,124,124,132,132,134,134,142,142,144,144,152,152,160,160,162,162,168,168,170,170,176,52,40,40,28,28,6,6,6,6,30,30,56,56,56,56,50,24,20,20,14,38,42,42,54,18,24,24,32,32,40,40,42,42,48,132,132,132,134,134,148,148,150,150,162,162,164,164,168,168,168,174,174,178,178,178,168,172,172,176,176,184,192,192,196,196,196,196,194,194,188,188,180,180,180,180,182,182,194,198,198,198,202,202,210,210,210,210,202,188,182,98,98,98,108,108,106,106,96,96,110,110,96,88,100,100,90,90,98,98,94,132,134,134,146,146,152,152,174,174,174,174,172,172,154,154,150,150,136,146,142,140,150,178,188,188,200,200,200,200,196,126,58,58,58,58,68,58,70,24,18,38,46,46,2,48,36,48,46,46,40,40,40,46,46,46,60,60,60,60,42,42,42,42,36,36,36,36,52,52,52,52,68,68,68,68,138,138,138,138,154,154,154,154,178,178,178,178,100,100,100,100,50,50,46,46,50,50,50,50,44,50,56,56,56,56,44,62,80,80,80,80,60,102,102,102,102,102,122,122,122,100,126,132,132,132,172,172,172,172,166,166,128,134,134,134,146,146,160,160,162,162,162,144,144,144,150,146,158,158,158,158,146,50,50,50,54,48,48,48,54,54,72,72,72,72,58,58,58,58,70,70,70,68,68,68,56,22,54,72,146,160,188,82,82,82,72,66,66,66,62,62,60,162,158,158,158,158,150,150,148,148,148,148,152,152,152,152,158,158,158,158,172,172,172,182,182,182,192,186,180,186,186,182,182,182,182,182,182,182,192,182,192,192,192,192,208,208,208,208,214,214,214,214,204,204,204,204,172,172,162,172,172,172,162,26,26,26,30,30,36,36,60,60,78,78,90,90,118,118,128,128,154,26,22,22,6,6,6,44,44,44,60,60,78,78,82,82,100,100,102,102,120,120,122,122,138,138,140,140,156,156,172,172,174,174,190,190,192,192,204,204,208,208,218,218,218,48,30,30,26,26,2,76,86,86,86,86,104,104,104,104,80,80,68,68,68,68,74,74,74,74,78,78,80,80,80,80,76,82,78,78,74,74,74,74,84,84,88,88,96,96,96,96,86,86,84,88,86,88,110,110,110,110,104,104,102,102,102,102,96,96,96,96,90,90,88,88,88,88,94,110,118,118,118,118,120,120,154,154,154,154,158,158,162,158,140,140,136,136,136,136,124,124,124,124,138,138,138,126,126,126,134,120,120,120,128,122,122,122,130,130,134,134,182,182,182,182,180,180,150,142,142,142,134,148,148,148,156,154,148,158,158,158,148,142,134,134,134,134,138,138,142,126,126,126,110,110,104,104,80,80,72,72,62,62,62,62,60,60,50,50,50,50,44,44,30,48,48,48,72,72,74,46,42,42,24,24,6,6,6,6,14,14,14,18,14,14,2,2,2,2,20,20,22,22,30,30,30,30,26,26,36,26,34,34,34,72,48,48,46,46,46,46,54,54,82,82,84,84,84,84,50,50,50,50,70,70,94,94,94,94,80,80,62,62,62,24,86,86,86,72,50,42,42,42,54,92,106,106,106,106,114,88,128,128,128,128,140,140,142,138,136,136,118,118,118,112,112,112,120,148,140,140,132,130,130,130,110,110,110,110,114,114,132,132,142,142,146,146,148,148,148,148,138,138,138,138,156,156,156,156,146,130,130,130,138,138,140,154,154,154,168,168,170,170,170,152,152,152,178,178,178,178,192,192,192,192,182,182,180,180,164,152,152,152,158,158,158,158,150,196,160,160,160,168,180,180,180,180,176,184,184,184,192,192,192,192,176,176,176,176,202,202,202,202,196,200,206,206,206,206,214,214,214,214,206,206,206,206,186,186,186,186,176,176,176,176,184,190,190,190,206,200,200,200,208,208,208,208,186,192,184,174,174,176,176,176,176,176,156,156,148,148,140,38,42,38,38,38,28,28,28,28,16,38,26,26,26,26,16,28,26,26,14,40,40,40,62,62,62,62,70,70,90,108,108,108,114,114,128,136,138,138,150,150,154,174,170,170,156,78,54,54,42,42,40,40,26,26,2,46,46,46,56,58,58,26,27,26,32,28,28,28,40,40,40,40,16,16,16,16,42,42,52,52,52,52,24,24,24,24,38,38,38,34,42,36,36,48,48,48,44,44,44,44,78,78,78,78,60,60,60,60,64,64,64,64,44,44,44,54,62,58,58,54,62,72,72,72,68,68,68,68,88,88,88,84,94,88,104,104,104,104,100,100,100,100,80,86,86,86,94,94,94,78,86,86,86,86,74,74,74,94,94,94,106,106,106,106,100,108,130,130,130,130,124,124,124,124,116,116,116,116,122,122,122,122,102,102,102,102,110,110,110,110,106,106,106,106,100,120,120,120,130,130,130,130,130,130,126,126,126,126,140,140,140,140,136,136,136,136,142,142,142,142,122,128,128,128,138,138,138,138,128,174,188,188,188,188,184,184,184,184,188,188,188,188,184,184,184,184,186,186,196,196,196,196,156,156,156,180,180,180,176,176,174,174,172,172,166,166,164,164,160,160,160,160,166,166,166,166,164,164,162,162,160,160,158,158,158,158,160,160,162,162,162,162,156,156,154,154,152,152,152,152,154,154,154,154,148,148,148,148,152,152,152,152,146,146,142,142,140,140,140,140,144,144,148,148,148,148,152,152,156,156,162,162,162,162,158,158,158,158,164,164,164,164,158,158,158,158,160,160,166,166,166,166,162,162,162,162,164,164,168,168,174,174,176,176,176,176,170,170,170,170,176,170,170,170,164,164,164,164,196,164,196,168,176,176,176,176,168,176,180,180,180,180,168,172,172,182,182,190,184,184,184,184,188,188,184,192,192,192,198,190,196,192,198,192,166,6,154,200,218,80,94,94,94,94,82,82,82,82,82,78,86,90,94,94,94,94,90,94,100,104,104,104,112,112,112,112,104,124,124,124,118,118,118,118,126,126,126,126,94,94,94,128,136,132,132,132,138,138,138,146,146,146,146,58,58,58,50,50,50,50,58,64,64,64,70,74,74,74,80,80,80,90,82,82,82,82,90,94,94,94,102,102,102,98,98,98,106,112,106,106,106,106,112,112,112,112,106,114,114,114,120,120,110,120,120,120,126,126,126,126,120,134,128,128,128,128,132,138,138,138,144,144,144,138,144,86,80,80,80,80,88,88,88,86,86,86,90,90,90,90,80,80,80,80,86,92,92,92,96,96,102,102,102,102,106,102,96,96,96,96,102,114,108,108,108,108,114,114,114,114,100,116,116,116,122,122,122,118,124,124,134,134,134,134,138,132,124,124,124,124,134,146,136,136,136,136,146,146,146,146,124,150,150,150,154,154,140,158,166,166,166,166,158,158,158,158,166,168,168,168,176,176,176,154,154,154,154,154,154,154,148,142,138,132,128,122,118,114,110,104,100,94,90,84,80,74,70,64,60,54,50,44,44,44,44,44,44,44,44,44,44,44,48,54,58,62,66,70,74,74,74,74,80,86,90,94,98,102,106,110,114,118,122,126,130,134,138,142,146,150,154,158,162,166,170,176,180,184,184,184,184,184,184,184,184,184,180,174,172,168,164,158,156,160,160,160,38,38,38,38,68,68,68,68,48,48,48,48,62,62,62,62,54,54,54,54,58,58,58,58,52,52,52,52,66,66,66,66,44,44,44,44,78,78,78,78,204,204,204,204,192,192,192,192,198,198,198,198,202,202,202,202,188,188,188,188,208,208,208,208,146,146,146,146,182,182,182,182,154,154,154,154,176,176,176,176,158,158,158,158,180,180,180,180,150,150,150,150,212,212,212,212,188,188,188,188,162,162,162,162,36,36,36,36,42,42,42,42,82,82,82,82,110,110,110,110,92,92,92,92,124,124,124,124,102,102,102,102,120,120,120,120,106,106,106,106,122,122,122,122,96,96,96,96,126,126,126,126,88,88,88,88,104,104,104,104,86,86,86,86,98,98,98,98,94,94,94,94,90,90,90,90,100,100,100,100,82,82,82,82,36,36,36,36,32,32,32,32,210,210,210,210,194,194,194,194,174,174,174,174,202,202,202,202,198,198,198,198,180,180,180,180,188,188,188,188,198,198,198,198,206,206,206,206,170,170,170,170,192,192,192,192,218,218,218,218,144,144,144,144,190,190,190,190,204,204,204,204,156,156,156,156,200,200,200,200,194,194,194,194,148,148,148,148,210,210,210,210,186,186,186,186,140,140,140,140,202,202,202,202,116,116,116,116,130,130,130,130,126,126,126,126,122,122,122,122,194,194,194,194,150,150,150,150,188,188,188,188,154,154,154,154,192,192,192,192,134,134,134,134,188,188,188,188,128,128,128,128,140,140,140,140,90,90,90,90,140,140,140,140,112,112,112,112,140,140,140,140,106,106,106,106,102,102,102,102,98,98,98,98,94,94,94,94,86,86,86,86,82,82,82,82,14,14,14,14,68,68,68,68,36,36,36,36,52,52,52,52,42,42,42,42,50,50,50,50,38,38,38,38,62,62,62,62,26,26,26,26,76,76,76,76,8,8,8,8,26,26,26,26,218,218,218,218,214,214,214,214,4,4,4,4,18,18,18,18,14,14,14,14,78,78,78,78,32,32,32,32,28,28,28,58,58,2,2,2,2,2,2,2,2,2,2,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100,102,104,106,108,110,112,114,116,118,120,122,124,126,128,130,132,134,136,138,140,142,144,146,148,150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180,182,184,186,188,190,192,194,196,198,200,202,204,206,208,210,212,214,216,218,58,58,2,2,2,2,2,2,2,2,2,2,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100,102,104,106,108,110,112,114,116,118,120,122,124,126,128,130,132,134,136,138,140,142,144,146,148,150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180,182,184,186,188,190,192,194,196,198,200,202,204,206,208,210,212,214,216,218,58,58,2,2,2,2,2,2,2,2,2,2,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100,102,104,106,108,110,112,114,116,118,120,122,124,126,128,130,132,134,136,138,140,142,144,146,148,150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180,182,184,186,188,190,192,194,196,198,200,202,204,206,208,210,212,214,216,218,58,58,2,2,2,2,2,2,2,2,2,2,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100,102,104,106,108,110,112,114,116,118,120,122,124,126,128,130,132,134,136,138,140,142,144,146,148,150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180,182,184,186,188,190,192,194,196,198,200,202,204,206,208,210,212,214,216,218,58,58,2,2,2,2,2,2,2,2,2,2,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100,102,104,106,108,110,112,114,116,118,120,122,124,126,128,130,132,134,136,138,140,142,144,146,148,150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180,182,184,186,188,190,192,194,196,198,200,202,204,206,208,210,212,214,216,218,46,47,46,34,34,34,34,26,26,26,26,38,38,38,38,28,28,28,28,16,16,16,16,2,46,46,46,76,76,76,76,88,88,88,88,96,96,96,96,82,82,82,82,90,90,90,90,102,102,102,102,118,118,118,118,80,80,80,80,40,40,40,40,40,40,30,30,30,76,76,76,86,86,86,32,32,84,84,84,84,84,80,80,80,80,84,42,36,36,36,36,42,42,42,54,54,60,60,66,66,34,48,48,48,48,34,34,34,76,92,92,92,92,78,78,78,58,58,58,68,68,66,66,50,40,40,40,44,44,44,44,38,84,90,90,90,90,86,86,86,90,72,50,34,42,2,6,6,10,10,14,14,106,106,110,110,114,114,54,54,54,64,64,64,46,68,68,68,68,46,46,46,94,102,96,106,96,104,30,14,20,30,30,18,74,74,74,94,94,94,34,34,34,54,54,54,112,124,124,124,124,144,144,150,150,158,158,170,170,194,194,194,194,192,192,184,184,168,168,144,144,142,142,142,142,130,130,130,130,112,134,136,136,148,148,148,148,138,138,138,158,158,158,172,172,172,172,156,180,180,180,180,164,164,164,150,150,144,176,170,170,170,170,158,158,158,160,172,162,172,160,150,148,152,152,152,152,148,158,158,158,172,172,172,180,184,184,184,184,180,180,180,180,184,184,184,184,180,180,180,180,180,180,180,108,108,108,122,106,106,106,122,142,144,144,162,138,138,138,148,148,154,174,192,190,192,192,212,198,202,202,218,218,218,50,50,50,54,54,54,54,48,58,66,66,66,66,62,62,62,62,50,50,50,50,66,66,66,66,48,56,56,60,60,62,70,64,64,62,62,56,56,52,52,48,54,62,68,2,2,2,24,24,24,24,14,14,14,14,22,22,26,26,30,30,34,34,34,34,40,40,42,42,48,48,50,50,60,60,64,64,70,70,72,72,78,78,82,82,98,98,98,98,102,102,98,98,92,92,92,92,90,90,86,86,76,76,72,72,64,64,62,62,50,50,40,40,38,38,30,30,26,26,22,22,20,20,18,206,200,200,194,194,194,184,170,170,160,160,158,156,158,158,174,174,178,178,184,186,176,176,162,184,184,184,182,182,162,62,62,1,2,2,2,2,2,2,2,2,2,2,2,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100,102,104,106,108,110,112,114,116,118,120,122,124,126,128,130,132,134,136,138,140,142,144,146,148,150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180,182,184,186,188,190,192,194,196,198,200,202,204,206,208,210,212,214,216,218,1,2,2,2,2,2,2,2,2,2,2,2,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100,102,104,106,108,110,112,114,116,118,120,122,124,126,128,130,132,134,136,138,140,142,144,146,148,150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180,182,184,186,188,190,192,194,196,198,200,202,204,206,208,210,212,214,216,218,1,2,2,2,2,2,2,2,2,2,2,2,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100,102,104,106,108,110,112,114,116,118,120,122,124,126,128,130,132,134,136,138,140,142,144,146,148,150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180,182,184,186,188,190,192,194,196,198,200,202,204,206,208,210,212,214,216,218,1,2,2,2,2,2,2,2,2,2,2,2,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100,102,104,106,108,110,112,114,116,118,120,122,124,126,128,130,132,134,136,138,140,142,144,146,148,150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180,182,184,186,188,190,192,194,196,198,200,202,204,206,208,210,212,214,216,218,1,2,2,2,2,2,2,2,2,2,2,2,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100,102,104,106,108,110,112,114,116,118,120,122,124,126,128,130,132,134,136,138,140,142,144,146,148,150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180,182,184,186,188,190,192,194,196,198,200,202,204,206,208,210,212,214,216,218,100,101,100,100,100,96,96,90,90,84,84,84,84,90,90,92,92,102,102,102,102,98,88,88,88,88,84,92,92,98,94,94,90,90,90,94,90,90,90,74,74,64,90,92,92,106,106,108,108,120,120,122,122,126,126,128,62,120,120,126,120,120,120,126,64,64,64,58,66,60,60,54,54,56,56,64,122,122,122,126,126,126,126,130,130,130,130,134,134,134,134,132,132,126,122,134,128,128,128,132,132,130,130,126,126,130,82,82,80,76,106,110,102,102,96,96,56,56,56,64,64,66,66,66,66,62,62,58,58,58,72,72,72,74,74,76,76,80,80,80,80,76,76,72,72,72,72,76,76,82,92,92,92,90,90,88,88,84,84,84,84,88,88,92,92,92,92,88,88,86,86,86,102,104,104,106,106,106,106,102,102,100,100,98,98,98,98,100,100,102,102,106,116,116,116,112,112,110,110,110,110,114,114,114,110,116,122,122,122,128,128,128,128,130,130,132,132,134,134,134,134,132,126,122,122,118,118,118,128,130,130,134,134,136,136,136,136,130,130,126,126,126,126,132,132,134,138,138,138,140,140,144,144,146,146,146,140,140,154,154,154,150,150,144,144,144,144,148,148,152,152,152,152,152,160,160,160,162,162,164,164,168,168,168,168,174,174,174,64,64,64,60,60,58,58,52,52,52,52,54,54,60,60,62,62,62,62,58,58,56,56,56,76,76,76,74,74,72,72,68,68,68,68,70,70,74,74,74,82,82,82,84,84,86,86,90,90,90,98,98,98,102,102,104,104,104,114,120,120,122,122,122,122,120,120,118,118,114,114,114,114,116,116,122,128,128,128,132,132,134,134,134,140,140,140,148,148,148,142,146,146,148,148,148,148,146,146,144,144,140,158,162,162,164,164,164,164,162,162,158,158,158,158,160,160,164,164,166,166,166,172,172,172,166,166,166,170,170,170,172,172,174,174,180,190,190,190,188,188,186,186,182,182,182,182,184,184,188,188,188,188,188,188,174,174,166,166,160,160,150,150,148,148,132,132,130,130,102,102,100,100,94,94,92,92,86,86,84,84,82,82,80,80,78,78,72,72,70,70,66,66,62,62,58,58,54,54,52,52,50,50,48,48,38,38,36,36,32,32,32,32,26,26,24,24,22,22,22,22,18,18,18,18,20,20,20,20,18,18,18,18,20,20,20,20,28,28,28,28,24,24,24,24,26,26,26,26,28,28,28,28,30,30,30,30,26,26,26,26,22,22,34,34,34,34,32,32,32,32,30,30,30,30,36,36,36,36,34,34,34,34,30,30,30,30,34,34,44,44,52,52,56,56,58,58,60,60,62,62,64,64,70,70,76,76,82,82,86,86,88,88,92,92,94,94,102,102,104,104,110,110,114,114,116,116,118,118,120,120,122,122,126,126,128,128,130,130,136,136,140,140,142,142,144,144,146,146,152,152,160,160,162,162,166,166,168,168,170,170,172,172,176,176,178,178,176,176,178,178,180,180,182,182,184,184,188,188,188,188,194,194,192,192,192,192,198,198,198,198,196,196,194,194,192,192,192,192,194,194,196,196,196,196,192,192,190,190,188,188,188,188,194,194,196,196,198,198,202,202,202,202,196,196,196,196,204,204,204,204,194,194,194,194,198,198,202,202,204,204,204,204,202,202,200,200,198,198,198,198,200,204,208,208,208,208,206,206,202,202,200,200,198,198,192,192,190,190,188,188,188,188,194,194,194,194,190,190,184,184,184,184,190,190,190,190,186,186,184,184,182,182,180,180,180,180,188,188,190,190,192,192,192,192,190,190,188,188,186,186,186,188,198,198,198,198,192,192,190,190,190,190,194,194,196,196,198,198,198,198,196,196,198,198,200,200,202,202,202,100,101,2,2,2,2,2,2,2,2,2,2,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100,102,104,106,108,110,112,114,116,118,120,122,124,126,128,130,132,134,136,138,140,142,144,146,148,150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180,182,184,186,188,190,192,194,196,198,200,202,204,206,208,210,212,214,216,218,110,111,110,92,92,90,90,88,88,84,84,82,82,80,80,78,78,58,58,58,58,56,56,56,56,54,54,54,54,52,52,52,52,58,58,58,58,70,70,70,70,80,80,134,134,138,138,140,140,142,142,144,144,154,154,154,154,164,164,164,164,170,170,170,170,160,160,158,158,154,154,154,154,148,148,140,140,134,134,130,130,128,128,126,126,124,124,110,112,112,140,140,140,142,142,142,142,150,150,150,158,158,158,166,166,166,166,168,168,168,168,176,176,176,168,172,172,176,176,176,176,180,180,182,182,186,186,188,188,200,178,184,184,190,190,198,198,202,202,208,174,202,202,204,204,212,172,182,182,184,184,186,186,188,188,200,200,206,206,214,158,164,164,172,172,178,178,180,180,182,182,184,184,186,186,190,190,202,146,146,146,156,156,158,158,166,166,170,170,180,120,120,120,130,130,132,132,142,142,142,106,106,106,108,108,108,92,90,90,86,86,86,86,80,80,80,80,78,78,78,70,62,62,62,62,54,54,54,54,50,50,50,50,46,56,54,54,50,50,50,50,42,42,34,34,30,30,28,28,26,26,24,24,22,48,40,40,36,36,26,26,24,24,16,46,28,28,26,26,12,50,46,46,34,34,26,26,20,20,18,60,56,56,48,48,42,42,36,36,32,32,28,78,78,78,74,74,70,70,56,56,54,98,98,98,94,94,90,90,90,90,98,98,98,98,94,94,92,92,90,90,90,90,92,108,108,108,106,106,102,102,102,102,104,104,106,106,110,110,110,114,114,114,116,116,120,120,120,122,122,122,124,124,126,126,130,130,130,136,140,140,142,142,138,138,136,136,134,134,134,134,140,140,142,146,146,146,150,150,154,154,154,66,66,66,68,68,70,70,72,72,72,72,70,70,66,66,64,64,70,70,72,72,72,72,68,68,66,66,64,64,64,76,82,82,82,82,80,80,78,78,76,76,76,76,80,80,82,82,84,84,84,86,86,86,88,88,94,100,100,100,98,98,96,96,96,96,98,98,100,100,102,102,102,102,102,102,100,100,96,96,96,114,114,114,116,116,122,122,122,122,120,120,116,116,114,118,118,118,128,128,128,128,130,130,134,134,134,134,134,136,136,136,138,138,140,140,142,142,142,148,154,154,154,154,152,152,150,150,148,148,148,148,152,152,154,160,166,166,166,166,156,156,164,164,154,98,98,98,96,96,94,94,90,90,90,90,92,92,94,94,98,98,96,100,100,100,102,102,106,106,106,106,104,104,102,102,102,82,82,82,86,86,90,90,96,96,98,98,104,104,106,106,118,58,59,58,58,58,76,76,76,76,58,84,84,84,96,96,96,96,82,106,106,106,116,116,116,116,104,124,114,114,114,114,124,120,112,142,130,130,130,152,152,152,152,62,92,92,92,92,60,60,60,66,72,72,72,72,66,66,66,76,76,76,82,82,82,82,74,74,78,78,78,78,74,74,74,82,82,82,86,86,86,86,80,88,64,64,64,80,80,70,70,92,60,60,52,52,52,52,66,66,66,86,86,86,98,98,98,98,92,66,66,66,26,26,22,86,86,86,124,124,134,86,66,70,70,74,74,74,80,80,80,86,86,86,104,104,110,66,64,64,40,40,34,58,58,94,94,106,106,106,106,106,106,106,106,102,108,108,108,108,102,102,102,96,102,108,114,114,114,114,98,98,98,104,108,108,104,104,108,108,104,104,108,108,104,104,108,98,112,112,112,112,100,100,100,106,106,106,100,100,100,100,116,116,116,116,108,108,108,98,90,90,90,90,98,114,122,122,122,122,114,14,15,14,14,14,22,22,22,14,22,22,22,32,32,32,46,54,54,54,66,66,66,76,76,76,76,76,84,84,84,84,82,82,76,76,76,76,82,82,82,56,50,50,50,50,56,56,56,56,40,68,78,78,78,78,68,68,68,84,84,84,92,98,98,102,118,110,110,126,126,126,136,136,136,136,128,144,144,144,152,142,152,142,156,168,168,168,178,178,178,178,166,166,172,172,172,20,96,30,31,30,30,30,52,52,52,52,24,24,24,24,44,44,44,44,34,34,34,34,44,44,44,44,26,64,88,88,88,88,76,76,76,76,88,88,88,88,58,58,58,58,82,82,82,82,64,64,64,98,98,98,106,106,106,106,94,94,94,94,124,124,124,124,110,110,110,110,96,20,20,20,34,34,34,34,46,46,46,46,68,68,68,68,76,76,76,76,104,104,114,114,112,112,104,104,104,104,126,126,126,126,152,152,152,152,160,160,160,160,192,192,194,194,198,198,196,196,162,162,196,196,196,196,152,152,152,152,124,124,124,124,86,86,86,86,112,112,110,110,102,102,102,102,72,72,72,72,68,68,68,68,44,44,44,44,38,38,38,38,32,32,32,32,26,26,26,26,20,20,20,68,50,50,50,50,58,58,58,58,50,50,50,50,70,70,70,82,100,100,100,100,82,134,134,134,146,146,146,146,134,158,158,158,178,178,178,178,158,202,202,202,212,212,212,212,202,202,212,212,212,212,200,200,200,14,14,14,22,22,22,28,36,36,36,36,40,36,26,26,26,26,38,40,44,44,44,44,54,54,54,54,44,54,58,58,58,58,68,68,68,68,56,70,74,74,74,74,82,86,82,82,82,82,46,38,8,94,98,98,98,98,94,98,106,106,106,106,96,108,112,112,112,112,112,116,120,120,120,120,126,126,136,128,128,128,132,132,132,138,142,142,142,142,150,150,150,164,160,160,160,160,164,162,152,152,152,152,164,168,178,178,178,178,182,176,168,168,168,168,174,174,182,182,186,186,186,186,196,200,196,196,196,196,86,90,94,94,94,94,88,92,106,106,106,106,110,100,100,100,110,114,114,114,122,122,122,122,112,124,128,128,128,128,124,128,138,138,138,138,142,130,142,146,146,146,156,156,156,156,146,154,160,160,160,160,156,160,174,174,174,174,160,166,180,180,184,180,180,180,196,196,196,196,178,206,206,206,206,6,6,6,18,18,18,22,22,22,28,28,28,28,22,34,34,34,42,42,42,62,52,52,52,52,62,62,62,62,70,62,62,62,62,62,42,70,70,70,80,80,80,80,68,76,88,82,82,82,86,74,86,80,80,80,88,92,92,92,100,100,100,106,112,112,112,112,104,104,104,104,114,128,128,128,176,176,176,176,170,170,170,170,132,132,132,132,126,134,138,138,138,138,142,142,142,142,142,142,148,148,148,148,142,148,152,152,152,152,148,150,158,158,158,158,162,162,162,162,166,166,166,166,172,126,126,126,178,178,178,178,176,134,136,140,144,152,154,158,160,164,166,128,178,178,126,134,134,134,138,138,138,138,134,140,140,140,144,144,144,144,140,146,146,146,154,154,154,148,148,162,156,156,156,156,162,158,154,164,164,170,170,170,164,166,172,190,190,190,196,196,196,196,190,190,196,196,196,196,190,190,190,22,23,22,22,22,48,48,48,48,18,42,42,42,20,54,54,54,54,64,64,64,86,86,72,72,72,72,84,84,84,84,100,100,96,96,86,86,84,84,84,118,102,102,102,102,118,114,102,126,126,126,144,144,144,144,120,136,158,78,82,82,82,82,78,78,78,78,90,90,90,34,34,34,48,48,48,48,28,54,54,54,70,70,70,90,76,76,76,76,94,94,94,94,74,72,72,72,26,26,26,26,34,34,46,46,78,78,78,88,88,108,108,108,82,122,110,110,110,110,126,120,110,130,130,130,140,140,140,140,146,146,146,146,156,156,156,160,160,160,164,164,164,164,174,174,174,182,182,176,188,178,178,178,186,186,186,186,204,192,192,198,218,216,192,192,196,196,214,208,194,136,126,34,34,18,18,30,30,160,160,160,162,162,170,170,170,170,178,178,178,190,180,180,180,180,194,194,194,194,188,198,198,198,206,206,206,122,122,122,134,134,142,142,142,142,134,134,134,134,144,144,144,144,144,144,152,152,152,152,166,160,160,160,164,164,164,166,174,174,174,174,166,166,166,166,176,176,176,180,180,180,188,188,188,218,218,218,218,218,218,218,218,218,218,218,218,218,212,208,202,198,192,186,182,178,174,170,166,160,154,150,146,142,138,132,128,124,118,114,108,104,98,94,90,84,80,74,70,64,60,54,50,44,40,34,28,22,18,14,10,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,6,10,16,18,24,28,34,38,42,46,50,54,58,62,66,70,74,78,84,88,92,96,100,104,108,112,118,122,126,130,134,138,142,146,150,154,158,162,166,168,174,176,182,186,190,194,198,202,206,210,214,218,218,218,218,218,218,218,218,218,218,218,218,218,218,218,218,218,218,218,218,218,218,218,218,218,218,218,218,218,218,218,110,126,118,118,126,136,136,136,136,124,124,124,142,142,142,152,164,154,154,154,170,170,170,178,178,178,178,106,106,106,106,178,18,18,18,26,26,26,26,32,32,32,32,34,34,38,38,40,40,40,50,50,50,50,58,74,64,64,64,58,28,36,36,36,36,72,72,72,82,82,82,94,92,78,78,94,100,100,100,116,108,108,108,118,118,118,124,124,110,140,136,136,136,156,156,156,156,136,164,164,164,178,178,178,178,164,172,172,172,190,36,36,36,38,38,52,42,76,70,70,70,86,86,86,86,66,106,92,92,92,102,102,102,112,124,110,110,110,110,130,122,110,138,138,138,140,140,158,148,148,148,162,162,162,168,168,160,178,178,178,178,190,190,190,190,178,196,196,196,214,214,214,214,196,206,206,206,218,218,218,38,64,64,64,64,34,34,34,36,36,36,64,72,72,72,94,94,94,94,72,72,94,94,94,100,100,100,110,110,110,110,120,120,120,124,124,124,142,142,142,142,126,126,126,126,142,142,148,150,150,166,166,166,154,154,154,158,178,214,214,214,166,166,166,166,160,160,160,160,132,132,132,132,126,126,126,126,106,106,106,106,102,102,102,102,74,74,74,74,70,70,70,70,26,26,26,26,22,22,22,22,4,4,4,4,10,10,10,10,4,4,4,4,28,28,28,28,4,4,4,4,16,16,16,16,4,4,4,4,10,10,10,10,4,4,4,4,32,32,32,32,2,2,2,2,6,6,6,6,10,10,10,10,4,4,4,4,20,20,20,20,4,4,4,4,8,8,8,8,4,4,4,4,22,22,22,22,26,26,26,26,54,54,54,54,58,58,58,58,82,82,82,82,86,86,86,86,94,94,94,94,98,98,98,98,104,104,104,104,108,108,108,108,126,126,126,126,130,130,130,130,136,136,136,136,140,140,140,140,154,154,154,154,158,158,158,158,186,186,186,186,188,188,188,188,202,202,202,202,206,206,206,206,214,214,214,214,218,218,218,218,182,182,182,182,218,218,218,218,214,214,214,214,218,218,218,218,214,202,198,198,198,198,208,208,208,208,214,214,214,214,178,178,178,178,214,214,214,64,50,50,50,50,64,64,64,70,70,70,84,84,84,84,70,68,64,84,92,92,92,92,100,100,100,100,96,96,96,64,32,32,32,32,76,76,76,76,54,54,54,60,60,60,78,72,72,66,66,52,100,100,100,100,52,52,52,52,44,44,44,70,138,138,138,134,156,156,156,156,134,134,134,98,98,98,98,70,152,152,152,140,140,144,120,120,120,120,136,136,136,136,144,138,138,138,142,142,142,144,146,146,146,146,152,152,152,152,156,156,156,156,160,160,160,160,148,148,148,98,98,98,98,60,60,54,126,126,126,126,54,54,54,54,54,54,98,98,98,88,88,66,66,78,78,78,74,74,74,44,18,18,18,32,32,32,44,44,44,44,54,54,54,58,66,66,66,66,60,60,60,60,68,74,74,74,78,86,86,86,92,92,104,104,104,104,92,92,92,124,116,116,116,116,124,124,124,132,132,132,138,138,138,146,146,146,146,152,32,32,32,32,42,50,54,98,138,142,154,154,154,32,22,22,22,22,22,22,22,22,22,22,22,22,30,36,42,48,54,28,8,8,8,8,14,36,42,102,210,210,210,210,156,8,12,12,20,20,26,26,28,28,34,34,34,34,32,32,32,32,40,40,44,44,52,52,52,52,56,56,60,60,64,64,64,64,58,58,56,56,50,50,50,50,56,56,58,58,68,68,72,72,74,74,74,74,68,68,64,64,58,58,58,58,64,64,68,68,72,72,72,72,68,68,50,50,28,28,28,28,34,34,38,38,48,48,54,54,62,62,62,62,54,54,52,52,52,52,60,60,68,68,74,74,74,74,80,80,84,84,92,92,92,92,86,86,84,84,64,64,64,64,72,72,74,74,78,78,78,78,74,74,60,60,60,60,76,76,84,84,94,94,94,94,92,92,92,92,98,98,106,106,110,110,110,110,98,98,98,98,106,106,112,112,118,118,118,118,102,102,102,102,114,114,126,126,132,132,132,132,134,134,140,140,146,146,146,146,142,142,138,138,134,134,134,134,138,138,142,142,164,164,174,174,178,178,178,178,168,168,160,160,152,152,152,152,164,164,168,168,174,174,174,174,168,168,166,166,162,162,162,162,164,164,168,168,172,172,172,172,166,166,166,166,170,170,174,174,178,178,176,176,172,172,170,170,170,170,180,180,182,182,188,188,188,188,182,182,182,182,188,188,190,190,190,190,188,188,186,186,180,180,180,180,188,188,196,196,202,202,202,202,198,198,194,194,194,194,198,198,200,200,200,200,188,188,188,188,196,196,202,202,206,206,206,206,202,202,190,190,182,182,180,180,170,170,170,170,174,174,178,178,182,182,182,182,174,174,170,170,166,166,164,164,156,156,156,156,160,160,164,164,166,166,166,166,152,152,152,152,156,156,158,158,162,162,162,162,160,160,158,158,154,154,154,154,158,158,162,162,166,166,166,166,174,174,178,178,190,190,194,194,202,202,204,204,212,212,212,212,200,200,200,200,208,208,212,212,214,214,214,214,212,212,210,210,206,206,206,206,208,208,210,210,214,214,214,214,206,206,206,206,212,212,214,214,214,214,212,212,210,210,208,208,208,208,218,218,218,218,214,214,208,208,204,204,204,204,206,206,208,208,212,212,212,212,208,208,202,202,200,200,200,200,208,208,208,208,202,202,200,200,190,190,190,190,198,198,200,200,212,212,216,216,218,218,218,218,212,212,208,208,204,204,198,198,190,190,190,190,198,198,198,198,188,188,188,190,196,196,196,196,186,186,186,186,176,176,176,176,182,182,182,182,172,172,170,170,166,166,166,166,162,162,158,158,154,154,150,150,146,146,146,146,140,140,136,136,132,132,132,132,134,134,138,138,138,138,130,130,128,128,124,124,124,124,126,126,128,128,128,128,120,120,118,118,116,116,116,116,118,118,120,120,120,120,124,124,128,128,130,130,130,130,126,126,126,126,140,32,32,32,42,42,42,42,42,54,62,54,54,54,62,54,62,72,72,72,80,96,86,86,86,102,102,102,100,100,98,102,102,102,114,114,114,114,102,58,58,60,60,60,72,72,72,72,66,58,72,72,72,72,72,66,66,80,90,82,82,82,94,94,82,102,102,102,114,118,118,118,134,142,142,142,150,150,150,150,156,156,166,164,158,158,158,158,156,156,154,154,150,150,144,144,128,128,128,128,140,140,140,130,120,120,120,120,124,124,130,152,152,152,146,142,142,142,136,132,132,88,76,76,76,76,72,72,72,72,70,70,70,70,66,66,42,42,68,68,70,70,92,92,88,50,50,66,66,28,28,28,34,34,82,82,98,16,22,16,14,14,14,14,18,18,18,18,16,16,16,16,26,26,26,26,30,30,30,22,32,32,32,32,38,38,38,38,44,44,44,44,48,48,48,48,58,58,58,58,62,62,62,62,58,58,58,58,66,24,34,34,34,34,42,42,42,42,46,46,46,46,50,50,50,50,54,54,54,54,58,58,58,58,62,62,62,62,72,72,72,72,76,76,76,76,80,80,80,80,100,100,100,100,104,104,104,104,110,110,110,110,114,114,114,114,110,110,110,110,114,114,114,114,118,118,118,118,124,124,124,124,128,128,128,128,134,134,134,134,130,130,130,130,126,126,126,126,122,122,122,122,130,130,130,130,140,140,140,140,144,144,144,144,148,148,148,148,156,156,156,156,144,144,144,144,140,140,140,140,134,134,134,134,130,130,130,130,146,146,146,146,150,150,150,150,154,154,154,154,158,158,158,158,166,166,166,166,170,170,170,170,174,174,174,174,170,170,170,170,174,174,174,174,170,170,170,170,170,170,174,174,174,174,170,174,178,178,178,178,174,174,174,18,14,14,14,14,18,18,18,18,22,22,22,22,26,26,26,26,30,30,30,30,34,34,34,34,30,30,30,30,26,26,26,26,22,22,22,22,18,40,40,40,56,56,56,56,38,42,42,42,56,52,52,52,38,14,14,14,38,38,38,38,28,28,28,28,14,18,26,26,26,26,16,16,16,20,20,20,36,36,36,36,18,38,46,40,40,40,42,42,42,42,46,46,58,58,58,58,64,64,64,64,62,62,56,56,52,52,50,50,44,44,44,44,36,46,48,48,54,54,54,54,60,60,60,60,50,50,50,50,46,46,42,66,66,66,74,74,74,74,78,78,78,78,82,82,82,82,86,86,86,86,90,90,90,90,86,86,86,86,82,82,82,82,76,76,76,76,86,86,86,86,62,62,62,62,66,72,72,72,86,86,70,94,94,94,106,106,106,106,110,110,110,110,114,114,114,114,110,110,110,110,92,96,96,96,102,102,102,102,106,106,106,106,110,110,110,110,94,118,118,118,132,132,132,132,122,122,122,122,126,126,126,126,122,122,122,122,134,134,134,134,118,140,140,140,146,146,146,146,150,150,150,150,154,154,154,154,158,158,158,158,148,148,148,148,144,144,144,144,138,178,186,186,186,186,182,182,182,182,168,168,168,168,164,164,164,164,188,188,188,188,192,192,192,192,188,188,188,188,160,160,160,160,186,182,182,182,186,186,186,186,168,168,168,168,172,172,172,172,180,180,180,172,172,172,176,176,176,176,172,98,98,98,102,102,102,102,106,106,106,106,102,102,102,102,98,98,98,108,108,108,104,104,104,104,110,110,110,110,114,114,114,114,110,110,110,110,106,62,62,62,72,72,72,72,84,84,84,84,90,90,90,90,94,94,94,94,98,98,98,98,94,98,98,98,102,102,102,102,106,106,106,106,102,102,102,102,106,106,106,106,110,110,110,110,122,122,122,122,130,130,130,130,134,134,134,134,142,142,142,142,150,150,150,150,158,158,158,158,166,110,110,110,114,114,114,114,110,110,114,114,114,114,110,110,110,12,12,12,22,22,40,40,38,38,20,20,12,50,50,50,54,54,54,54,50,62,62,62,72,72,72,72,76,76,76,76,80,80,90,90,90,90,86,86,86,86,80,80,72,72,72,72,72,72,66,66,66,66,58,98,98,98,114,114,114,114,96,100,100,100,112,112,112,112,100,116,116,116,124,124,124,124,116,116,128,140,140,140,146,146,146,146,162,162,162,162,148,148,138,142,142,142,156,156,152,152,142,168,168,168,172,172,172,172,168,192,188,188,176,176,176,176,182,182,188,188,194,192,198,198,198,198,180,180,180,180,182,182,192,192,196,196,196,182,182,186,194,194,194,194,182,14,14,14,20,20,20,20,32,32,30,30,16,80,80,80,114,114,114,114,106,106,98,98,98,98,82,88,88,88,92,92,92,92,88,100,100,100,106,106,106,106,100,108,90,90,100,100,106,106,106,122,118,118,114,114,114,114,108,108,104,104,104,104,86,84,84,84,72,72,72,72,54,54,54,54,62,62,62,62,78,78,112,112,138,138,138,138,118,134,132,132,126,126,130,130,138,126,126,126,118,124,104,122,120,120,114,114,120,120,128,116,116,116,110,116,116,102,102,102,114,112,112,112,104,104,104,104,100,100,90,102,102,102,94,94,98,98,106,86,98,88,96,92,90,90,82,82,84,84,94,84,80,80,72,80,76,76,66,66,64,72,84,80,80,80,86,80,78,78,66,66,66,66,56,70,86,72,72,72,78,78,80,76,64,64,64,64,60,56,56,56,62,70,70,70,58,60,58,58,58,58,54,54,54,54,78,78,78,78,74,74,72,60,60,66,66,70,70,120,120,120,144,146,146,146,142,142,134,134,132,132,132,124,124,132,132,138,138,62,54,54,48,48,48,48,62,50,50,54,54,58,58,132,142,142,142,142,150,150,150,150,138,144,144,140,140,60,60,60,74,74,84,64,64,64,60,60,58,58,58,58,68,68,68,68,66,66,66,66,70,70,76,76,76,82,98,98,108,106,106,106,116,116,116,116,112,112,112,112,126,126,126,126,122,122,122,122,124,124,134,134,134,134,124,124,102,126,126,126,132,132,152,152,152,152,162,130,130,130,134,134,152,152,152,152,160,160,164,160,160,160,166,166,166,100,100,100,104,104,104,104,108,108,108,108,116,110,110,110,112,112,112,100,100,100,104,100,100,100,96,96,96,96,104,104,104,60,60,60,56,56,56,56,64,106,76,76,76,76,60,60,54,54,54,54,50,50,46,46,42,42,42,42,46,46,70,70,70,70,74,74,76,76,76,68,68,68,72,72,78,78,78,78,74,74,70,48,48,48,42,42,34,34,34,34,44,48,48,48,52,52,52,52,48,62,66,66,66,66,62,62,62,54,50,50,50,50,54,54,54,58,58,58,62,62,62,62,58,54,64,64,70,108,108,108,118,118,122,122,122,122,124,124,130,130,130,130,134,120,114,114,112,112,112,112,118,118,120,120,122,122,126,126,126,126,122,106,106,106,104,104,100,100,92,92,92,92,96,96,100,100,108,74,76,78,86,86,86,86,80,80,74,74,70,70,70,74,74,74,82,82,84,84,92,92,96,96,96,96,94,94,90,90,86,86,82,82,82,74,74,74,78,78,84,46,46,46,42,42,42,42,34,34,30,30,22,22,22,22,18,18,18,18,32,32,32,32,44,44,44,74,74,74,90,90,90,90,86,86,86,86,76,76,76,76,72,138,138,138,142,142,142,142,150,150,150,150,154,154,154,154,150,150,150,150,138,138,138,22,23,22,22,22,46,46,46,46,58,58,58,20,76,78,78,78,78,78,58,58,44,48,48,54,54,58,58,58,58,46,46,46,46,46,50,50,58,58,62,62,62,62,28,28,28,28,42,42,44,44,44,44,66,68,68,68,54,54,54,54,62,62,62,58,58,54,64,60,54,32,32,32,38,38,38,34,34,54,54,54,58,58,58,70,70,70,26,26,26,26,30,30,30,30,50,50,50,50,56,56,56,30,30,30,42,42,42,36,36,36,26,26,26,52,64,64,64,64,46,46,46,46,58,58,58,58,50,50,50,50,62,62,62,62,48,48,48,48,66,88,78,78,74,74,74,74,60,60,60,60,52,52,52,52,58,58,64,64,64,64,68,68,74,74,76,76,90,90,98,98,102,102,112,112,126,126,138,138,94,94,90,90,90,90,50,50,48,88,92,92,92,92,96,96,102,102,102,102,106,106,116,116,118,118,118,118,120,120,122,122,134,134,134,134,130,130,130,130,138,138,140,140,140,140,128,128,128,128,132,132,138,138,138,138,130,108,104,104,104,108,108,104,108,108,118,118,128,128,132,132,140,140,142,142,152,152,154,104,86,86,76,76,58,108,116,116,126,126,136,136,142,142,152,152,154,104,78,78,70,70,68,68,56,108,118,118,122,122,126,126,134,134,136,136,148,104,96,96,88,88,84,84,76,76,70,70,62,102,102,102,98,98,94,94,90,90,90,90,96,96,100,100,100,100,96,96,94,94,100,110,110,110,112,112,118,118,122,122,122,122,118,118,112,110,114,114,116,116,118,118,118,104,105,104,100,100,94,94,90,90,78,78,78,78,92,92,94,94,114,102,102,102,102,102,96,96,84,84,70,70,70,70,78,78,90,90,98,98,98,98,94,94,86,120,120,120,146,146,146,146,142,142,118,118,114,114,114,114,114,108,142,130,130,100,118,102,122,134,134,134,142,142,152,152,152,142,146,146,152,152,152,152,158,16,16,16,32,32,32,32,28,28,26,26,26,26,24,24,20,20,20,20,16,16,16,30,30,30,34,34,36,36,38,38,38,38,42,38,34,32,42,42,44,44,44,44,40,36,36,36,52,52,52,52,48,48,32,32,32,44,68,64,58,58,58,58,48,52,86,82,66,66,90,88,78,88,88,88,86,86,78,78,74,90,204,166,166,166,162,162,158,158,156,156,156,142,142,142,130,130,128,138,138,138,134,134,132,132,124,124,122,122,122,122,124,124,122,122,116,116,116,116,114,114,108,108,108,108,112,112,104,104,112,112,112,112,116,116,118,118,120,120,122,122,124,124,130,130,136,136,146,146,148,148,156,156,160,160,164,164,166,166,170,170,172,172,176,176,178,178,184,184,184,184,182,182,180,180,176,176,176,176,184,184,184,184,180,180,180,180,184,184,184,184,178,178,170,170,164,164,158,158,154,154,154,116,116,116,120,120,126,126,132,148,154,154,162,162,162,162,162,132,132,132,126,126,126,156,160,160,164,164,164,164,166,142,142,156,162,162,162,162,168,164,164,164,156,166,166,166,162,162,162,164,170,170,170,170,166,170,170,170,170,170,174,174,174,174,170,112,112,112,108,108,106,114,116,116,116,112,112,112,112,112,116,112,112,112,112,112,116,116,116,116,112,112,110,110,110,106,106,110,110,110,106,182,182,182,174,180,180,180,188,180,180,180,180,180,180,180,184,184,186,180,176,176,174,174,174,174,178,180,182,184,184,184,180,182,184,176,180,186,186,186,184,184,180,172,172,172,176,178,178,170,172,172,178,178,178,142,146,146,146,146,142,142,146,146,146,146,148,148,148,148,146,146,144,144,140,144,144,144,144,144,138,138,138,148,148,148,152,152,152,152,156,156,156,156,158,148,148,90,28,28,28,32,32,36,36,40,40,44,44,48,48,52,52,56,56,60,60,64,64,68,68,72,72,76,76,80,80,84,84,88,88,92,92,96,96,102,102,98,98,120,120,124,124,128,128,172,172,174,174,184,184,188,188,194,194,198,198,94,94,94,94,94,98,98,98,98,98,36,32,32,32,36,36,32,98,88,88,76,76,64,64,52,52,40,40,48,56,56,56,56,48,48,48,44,44,44,36,36,46,60,68,68,68,68,60,60,60,76,72,72,72,72,82,82,82,82,76,86,90,90,90,90,86,86,86,184,190,190,216,204,204,204,208,208,208,208,204,210,214,214,214,214,210,210,210,206,202,202,200,208,208,208,206,216,214,214,204,208,206,206,194,200,196,196,184,188,176,186,192,192,196,210,200,212,190,190,180,180,148,148,126,126,120,120,110,110,92,92,88,88,88,88,82,82,26,26,20,20,20,14,14,14,26,26,32,32,42,42,52,52,54,54,62,62,64,64,84,84,104,104,112,112,128,128,136,136,140,140,148,148,150,150,158,158,160,160,170,170,174,174,184,184,186,186,188,188,196,196,204,204,210,210,218,70,30,36,36,42,42,42,46,46,46,46,44,44,44,44,42,42,38,60,60,60,66,66,66,62,62,1,2,58,200,186,186,186,98,98,98,106,106,106,114,114,114,114,106,106,106,118,118,118,126,126,126,126,118,130,138,138,138,138,130,130,130,98,102,102,102,98,102,66,66,66,68,64,64,64,62,66,66,66,70,70,70,66,60,60,58,58,58,66,66,66,70,70,70,70,68,68,66,66,62,62,62,62,64,64,66,64,68,70,70,66,66,62,62,70,68,68,66,66,68,68,78,78,80,80,80,80,76,74,68,74,74,78,78,78,82,82,84,84,84,72,72,72,54,82,82,82,62,92,66,66,70,80,104,112,112,112,92,98,100,100,124,132,134,134,116,110,110,110,126,108,80,80,80,82,80,80,78,78,80,80,84,84,86,86,86,84,88,88,90,90,94,94,94,94,96,102,102,102,106,106,102,102,102,104,104,108,114,112,110,110,110,110,104,104,104,106,106,96,94,94,98,98,98,94,90,90,90,90,92,92,96,80,82,82,82,80,78,78,84,74,78,78,78,78,80,78,74,106,106,106,110,110,110,110,114,114,114,114,118,118,118,108,112,104,116,116,106,110,110,110,116,122,122,122,126,126,126,126,130,130,130,130,134,134,134,122,134,122,134,126,122,136,136,136,140,140,144,144,144,144,148,148,148,148,152,152,154,154,154,138,138,138,144,144,144,144,140,142,142,142,146,146,146,132,132,132,130,122,122,122,120,120,120,112,106,152,146,146,146,146,148,148,150,150,150,146,146,146,150,150,150,150,148,148,146,146,148,148,154,154,154,154,158,158,158,158,160,160,160,160,158,158,160,160,160,160,158,158,158,158,160,160,158,158,158,158,160,160,158,158,158,170,164,164,168,168,168,168,162,162,166,166,164,166,168,168,170,170,166,166,162,162,166,166,170,170,174,174,174,174,172,172,172,172,178,178,178,178,174,176,176,186,180,180,180,180,184,184,182,182,182,182,180,180,180,180,178,178,178,178,180,180,184,184,184,184,180,180,178,180,186,186,188,188,190,190,192,192,192,192,190,190,192,192,192,192,194,194,194,194,192,192,192,192,188,188,190,190,190,190,194,186,188,186,184,188,188,188,186,168,168,168,170,170,168,168,170,170,170,170,172,172,172,156,152,152,152,152,154,154,154,154,152,152,152,122,218,218,218,218,122,88,82,82,80,80,80,80,74,74,70,70,66,66,60,60,52,52,42,42,34,34,26,26,8,8,8,8,2,2,2,2,4,4,4,18,18,18,20,20,24,24,24,24,22,22,22,16,22,18,22,22,24,24,24,24,20,20,16,16,16,16,22,22,18,18,18,20,14,14,14,14,14,28,28,28,34,34,34,34,28,28,28,28,34,34,34,30,30,30,34,34,38,38,42,30,30,30,34,28,26,26,24,24,28,32,26,26,30,30,30,30,36,36,34,34,30,30,30,186,186,186,206,206,206,206,202,202,186,206,156,134,114,108,108,108,94,94,94,94,98,36,36,36,28,28,28,28,36,36,36,36,30,42,42,42,50,50,50,58,58,58,54,54,54,54,62,72,72,72,66,66,66,66,74,66,66,78,78,78,86,80,86,86,86,98,92,92,92,92,100,100,100,100,98,112,112,112,116,116,116,116,122,122,122,126,126,126,138,124,136,124,138,142,142,136,150,158,158,150,166,172,172,172,184,184,184,184,172,46,50,50,50,50,44,44,44,48,48,50,54,54,54,54,54,50,52,52,54,54,48,48,46,46,46,46,54,48,50,50,50,50,48,42,38,38,36,36,42,42,44,44,44,44,40,42,42,42,46,38,36,46,46,46,48,48,42,42,42,42,38,38,42,40,28,28,32,32,44,44,42,42,30,46,46,46,48,48,58,58,58,58,52,60,66,66,66,66,70,70,70,70,64,72,72,72,78,78,78,84,88,88,92,92,92,98,98,98,84,106,106,106,112,112,112,112,118,118,118,118,124,124,124,130,130,130,136,136,136,136,130,140,140,140,146,146,146,148,152,152,152,152,152,152,158,158,158,162,162,162,170,166,158,176,172,172,172,172,178,178,178,178,174,182,182,182,182,186,190,190,190,190,190,190,196,196,196,196,202,160,156,156,154,154,160,164,170,170,168,168,168,168,166,166,166,166,160,164,164,164,156,164,168,168,168,168,166,166,164,162,162,162,164,158,158,158,156,156,156,156,154,162,162,166,166,162,166,158,158,158,158,154,154,154,156,170,170,34,44,44,44,44,36,36,36,36,40,14,6,6,6,6,16,16,16,16,6,4,6,16,18,18,18,18,34,38,38,40,48,48,48,48,52,52,52,52,50,50,48,44,40,40,40,40,34,34,34,34,30,30,26,26,22,22,22,22,12,12,12,12,2,34,22,22,18,18,18,10,8,8,2,28,32,28,28,14,14,80,80,80,72,72,72,72,96,96,96,96,92,92,92,92,80,88,88,88,112,112,112,112,96,94,94,94,76,76,74,74,72,72,72,72,62,62,62,62,74,86,86,86,88,88,118,118,118,118,110,118,130,130,130,130,114,114,114,114,112,112,92,128,142,142,142,142,124,124,124,108,108,108,134,134,134,124,124,124,136,136,136,136,148,148,148,148,142,78,78,82,82,86,86,92,92,90,90,78,78,82,82,84,84,88,88,88,88,90,90,78,78,82,82,86,86,90,90,78,78,82,82,86,86,90,90,84,84,84,90,90,90,90,84,64,64,64,68,68,68,68,62,78,78,78,82,70,70,70,74,74,74,74,68,66,66,66,70,70,70,70,66,74,74,74,80,80,80,80,74,64,70,70,70,70,64,64,64,74,74,74,80,80,80,70,64,64,64,64,70,70,70,74,80,80,80,80,74,74,74,82,82,84,84,84,78,66,66,66,72,78,78,74,70,82,82,82,88,88,88,82,82,82,88,88,88,88,82,90,90,92,92,94,94,100,100,100,110,104,104,104,110,104,104,104,106,106,106,102,102,108,110,110,110,110,106,104,102,96,102,102,96,96,102,102,102,90,90,88,88,92,92,96,96,102,102,96,96,100,100,94,94,94,94,100,100,96,96,100,100,102,102,106,106,106,106,110,110,110,110,110,110,106,106,106,106,102,102,108,108,112,112,116,116,112,112,116,116,112,112,116,116,120,120,120,126,126,126,126,120,120,120,120,126,126,126,126,120,120,120,120,126,126,126,120,118,114,114,114,118,118,118,118,112,120,120,120,128,128,128,128,118,114,126,126,126,126,114,114,114,114,114,114,118,118,118,114,112,106,110,110,110,110,104,104,104,98,108,108,108,108,98,98,98,116,116,116,120,120,120,120,116,124,124,124,130,130,130,130,126,110,110,110,114,114,114,110,116,130,130,130,130,130,130,134,134,134,134,134,134,134,134,138,138,138,138,138,138,138,138,126,126,126,126,126,126,126,126,138,138,138,142,142,142,142,140,140,140,140,138,128,128,128,132,132,132,132,130,130,130,130,128,136,140,140,140,140,136,136,136,138,138,138,142,142,142,142,140,142,144,144,144,144,148,134,134,134,140,140,140,138,134,134,134,134,138,144,144,144,148,132,136,136,136,136,130,130,134,130,128,138,138,138,142,142,142,142,136,130,134,134,134,134,128,138,138,138,144,144,144,144,138,138,138,140,140,140,146,146,146,146,142,146,146,146,142,146,142,142,140,140,140,142,136,136,136,136,132,106,96,72,72,72,66,122,122,122,118,98,98,98,68,68,66,80,82,82,112,18,18,18,24,24,24,24,36,36,40,40,40,40,26,26,34,34,34,34,16,24,24,24,30,30,30,30,22,44,44,44,58,58,58,58,48,48,48,48,54,54,54,54,50,50,50,50,58,58,58,58,42,42,42,80,68,68,60,60,60,60,62,62,66,66,80,80,80,80,72,66,64,64,68,68,72,72,72,80,82,86,86,86,92,92,92,92,98,98,98,98,104,104,104,104,98,98,98,92,90,90,90,90,86,86,86,86,82,110,110,110,122,122,122,122,116,116,116,116,112,112,112,112,108,138,138,138,146,146,152,152,152,152,158,158,158,158,152,152,152,152,142,142,142,142,138,138,138,144,144,144,154,154,154,154,142,164,164,164,170,170,174,174,174,174,170,170,170,170,166,166,166,166,162,178,178,178,182,182,182,182,186,186,186,186,182,182,182,182,194,194,194,194,176,48,38,38,28,28,28,28,32,32,34,34,34,34,26,26,22,22,18,54,54,54,60,60,62,62,66,58,50,116,124,124,128,128,128,128,126,126,114,114,114,114,116,116,122,122,134,160,160,160,154,154,150,150,140,140,140,140,146,146,152,152,164,164,164,190,190,190,202,202,204,204,210,210,210,192,184,36,42,48,48,48,44,44,80,80,80,80,70,70,58,58,54,54,54,54,42,42,40,12,34,34,58,58,72,72,98,98,98,98,74,74,38,38,8,8,8,8,22,22,30,30,52,52,62,62,68,68,68,68,52,52,34,34,32,32,32,32,22,22,10,10,2,2,2,2,10,10,22,10,6,6,6,6,16,16,28,28,40,40,44,44,76,76,90,90,90,90,86,90,92,92,138,138,140,140,158,158,158,158,138,142,148,148,148,148,134,134,154,154,154,154,122,122,116,116,116,116,170,170,170,170,144,144,136,136,128,128,128,128,136,136,136,136,112,112,98,98,98,98,110,110,110,110,98,72,62,62,50,50,48,48,48,48,62,62,66,66,68,68,68,68,62,52,42,42,40,40,64,120,120,120,96,96,96,96,104,104,122,122,122,122,130,130,130,132,150,150,156,156,156,156,152,152,132,132,126,126,126,126,144,144,144,144,132,132,132,132,126,126,74,74,72,72,72,72,82,82,84,84,86,86,86,86,90,90,92,92,102,102,130,130,162,162,164,164,166,166,166,166,170,170,202,202,208,208,208,208,196,196,196,196,208,208,208,208,198,198,198,198,214,214,214,214,212,212,210,210,196,196,192,192,192,186,176,176,164,164,168,168,174,174,178,178,178,178,174,174,172,172,172,172,172,172,136,136,130,130,106,106,104,104,96,96,10,10,2,2,2,2,12,12,14,14,20,20,20,20,22,22,34,34,46,46,50,50,66,66,72,72,90,90,92,92,120,120,122,122,134,134,144,144,164,164,168,168,168,168,166,166,136,136,116,116,110,110,102,102,90,90,84,84,66,66,58,58,46,46,14,14,4,4,4,4,26,26,56,180,180,204,204,204,190,190,190,190,200,206,208,208,208,208,200,200,182,182,174,174,174,174,178,178,178,178,168,168,164,164,134,144,144,144,134,134,130,130,118,120,128,120,118,118,118,118,108,108,110,110,120,120,120,120,110,110,126,126,128,128,152,152,154,154,154,154,140,140,140,140,156,156,160,160,174,174,174,174,176,176,178,178,190,190,202,190,182,182,182,182,190,190,172,172,170,170,170,170,174,174,176,178,182,182,184,58,46,46,22,14,12,12,12,12,26,28,30,30,46,46,58,58,72,72,72,72,78,78,116,116,116,116,104,104,104,104,124,160,160,160,158,158,150,150,144,134,124,124,110,110,110,110,106,106,102,102,102,102,106,106,108,108,110,110,106,106,104,104,100,100,100,100,116,116,154,154,164,178,208,208,208,208,208,218,218,216,216,216,214,214,186,186,130,130,110,50,36,36,12,24,2,2,2,44,50,50,64,64,48,10,10,10,42,42,42,42,36,36,36,36,54,54,54,54,106,106,106,106,108,108,158,158,152,152,152,152,158,158,156,156,156,156,168,168,168,168,150,150,190,190,190,190,218,136,130,128,124,124,124,124,134,134,118,118,118,118,122,122,128,128,128,128,114,114,114,114,122,122,122,122,112,112,112,112,118,118,118,118,104,104,104,104,118,112,110,120,126,126,126,120,112,112,112,112,122,122,126,122,114,114,108,108,108,108,118,118,114,114,118,118,116,116,118,118,118,118,116,116,118,118,118,118,116,116,118,118,116,116,116,116,114,114,114,114,112,112,112,112,110,110,110,110,108,108,108,108,110,110,106,106,108,108,106,106,106,106,108,108,108,108,106,106,110,108,108,108,106,106,106,106,108,108,106,106,106,106,110,110,108,106,106,106,110,110,106,106,108,108,108,108,106,106,108,108,108,108,106,106,108,108,108,108,106,106,108,108,108,108,106,106,108,108,106,106,106,106,104,104,106,104,106,106,104,104,104,104,102,102,102,102,100,100,100,98,102,102,102,102,100,102,80,80,80,80,84,84,96,96,100,100,74,92,98,98,98,98,64,62,68,68,82,82,82,82,82,72,68,68,68,68,96,94,88,88,88,88,90,90,114,114,124,124,126,126,126,126,154,154,154,154,148,148,148,148,140,140,144,144,142,142,144,144,142,142,140,140,142,142,138,138,140,138,138,138,136,136,136,136,134,134,134,134,132,132,134,134,132,132,132,132,130,130,130,130,132,132,130,130,132,132,138,138,126,118,124,124,132,132,130,130,130,130,132,132,130,130,132,132,132,132,130,130,130,130,136,132,136,136,132,132,136,136,132,132,134,134,136,134,132,22,22,22,18,18,16,16,16,16,20,20,22,22,24,24,26,18,18,18,14,14,14,14,18,18,24,24,24,24,28,28,28,28,32,32,32,32,30,30,30,28,28,28,22,22,22,22,24,24,28,28,28,28,30,30,56,56,58,58,58,58,60,60,66,66,66,66,62,62,56,56,54,54,50,50,44,44,44,44,38,38,34,34,30,30,26,26,26,26,66,26,28,28,44,44,46,46,64,42,42,42,48,48,48,66,74,74,78,78,78,78,82,82,84,84,86,86,90,90,100,96,96,96,100,100,100,100,102,102,102,102,100,100,96,96,96,96,96,96,90,90,90,90,86,92,92,92,86,86,86,86,82,82,82,82,84,84,84,84,82,82,74,74,70,70,70,70,68,68,64,64,58,58,58,46,46,46,38,38,34,34,34,34,32,32,28,28,28,28,26,26,22,22,28,28,32,28,34,34,34,34,38,38,38,38,44,44,48,48,48,48,44,44,44,58,58,58,56,56,52,52,52,52,58,58,58,58,64,64,64,64,70,70,70,70,76,76,76,76,70,70,68,68,68,68,72,72,72,28,38,38,38,38,28,28,28,32,32,56,60,60,60,60,48,48,48,48,58,56,54,54,54,42,46,46,46,46,40,40,40,40,42,44,44,44,48,48,48,48,50,50,50,48,46,44,36,36,34,26,26,62,62,62,62,62,64,64,64,64,58,58,56,56,56,56,42,42,42,42,38,38,38,38,46,46,46,46,50,50,50,50,54,54,54,54,62,48,52,36,36,42,42,46,46,48,52,52,52,62,68,60,66,24,18,26,20,24,24,18,18,104,104,104,110,110,110,104,112,108,108,108,118,118,120,120,124,124,124,124,112,112,112,112,118,130,130,130,152,148,148,148,134,134,134,134,138,138,138,138,134,134,134,134,150,150,150,150,128,154,154,154,176,176,176,176,158,158,158,158,162,162,162,162,158,158,158,158,176,176,176,176,150,180,180,180,188,188,188,188,202,202,202,202,178,186,194,124,130,130,130,130,122,122,122,136,158,158,158,158,148,148,148,148,140,140,140,140,130,130,130,130,134,90,90,90,100,100,100,100,104,104,104,104,108,108,108,108,112,112,112,112,124,124,124,124,120,120,120,120,114,114,114,114,106,90,126,126,126,126,98,104,118,130,130,130,152,152,152,152,136,136,136,136,142,142,142,142,136,136,136,136,154,154,154,154,128,158,158,158,172,172,172,172,172,172,182,182,182,182,156,188,188,188,216,216,216,216,194,194,194,194,188,162,172,170,188,188,188,188,168,168,168,114,98,98,98,98,116,116,116,84,84,90,90,2,4,4,200,200,218,218,218,218,26,26,4,218,214,214,18,18,2,2,2,2,194,194,214,110,110,110,110,110,98,98,98,98,130,130,130,130,104,98,2,120,218,100,110,110,102,102,102,102,110,110,104,104,104,104,110,110,104,104,104,104,108,62,18,18,18,18,52,52,52,52,62,62,62,62,50,50,50,50,62,46,10,10,10,10,2,2,2,2,10,10,10,10,2,2,2,2,8,8,12,12,16,16,16,16,8,8,6,6,4,4,4,4,18,18,18,18,4,4,4,4,16,16,16,16,10,10,2,2,2,2,14,18,42,44,44,44,50,50,52,52,68,68,70,70,86,86,104,106,124,124,126,126,144,144,162,162,164,164,182,182,184,184,204,204,218,218,218,218,218,218,196,196,218,218,218,218,192,192,218,218,194,194,194,194,218,218,218,218,192,192,174,174,150,150,148,148,124,124,110,110,84,84,58,58,46,46,20,20,2,2,2,2,2,2,74,74,74,74,130,130,158,158,184,184,184,184,160,160,156,156,126,160,160,160,162,162,174,174,180,180,180,180,178,178,162,162,148,148,148,148,164,164,166,166,190,190,162,162,158,158,126,126,126,126,160,160,210,210,210,210,164,164,150,150,74,74,72,72,40,40,40,40,126,126,126,126,104,104,92,92,54,54,54,54,90,90,94,94,170,170,170,170,146,146,126,126,32,32,32,32,102,102,110,110,194,194,194,194,130,130,112,112,38,38,38,38,110,110,126,158,170,170,156,136,118,118,74,74,116,184,198,198,198,198,186,186,186,186,196,174,162,162,162,162,178,162,162,162,176,48,168,168,168,168,48,50,50,66,66,66,82,82,82,92,92,92,112,110,104,104,104,118,118,118,130,130,130,142,142,142,154,154,154,46,166,166,166,166,50,50,50,50,166,166,166,166,54,54,54,54,162,162,162,162,50,50,50,62,62,62,70,70,70,82,82,82,90,90,90,98,98,98,122,122,122,132,132,132,138,138,138,146,146,146,154,154,154,168,52,52,52,52,170,170,170,44,44,44,44,30,30,30,68,68,68,68,94,94,94,94,64,64,64,64,94,94,104,104,104,104,40,40,40,40,50,50,50,50,78,78,78,78,154,154,154,154,88,88,88,88,162,162,162,162,98,22,34,34,34,34,38,38,38,38,50,50,50,50,60,60,78,78,84,84,84,84,86,86,90,90,96,96,102,102,106,106,106,106,108,108,110,110,114,114,122,122,122,122,138,138,144,144,154,154,154,154,156,156,156,156,162,162,162,162,164,164,168,168,168,168,170,170,172,172,176,176,176,176,180,180,180,180,176,176,176,176,172,172,172,172,168,168,168,168,164,164,164,164,160,160,160,160,154,154,154,154,120,120,120,120,100,100,100,100,94,94,94,94,80,80,76,76,76,76,68,68,68,68,60,60,60,60,56,56,52,52,52,52,50,50,50,50,46,46,32,32,32,32,28,28,28,28,26,26,26,26,22,22,22,22,18,18,18,22,22,22,18,18,18,24,24,24,26,26,28,28,28,28,32,32,32,32,36,36,36,36,42,42,42,42,46,46,46,46,50,50,50,50,54,54,54,54,58,58,58,58,62,62,62,62,82,82,82,82,86,86,90,90,90,90,94,94,94,94,98,98,100,100,102,102,102,102,104,104,112,112,116,116,116,116,128,128,134,134,134,134,138,138,138,138,146,146,146,146,166,166,166,166,176,38,38,38,42,42,42,42,46,46,46,46,50,50,50,50,54,54,54,54,58,58,58,58,62,62,62,62,66,66,66,66,70,70,70,70,88,88,88,88,92,92,92,92,96,96,100,100,104,104,104,104,110,110,112,112,114,114,116,116,116,116,118,118,120,120,122,122,122,122,128,128,130,130,132,132,136,136,136,136,138,138,142,142,142,142,146,146,150,150,154,154,154,154,150,150,150,150,148,148,148,148,146,146,144,144,132,132,130,130,126,126,124,124,98,98,98,98,88,88,78,78,70,70,70,70,66,66,66,66,58,58,58,58,54,54,52,52,52,52,50,50,46,46,46,46,42,62,62,62,66,66,66,66,62,66,66,66,70,70,70,70,74,74,74,74,70,70,70,70,74,74,74,74,70,74,74,74,78,78,78,78,74,74,78,78,78,78,74,74,74,80,80,80,84,84,84,84,80,84,84,84,88,88,88,88,84,80,80,80,84,84,84,84,80,86,86,86,90,90,90,90,86,90,90,90,94,94,94,94,90,94,94,94,98,98,98,98,94,94,98,98,98,98,94,94,94,96,96,96,100,100,100,100,96,104,104,104,108,108,108,108,104,104,108,108,108,108,104,104,104,114,110,110,110,110,114,114,114,118,118,118,122,122,122,122,118,118,122,122,122,122,118,118,118,126,126,126,132,132,132,132,124,124,128,128,128,128,124,124,124,128,124,124,124,124,128,128,128,132,132,132,136,136,136,136,132,132,136,136,136,136,132,132,132,136,136,136,140,140,140,140,136,140,142,142,146,146,142,142,142,140,140,140,136,136,136,136,140,144,148,148,150,150,146,146,146,146,142,142,146,146,142,142,142,148,152,148,148,148,152,152,152,22,18,18,18,18,14,14,14,14,18,18,22,22,22,94,94,94,100,100,100,100,100,100,108,108,108,108,112,112,116,116,116,116,108,108,108,94,98,98,110,122,126,126,126,126,134,134,134,134,138,138,138,138,134,134,134,134,126,126,126,126,122,122,122,126,126,126,134,134,134,134,126,144,144,144,148,148,148,148,158,158,158,158,162,162,162,162,158,158,158,158,148,146,146,146,142,168,168,168,172,172,172,172,176,176,178,178,182,182,182,182,186,186,186,186,174,174,174,174,170,188,188,188,194,194,194,194,188,188,188,188,194,194,194,194,188,192,102,102,102,102,194,194,194,194,110,110,110,110,196,192,192,192,120,120,120,120,194,194,194,194,194,194,148,148,148,148,194,190,158,188,188,18,19,18,18,18,8,8,8,8,32,32,32,32,14,14,14,14,32,40,40,40,56,64,64,64,76,102,102,90,110,118,118,118,124,124,124,132,140,140,140,140,132,132,132,156,150,150,150,150,160,160,160,160,150,166,166,166,176,176,164,164,174,58,58,58,58,58,66,66,68,68,68,68,76,76,76,76,80,80,80,92,102,102,102,102,92,92,92,92,96,108,108,108,114,114,116,116,116,116,122,122,122,122,130,130,130,134,134,134,142,142,142,142,132,144,144,144,150,150,150,150,158,158,158,158,162,162,162,174,166,166,166,166,174,166,174,180,180,180,180,180,186,186,186,186,190,190,190,190,194,194,194,202,202,202,202,194,206,218,210,210,210,210,216,216,216,216,210,16,16,16,22,22,22,22,30,30,30,30,34,34,34,34,40,40,40,40,46,46,46,58,58,58,50,58,62,62,52,68,68,68,76,82,82,82,90,100,100,100,100,100,110,110,110,110,104,104,104,104,110,110,110,110,98,114,122,114,112,112,112,112,118,118,110,104,98,132,132,132,140,148,158,158,158,158,150,150,150,176,168,168,168,168,178,178,178,178,168,192,192,192,202,192,186,58,58,58,58,58,66,58,48,58,50,58,66,74,74,74,80,80,80,80,86,86,86,86,92,92,92,112,112,102,114,126,126,126,118,126,118,126,128,116,106,130,126,126,126,126,124,140,140,140,140,140,144,144,144,144,150,150,150,150,152,152,156,156,156,172,162,162,162,162,168,168,160,28,28,28,36,46,40,46,54,46,46,46,54,46,42,66,66,66,76,76,78,78,78,72,72,72,78,78,78,86,86,86,96,88,88,88,92,92,94,94,86,86,86,86,88,88,94,116,116,116,110,116,122,132,126,126,126,126,134,126,134,150,144,144,144,144,138,138,138,138,138,138,154,148,148,154,156,156,156,164,164,164,172,172,172,172,164,170,170,170,174,196,186,186,186,186,194,194,194,194,188,54,54,54,50,54,60,50,46,54,54,54,42,62,54,70,70,70,76,76,76,76,84,84,84,84,90,90,90,114,114,114,114,114,104,114,116,110,110,116,104,106,106,130,122,122,122,122,134,122,126,126,130,114,106,114,114,126,126,126,118,118,118,118,130,130,130,130,126,130,132,132,132,132,140,140,140,152,152,152,144,152,160,154,154,154,142,154,162,176,176,176,182,182,182,182,190,190,190,190,196,196,196,54,54,54,72,72,72,72,58,56,42,42,42,42,30,30,30,30,50,50,54,54,54,54,58,58,70,70,70,70,74,74,94,94,94,94,80,80,80,80,76,72,76,110,110,110,118,118,118,118,126,126,126,126,136,136,136,136,146,146,146,146,150,150,162,162,162,162,166,166,166,166,156,156,156,156,162,162,162,162,142,142,142,142,142,142,130,130,120,118,118,118,108,108,104,104,104,104,106,104,96,96,94,94,94,94,106,90,90,90,104,104,104,104,98,98,98,98,92,92,92,92,86,86,86,86,86,86,112,112,112,112,118,118,118,118,130,130,138,138,138,138,146,146,146,146,140,140,136,136,136,130,130,130,118,118,118,146,168,168,168,168,162,162,162,162,152,152,152,152,146,146,146,150,164,164,164,164,150,150,150,166,166,166,174,174,174,174,174,174,184,184,184,184,192,192,192,192,200,200,200,200,190,190,190,190,172,172,172,172,164,164,164,2,10,98,98,98,86,86,86,86,120,120,120,120,112,112,112,112,102,102,102,102,100,100,100,100,94,94,94,86,122,122,84,90,90,94,94,88,92,100,100,100,94,96,96,96,96,96,104,104,104,108,108,108,112,108,114,114,114,114,118,118,126,112,112,112,118,52,18,18,18,18,24,24,52,52,62,62,62,62,42,42,14,68,68,68,94,94,106,106,106,106,90,90,66,106,106,106,122,122,122,122,130,130,130,130,138,138,138,138,148,148,148,106,160,128,118,118,106,114,120,120,120,120,120,120,114,114,114,118,118,144,144,144,154,154,154,154,152,152,146,158,158,158,168,168,168,168,174,174,174,174,182,182,184,184,184,184,172,172,156,156,156,156,168,168,168,170,154,190,190,190,190,190,190,190,196,196,196,196,194,194,186,186,186,186,192,192,188,192,192,192,186,126,126,126,102,102,98,98,94,94,94,94,102,102,104,104,108,108,108,108,110,110,112,112,116,116,116,116,112,112,110,112,114,114,114,114,110,114,114,114,112,112,110,112,120,120,126,126,124,124,122,122,122,122,120,120,118,118,116,116,116,116,118,118,118,118,112,112,110,110,108,108,106,106,106,106,108,108,106,106,104,104,98,102,106,106,106,106,96,100,102,102,104,104,106,106,108,108,108,108,114,114,114,116,116,160,198,198,198,198,192,192,186,186,176,172,162,162,158,158,156,156,156,156,164,160,160,160,190,190,190,162,174,176,178,180,182,184,186,188,184,178,176,172,170,166,162,166,168,166,168,172,174,178,182,188,192,18,2,2,2,2,18,24,24,24,32,32,32,40,40,40,46,46,46,46,42,46,46,46,42,42,42,50,42,44,44,44,54,54,54,54,60,60,60,60,54,54,54,68,80,68,64,64,64,64,76,76,78,78,78,78,68,68,56,14,14,14,16,16,16,16,18,18,18,18,26,26,26,26,30,30,30,30,34,34,34,34,40,40,40,40,42,42,42,42,46,46,46,46,48,48,50,50,50,50,52,52,52,52,56,56,56,56,62,62,62,62,66,66,66,66,68,206,206,206,202,202,202,202,198,198,198,198,196,196,194,194,194,194,184,184,184,184,176,176,176,176,168,168,168,168,160,160,160,160,154,154,154,154,148,148,148,148,144,144,144,144,68,66,64,22,22,22,26,26,26,26,34,34,40,40,40,40,48,48,48,48,40,40,40,40,52,52,52,52,46,46,46,46,58,58,58,58,52,52,52,52,64,64,64,64,66,66,74,74,74,74,80,86,106,106,110,110,118,118,118,118,120,120,134,134,140,140,140,140,148,148,148,148,156,156,156,156,158,158,158,158,162,162,162,162,170,170,170,170,174,174,174,174,182,182,182,182,190,190,190,180,180,180,172,172,172,172,166,166,166,166,160,160,160,160,154,154,154,154,156,156,156,156,148,148,148,148,138,138,138,138,134,134,134,134,128,128,120,120,120,120,106,106,102,102,88,88,88,88,84,84,76,76,76,76,82,82,82,82,68,68,68,68,60,60,60,60,66,66,66,66,58,58,58,58,50,50,50,50,58,58,58,58,48,48,48,48,50,50,50,50,40,40,40,40,26,26,26,80,84,84,84,84,88,88,88,88,92,92,92,92,96,96,96,96,84,88,88,88,82,82,82,80,78,114,114,114,110,110,110,110,116,116,116,116,118,118,122,122,122,122,130,130,130,130,134,134,122,122,114,114,114,114,114,78,86,86,86,86,92,92,92,92,88,88,84,84,74,74,72,72,70,70,70,70,74,82,82,82,82,82,74,74,74,104,108,108,112,112,112,112,118,118,120,120,120,120,118,118,104,104,104,104,108,116,116,116,116,116,110,90,84,84,84,84,80,80,76,34,34,34,52,52,52,52,60,60,60,60,56,56,56,56,68,68,68,68,72,72,78,78,88,88,90,90,96,96,96,96,98,98,106,106,114,114,118,118,126,126,130,130,138,138,138,138,132,132,132,132,142,142,142,142,150,150,150,150,156,156,156,156,164,164,164,52,52,52,50,52,54,54,58,58,58,58,62,62,62,62,70,70,70,70,62,62,62,62,76,76,84,84,84,84,74,74,86,86,92,92,94,94,100,100,100,100,104,104,108,108,110,110,112,112,116,116,138,138,138,138,136,136,136,136,146,146,146,146,138,138,154,154,154,154,148,66,66,66,72,72,72,72,66,66,66,66,74,74,74,74,70,70,70,70,78,78,78,78,86,86,104,104,120,120,120,120,130,130,130,130,126,126,126,126,136,136,136,136,128,128,128,128,138,138,138,110,92,92,78,78,78,78,70,70,70,70,76,76,76,76,78,78,94,94,120,120,120,120,128,128,128,128,108,108,108,132,132,132,116,116,88,88,78,78,74,74,72,72,82,82,72,72,72,86,86,86,88,88,96,96,106,106,122,122,128,128,134,116,116,116,98,98,98,98,90,90,90,90,86,14,128,134,148,154,206,186,186,186,164,164,164,164,170,170,174,174,180,180,180,180,182,182,198,198,200,200,212,212,212,212,186,176,176,184,184,184,182,180,178,178,178,178,184,190,190,170,170,172,172,166,178,166,178,6,6,10,10,14,14,16,16,20,20,24,24,28,28,30,30,34,34,38,38,42,42,44,44,44,44,44,44,48,48,52,52,56,56,60,60,62,62,102,102,102,106,106,118,148,148,150,150,154,154,154,154,154,154,158,158,162,162,162,162,166,166,166,166,170,170,170,170,174,174,174,174,174,174,178,178,178,178,182,182,182,182,186,186,186,186,186,186,188,188,188,188,192,192,192,192,192,192,194,194,194,194,198,198,198,198,194,194,200,200,200,200,200,200,204,204,204,204,204,204,210,210,210,210,210,210,212,212,212,212,216,216,14,14,20,20,34,28,28,28,28,36,36,36,42,48,48,48,48,42,42,42,42,50,50,56,56,56,56,50,50,50,50,56,66,66,66,70,70,70,70,66,66,66,70,78,78,78,78,70,70,70,70,76,76,84,80,80,80,86,94,94,94,100,100,100,96,96,96,102,106,110,110,110,110,104,104,104,104,110,114,118,118,118,118,114,114,114,114,114,118,118,118,124,124,124,130,136,136,136,136,130,130,130,130,136,148,148,148,154,154,154,146,146,146,154,158,158,158,162,162,162,166,166,166,170,170,170,174,180,180,180,180,174,174,174,174,178,182,192,186,186,186,192,14,6,6,6,6,10,10,10,18,22,22,22,22,16,16,16,16,22,26,30,30,30,30,24,24,24,24,28,38,42,42,42,42,36,36,36,36,36,40,48,48,48,48,42,42,42,42,48,44,54,52,52,52,58,66,70,70,70,70,66,66,66,66,70,74,74,74,78,78,78,84,80,80,80,80,84,88,88,88,92,92,92,102,102,102,106,106,106,100,108,112,116,116,116,116,110,110,110,110,116,120,120,120,120,120,124,124,124,124,118,124,124,124,128,128,128,138,130,130,130,130,134,134,134,134,130,144,150,150,150,150,144,144,144,144,148,152,152,152,156,166,160,160,160,156,164,170,174,174,174,174,170,170,170,170,170,174,174,174,178,178,178,188,180,180,180,180,186,186,186,190,190,190,194,194,194,198,198,198,202,202,202,212,204,204,204,204,212,212,212,208,208,208,202,202,202,202,214,214,214,214,210,18,18,18,12,12,12,12,20,24,30,30,30,30,24,24,24,24,32,40,34,34,34,34,40,40,40,40,34,50,50,50,52,54,54,54,48,48,48,62,54,54,54,54,60,60,60,60,54,62,70,66,66,66,70,70,70,82,86,86,86,86,82,82,82,82,86,90,90,90,90,90,94,94,94,94,88,92,92,92,96,96,96,100,100,100,106,106,106,102,96,104,110,110,110,110,104,104,104,104,110,118,112,112,112,112,118,122,122,122,126,126,126,134,134,134,138,138,138,142,142,142,148,148,148,152,152,152,156,30,30,30,36,36,36,36,18,18,18,18,30,30,30,30,24,24,24,24,18,18,18,18,38,38,38,38,24,24,24,24,32,46,56,56,56,56,42,42,42,42,58,58,58,58,48,48,48,60,60,60,64,64,64,64,70,70,70,70,76,76,76,76,70,70,70,70,64,64,64,64,60,94,82,82,82,82,98,98,98,98,86,86,86,86,92,92,92,92,86,86,86,86,98,98,98,98,94,104,104,104,108,108,108,108,102,124,124,124,130,130,130,130,110,110,110,110,122,122,122,122,116,116,116,116,110,110,110,110,126,126,126,126,114,114,114,114,126,126,126,136,144,144,144,144,148,148,148,148,132,132,132,132,142,142,142,142,136,136,136,136,130,130,130,130,146,146,146,146,138,138,138,160,170,170,170,170,154,154,154,154,170,170,170,170,158,158,158,158,164,164,164,164,158,158,158,178,178,178,182,182,182,182,176,182,176,176,176,176,180,180,180,194,194,200,200,58,66,66,66,66,58,58,58,74,74,74,82,82,74,88,88,88,96,100,100,100,108,108,108,122,114,114,114,114,122,122,122,122,118,126,126,126,134,134,134,134,126,130,130,130,126,148,138,138,138,138,146,146,138,150,150,158,158,158,166,16,16,16,58,58,100,100,138,138,138,138,10,74,74,74,86,86,106,106,122,122,122,122,126,126,164,164,170,170,170,170,168,168,130,130,130,130,160,160,160,160,140,140,140,140,156,156,156,156,146,146,146,146,148,156,182,182,184,184,184,184,74,74,62,62,62,62,64,64,108,108,114,114,114,114,106,106,72,72,64,64,64,64,70,70,122,122,126,126,126,126,120,120,54,54,50,50,50,50,74,74,194,194,200,200,200,200,206,206,206,206,196,196,196,196,174,174,170,170,170,170,138,138,128,128,128,128,156,156,156,156,138,138,134,134,134,134,136,136,138,142,150,150,170,170,176,176,176,176,178,178,184,184,206,206,206,206,212,212,212,212,106,106,106,106,96,96,96,96,90,90,66,66,58,58,54,54,54,54,38,38,38,38,64,64,64,64,84,84,92,92,56,56,56,56,32,32,32,32,26,26,26,26,16,16,16,16,26,26,26,26,4,4,4,4,2,4,14,14,14,14,28,28,28,28,10,10,10,10,110,110,110,110,116,110,24,24,24,24,182,182,182,182,216,216,216,216,202,202,202,202,190,190,190,190,202,202,202,202,208,208,208,208,216,216,216,216,218,218,218,218,208,208,208,208,214,214,214,214,198,198,198,198,188,188,186,186,186,186,164,164,164,164,168,168,168,168,156,156,156,156,108,108,106,106,98,98,96,96,86,86,80,80,78,78,70,70,66,66,58,58,56,56,48,48,46,46,38,38,36,36,28,28,26,26,18,18,16,16,14,14,14,14,2,2,2,2,22,22,22,38,28,28,18,18,18,18,30,30,34,34,42,42,42,42,36,34,26,26,24,24,22,22,22,22,26,26,26,26,30,30,36,36,38,38,38,38,36,36,32,46,46,46,58,58,58,58,62,62,72,72,76,76,76,76,86,86,86,86,82,82,78,78,78,78,74,74,70,70,56,56,48,48,46,46,46,70,62,62,60,60,60,60,66,66,70,70,70,70,68,92,112,112,122,122,122,122,116,116,100,100,98,98,98,98,96,96,88,88,88,88,92,92,92,98,98,98,112,112,112,112,106,106,98,124,124,124,134,134,140,140,146,146,146,146,136,136,136,136,132,132,126,126,126,126,130,130,130,130,128,128,124,124,124,156,152,152,150,150,150,150,156,156,158,158,160,160,160,160,156,154,152,166,166,166,172,172,174,174,174,174,194,194,194,194,186,186,186,186,178,176,176,176,170,170,168,168,168,168,164,182,182,182,202,202,202,202,190,190,190,190,200,200,202,202,202,202,198,198,194,194,190,190,190,190,192,192,200,200,202,202,202,202,196,196,182,200,216,216,216,216,212,212,212,212,206,206,206,206,202,202,202,218,218,218,214,214,210,210,210,210,212,212,212,212,202,202,200,200,200,200,206,206,208,206,198,198,198,198,204,204,206,206,210,210,210,210,208,208,208,208,210,210,218,218,218,218,214,214,214,214,208,208,208,208,206,206,202,202,202,202,206,206,206,206,210,210,212,212,212,212,212,214,214,214,218,218,218,218,198,198,198,198,190,190,190,190,196,196,200,200,200,200,194,200,200,200,196,124,108,108,88,88,88,88,100,100,130,130,144,144,144,144,124,148,148,148,142,148,148,148,144,144,144,144,128,128,100,100,90,90,90,92,94,94,94,94,106,106,118,118,132,132,132,148,148,148,124,124,122,122,118,110,112,112,120,110,110,110,114,114,124,124,124,124,126,126,130,110,110,110,114,114,114,114,106,106,106,106,102,102,102,106,106,106,110,110,110,110,106,106,112,112,112,112,114,114,118,118,118,118,118,118,126,120,120,120,116,116,116,116,118,118,122,128,118,118,116,120,120,120,124,126,136,136,120,120,132,132,136,136,126,126,118,114,106,106,102,102,102,102,104,104,110,110,114,114,114,114,114,114,112,106,106,106,112,114,116,116,116,116,108,108,104,104,100,100,100,100,106,110,116,116,108,108,102,102,102,102,106,110,116,116,118,118,118,118,116,116,114,120,120,120,114,114,114,114,118,118,118,158,158,158,170,170,190,190,210,210,210,210,194,194,190,190,170,170,174,174,180,180,194,194,202,202,202,202,184,184,178,178,162,162,158,158,156,156,156,156,160,178,190,190,196,196,196,146,142,142,136,136,136,136,134,134,116,116,110,110,110,110,108,108,100,100,94,94,94,94,92,92,88,88,78,78,72,72,66,66,66,66,64,64,62,62,50,50,46,46,40,40,40,40,40,40,36,36,32,32,30,30,30,30,34,28,34,34,68,68,72,68,66,66,40,40,36,36,2,2,2,2,26,12,12,12,24,24,26,26,30,30,30,30,26,40,50,50,72,72,72,72,68,78,78,78,74,78,78,74,74,74,78,72,72,196,196,196,202,208,218,218,218,162,160,160,154,154,154,154,158,158,160,160,162,162,162,162,154,154,150,150,150,150,150,150,146,146,142,142,142,142,138,134,126,126,120,120,120,120,110,110,108,108,98,98,96,96,88,88,86,86,78,78,74,74,64,64,62,62,52,52,50,50,40,40,20,20,14,14,14,14,10,10,2,16,16,16,22,22,30,30,38,38,42,42,54,54,56,56,64,64,66,66,68,174,178,178,190,172,172,172,182,182,182,182,186,218,218,218,180,180,180,180,194,194,208,208,218,218,218,218,218,2,3,2,16,16,16,16,10,10,8,8,2,2,2,2,8,8,12,12,12,12,8,8,6,6,4,4,4,4,18,18,18,18,32,32,34,34,46,46,46,46,42,42,36,36,32,32,32,32,36,36,48,48,54,54,54,54,44,44,42,44,68,68,74,74,74,74,58,58,54,54,54,54,68,68,72,72,72,72,74,74,96,96,98,98,98,98,96,96,16,16,12,12,12,12,12,12,10,10,2,2,2,2,20,20,28,28,32,32,32,32,22,22,22,22,26,26,36,36,42,42,42,42,44,44,62,62,64,64,64,64,58,58,46,46,42,42,40,40,38,38,38,38,56,56,62,62,70,70,74,74,74,74,66,66,66,66,78,78,92,92,106,106,106,106,94,94,84,84,76,76,72,72,70,70,66,66,66,66,68,68,68,68,66,66,62,58,58,58,62,62,62,62,48,48,38,38,36,36,36,36,32,32,24,24,24,24,30,30,30,30,24,24,18,18,14,14,14,14,24,24,18,18,18,18,28,28,28,28,50,50,50,50,60,60,62,62,72,72,76,76,86,86,92,92,110,110,110,110,128,128,128,128,118,118,118,118,122,122,128,128,138,138,140,140,136,136,130,130,132,132,134,134,136,140,142,142,148,148,150,150,150,150,142,142,144,144,156,156,158,158,164,164,164,164,160,160,160,160,156,156,152,152,152,152,154,154,156,156,164,164,170,170,170,170,162,162,164,164,172,172,176,176,176,176,176,176,182,182,182,182,178,178,176,178,186,186,186,186,180,180,180,180,186,186,188,188,188,188,184,184,190,190,194,194,194,194,190,190,192,192,194,194,194,194,196,196,196,196,194,194,192,194,208,208,208,208,204,204,202,202,202,202,204,204,206,206,210,210,210,210,212,212,214,214,206,206,202,202,202,202,204,204,206,206,212,212,214,214,210,210,206,206,202,204,210,210,212,212,212,212,204,204,200,200,198,198,200,200,204,204,204,204,210,210,210,210,206,206,194,194,188,188,188,188,190,190,192,192,192,192,190,190,188,188,178,178,176,176,170,170,170,170,168,168,166,166,162,162,162,162,160,160,156,156,144,144,144,144,140,140,138,138,138,138,132,132,126,126,120,120,120,120,136,136,140,140,150,150,152,152,164,164,170,170,174,174,176,176,186,186,198,198,204,204,204,204,200,200,180,180,178,178,178,178,172,172,166,166,158,158,156,156,154,154,154,154,152,152,152,152,150,150,148,148,148,148,154,152,140,140,126,126,124,124,118,118,118,118,128,128,128,128,118,118,118,118,130,130,130,130,120,120,120,120,128,128,134,134,136,136,136,136,128,128,128,128,136,136,136,136,140,140,144,144,144,144,146,146,156,156,166,166,168,168,172,172,174,174,174,174,196,196,208,208,210,210,212,212,212,212,196,196,166,166,158,158,144,144,138,138,136,136,130,130,130,130,138,138,128,128,128,128,132,132,132,132,114,114,114,114,116,116,120,120,120,120,130,130,134,134,134,134,140,140,142,142,150,150,156,156,158,158,174,174,188,188,192,192,208,208,208,208,194,194,194,194,206,206,206,206,200,200,200,200,206,206,206,206,204,204,198,198,198,198,210,210,210,210,202,202,132,132,130,130,130,130,140,140,140,140,134,134,134,134,142,142,144,144,144,144,134,134,134,134,142,142,146,146,150,150,150,150,152,152,154,154,158,158,158,158,166,166,168,168,168,168,158,156,156,156,164,164,166,166,192,192,194,194,194,194,176,176,176,176,194,194,194,194,178,178,170,170,170,170,168,168,158,158,152,152,152,152,162,162,164,164,174,174,176,176,176,176,158,158,154,154,154,154,156,156,158,158,158,158,156,156,154,156,158,158,158,158,156,156,156,156,158,158,158,158,156,156,158,156,156,158,158,158,160,160,160,160,164,164,166,166,172,172,172,172,168,168,166,166,160,160,166,166,168,168,170,170,170,170,164,164,160,160,158,158,158,158,162,162,168,168,168,98,98,98,76,76,70,70,60,60,58,58,48,48,48,48,46,46,44,44,44,44,36,36,30,30,24,24,20,20,2,2,2,2,4,4,4,4,6,6,18,18,22,22,22,22,20,20,18,18,4,8,14,14,30,30,32,32,42,42,42,42,42,42,46,46,44,44,26,26,22,22,38,42,46,46,58,58,58,58,50,50,48,48,48,48,52,58,60,60,60,60,48,48,46,48,60,60,64,64,64,64,50,50,44,44,40,40,40,40,50,50,62,64,70,70,70,70,60,60,18,18,16,16,16,16,20,20,22,22,30,38,38,38,42,42,46,46,52,60,78,78,80,80,80,80,84,84,94,94,96,96,100,100,98,98,90,90,84,84,84,84,78,78,76,76,66,66,64,62,62,62,78,78,90,90,92,92,94,94,94,94,84,84,36,36,16,16,4,4,2,2,2,2,10,10,30,30,32,32,32,32,26,26,14,14,12,12,12,12,14,14,16,16,20,20,20,20,42,42,46,46,46,46,52,52,66,66,70,70,70,70,74,74,92,92,92,92,90,82,66,44,6,2,4,4,86,88,94,94,94,86,82,82,82,82,96,96,96,96,94,52,52,52,52,52,52,52,52,154,158,158,158,158,158,158,158,158,164,164,164,164,154,154,174,174,174,174,172,172,170,164,158,156,148,142,138,120,120,120,116,116,116,116,140,140,140,140,120,120,120,120,142,142,130,130,118,118,118,174,178,178,188,188,188,188,168,168,198,198,198,198,182,208,208,208,202,202,144,144,134,136,142,150,154,154,160,160,172,172,174,178,196,198,186,186,166,166,156,156,148,148,136,136,132,132,132,132,122,122,122,122,126,126,126,120,112,112,104,84,80,80,72,72,72,74,82,82,74,74,70,70,64,64,64,64,74,74,56,54,42,42,36,36,36,36,44,42,38,38,32,32,20,20,14,14,8,8,8,8,6,6,4,4,2,2,2,54,56,56,62,62,62,62,56,56,56,56,80,80,82,82,88,88,88,88,82,82,82,90,90,90,78,78,78,78,84,172]; +var yP = [44,48,48,68,68,64,64,40,58,58,58,50,50,50,50,66,66,66,32,66,66,66,66,52,52,52,52,36,26,26,26,82,64,64,64,46,46,42,42,42,42,50,50,56,56,62,62,68,68,40,40,40,40,52,52,52,52,64,64,72,88,122,94,80,86,86,86,106,86,86,86,90,90,96,96,100,100,100,100,96,96,78,78,102,102,102,102,102,102,78,78,78,78,72,72,70,70,70,70,74,74,78,78,92,76,108,100,100,100,110,110,110,104,92,92,86,86,84,84,84,84,84,84,106,106,106,106,100,100,94,94,94,94,96,96,98,98,106,106,72,72,72,72,82,82,86,86,86,86,88,88,110,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,10,10,10,20,20,20,20,12,12,12,16,12,12,16,16,16,16,4,4,4,4,42,42,42,42,20,20,20,20,40,40,40,40,14,14,14,14,40,40,40,40,22,22,22,22,40,40,40,40,18,18,32,32,32,32,14,14,64,64,66,66,66,66,64,64,60,60,14,22,80,36,86,54,54,38,34,34,40,40,40,40,10,10,10,10,106,106,106,106,98,106,106,106,94,94,94,94,66,66,66,66,106,106,106,106,94,94,94,94,66,66,66,66,104,104,104,104,94,94,94,66,58,58,58,58,106,106,106,106,96,96,96,58,50,50,50,50,50,18,18,18,18,18,16,16,2,2,2,2,56,56,56,56,56,42,110,110,110,110,98,98,98,98,152,152,152,152,192,192,192,12,12,12,26,26,26,26,14,14,14,14,50,50,50,42,30,22,22,22,22,22,36,34,34,36,48,1,2,2,2,2,28,28,28,28,10,10,10,20,20,20,44,44,44,44,6,6,6,6,2,2,2,2,34,34,34,26,26,26,10,10,10,10,28,16,16,16,30,30,30,30,44,44,44,44,94,94,94,94,110,110,110,110,90,90,90,2,2,2,2,12,18,26,32,56,60,64,68,74,78,72,52,52,52,52,54,54,56,56,56,56,54,54,52,52,54,54,56,56,54,54,54,54,52,52,54,52,50,50,52,52,50,50,38,10,20,28,32,50,52,60,62,70,72,74,74,22,22,22,22,22,86,100,100,100,100,100,100,100,100,100,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,100,12,12,12,24,24,24,24,28,28,28,28,24,24,16,16,14,14,14,14,18,18,22,22,30,30,30,30,30,30,40,40,36,36,26,26,18,18,18,18,14,14,8,8,8,8,18,18,20,20,26,26,26,26,22,22,2,2,2,12,12,12,4,4,2,4,12,12,12,12,14,14,16,16,30,30,30,30,26,26,4,26,28,28,28,28,16,16,10,10,10,10,12,16,20,20,22,22,22,22,20,20,14,14,10,10,8,8,2,2,2,2,14,14,28,16,12,12,12,12,10,10,8,8,8,8,8,94,80,80,70,70,60,60,60,60,64,64,94,72,72,72,74,74,80,80,82,82,82,82,76,76,74,76,78,78,90,90,90,90,88,88,76,76,68,68,68,68,70,70,72,72,82,82,82,82,78,78,72,72,68,68,68,68,68,68,62,62,54,54,54,54,76,76,76,76,72,72,50,58,60,60,60,60,62,72,74,74,74,74,60,60,58,58,58,58,60,60,64,64,68,68,68,68,56,56,46,46,42,42,42,42,44,44,66,66,82,82,78,78,66,66,64,64,54,54,54,54,64,64,66,66,66,66,68,68,80,70,66,66,66,66,42,66,76,76,76,76,68,68,62,62,62,62,64,64,72,72,74,74,74,74,70,70,68,62,54,54,54,54,60,60,72,60,58,58,56,56,56,46,46,10,10,10,10,10,38,38,48,48,52,52,68,68,68,68,50,50,50,50,48,48,40,40,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,42,46,56,58,68,70,80,98,106,108,112,114,114,114,114,114,20,20,20,20,20,32,32,32,100,100,100,100,100,100,100,100,100,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,100,36,36,36,24,24,24,24,42,42,42,42,36,30,30,26,26,26,26,26,38,38,38,38,52,52,52,52,40,40,40,40,34,34,34,34,28,28,28,28,24,24,24,24,22,22,22,22,20,20,18,18,16,16,16,16,14,14,14,14,102,102,78,78,78,78,78,78,70,70,70,70,70,70,78,78,70,64,34,34,34,34,34,38,86,86,86,86,86,86,86,86,26,26,26,26,26,26,26,56,84,90,94,98,100,104,108,114,118,124,132,140,148,160,168,178,198,20,20,20,38,38,38,38,34,34,28,28,18,18,12,12,10,10,10,10,28,28,42,28,28,28,42,42,42,42,34,34,34,14,46,46,46,40,40,40,24,24,24,24,42,32,22,22,22,22,34,34,34,34,42,42,42,42,54,54,54,54,40,40,40,24,24,48,14,14,14,20,20,28,28,28,24,24,24,24,38,38,38,16,48,48,48,48,48,48,36,36,36,54,4,4,54,62,66,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,1,2,2,18,18,78,78,36,36,36,36,86,86,124,124,154,154,154,154,122,122,122,122,104,104,104,104,80,80,80,80,118,118,118,118,158,158,158,158,154,154,120,120,92,92,58,58,56,56,56,56,60,60,76,76,100,100,100,100,142,142,142,142,122,122,122,122,120,120,84,84,68,68,68,68,44,44,44,44,162,162,162,162,158,158,138,138,102,102,102,102,116,116,116,116,82,82,82,82,32,32,32,32,36,36,74,74,136,136,136,138,168,168,168,168,184,184,184,168,180,180,180,180,166,166,166,170,174,174,170,176,176,176,172,164,138,166,136,166,136,166,128,166,138,174,174,174,110,110,110,110,182,182,182,182,198,198,190,190,190,190,198,198,176,176,176,108,108,108,82,82,82,82,106,82,82,82,106,106,80,80,110,110,110,38,38,38,32,32,32,32,42,42,42,42,30,42,42,42,26,26,26,26,38,38,38,38,32,32,32,18,40,34,34,34,42,18,30,30,30,30,26,26,26,26,32,16,16,16,30,30,30,30,22,22,22,12,28,28,28,20,14,14,14,14,24,24,24,24,20,16,22,22,22,22,14,14,14,22,32,32,32,10,10,10,16,16,16,16,22,22,22,22,22,22,8,8,8,8,22,22,22,22,8,8,8,8,22,22,22,22,6,6,6,6,22,28,28,28,24,24,2,2,2,18,30,38,48,56,68,76,88,96,108,116,126,134,148,156,168,174,186,194,198,198,190,182,178,172,166,160,154,148,142,118,112,106,98,92,84,78,72,68,56,50,42,34,24,18,10,4,2,18,44,44,42,42,10,42,42,42,34,34,34,34,50,50,50,50,8,30,30,30,46,30,28,28,8,26,26,16,46,46,46,38,38,38,30,30,30,30,38,44,6,6,6,6,26,26,26,26,70,40,112,70,70,70,110,72,70,70,30,62,94,94,94,94,58,58,58,52,102,102,102,56,96,96,96,76,76,76,64,64,64,64,90,90,90,106,50,82,82,82,106,82,80,80,44,60,60,52,110,110,110,96,68,68,68,68,96,96,96,110,48,48,48,48,60,60,60,60,64,64,114,114,114,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,110,102,88,80,68,60,42,36,24,18,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,16,22,34,38,48,54,66,72,82,86,98,104,122,62,90,90,90,90,58,58,58,58,44,44,44,44,60,60,60,70,78,78,78,78,66,66,66,66,80,80,80,80,64,64,64,70,66,66,66,66,72,72,72,72,66,70,70,70,74,74,74,74,70,82,86,86,86,86,78,90,100,90,100,100,100,100,126,126,132,132,144,144,144,144,140,140,132,132,126,126,126,126,126,126,124,124,112,112,112,112,132,132,132,132,138,138,148,148,154,154,154,154,144,144,136,136,132,132,130,130,98,98,98,122,122,122,126,126,132,132,150,150,146,146,128,128,120,120,120,120,122,132,128,128,126,126,126,126,122,118,126,126,126,126,134,134,134,134,126,132,182,182,182,182,158,158,158,158,184,184,184,184,148,152,162,162,150,142,152,152,140,182,192,192,192,192,188,188,188,188,198,198,198,198,180,184,198,198,198,198,184,184,184,184,194,194,194,192,192,192,188,194,184,184,180,180,162,162,160,160,160,160,124,124,124,124,160,160,182,182,182,182,194,194,194,194,178,178,194,194,194,194,198,198,198,198,176,162,162,162,136,136,164,164,164,164,174,174,174,174,158,124,84,84,84,84,74,74,74,74,84,84,84,84,76,76,76,76,84,84,74,68,62,56,48,42,34,78,78,78,78,78,78,82,82,82,82,82,82,44,24,24,24,24,18,18,18,18,24,24,24,24,10,90,114,114,116,116,116,116,116,116,110,110,88,88,88,94,154,98,98,22,14,14,14,14,32,32,32,32,60,60,60,60,48,48,28,24,20,18,8,8,8,8,32,32,32,32,22,22,22,10,60,12,12,12,52,52,52,52,2,2,68,2,2,2,68,26,26,8,60,60,60,94,108,96,110,98,114,108,92,92,106,106,92,92,104,104,130,130,130,130,102,102,102,108,108,108,114,114,114,114,120,120,120,120,126,126,126,130,164,188,188,188,198,198,198,198,186,186,186,186,198,198,198,198,186,150,150,158,158,168,168,140,166,146,172,152,176,160,176,160,178,98,98,98,98,98,98,92,92,94,94,94,94,104,108,120,124,134,140,98,98,98,94,94,94,94,104,104,100,98,94,94,94,94,102,102,102,12,12,36,36,36,48,48,48,48,56,56,56,56,46,58,46,46,46,46,58,58,58,54,84,84,84,84,92,92,92,92,100,100,100,100,48,48,48,48,38,38,38,42,42,42,46,46,46,36,24,24,42,24,24,24,20,20,20,20,12,12,12,12,22,22,22,22,26,26,26,58,80,80,80,80,76,84,90,90,84,84,92,92,84,84,92,86,86,76,76,76,32,32,32,32,40,40,40,40,100,100,100,100,122,122,122,128,128,128,118,118,118,118,132,132,132,132,118,118,118,122,130,124,124,122,122,118,118,118,130,130,130,120,112,112,112,112,168,168,168,168,102,102,102,102,82,82,82,168,168,168,172,172,172,172,186,186,186,186,172,172,172,172,186,186,186,186,174,174,174,174,186,186,186,186,170,170,170,170,168,130,138,138,138,138,162,162,162,162,174,174,174,174,180,180,180,180,174,174,174,178,184,184,184,184,194,194,194,194,178,174,156,156,156,156,144,144,144,134,150,150,150,150,134,134,134,140,140,140,128,138,138,138,118,118,118,118,146,146,146,126,138,142,138,134,138,138,146,14,4,4,4,4,34,34,34,40,40,40,48,48,48,48,42,42,42,42,36,36,36,36,30,30,30,30,20,20,20,20,12,12,12,12,30,16,16,8,30,30,14,8,30,18,18,18,4,4,4,4,32,22,22,30,4,22,22,22,28,28,28,28,18,18,18,18,22,28,8,18,18,18,4,4,4,50,74,74,74,74,60,60,60,60,50,50,50,50,76,48,48,48,58,58,58,58,60,60,162,162,164,68,68,68,44,44,44,56,56,70,46,46,46,46,70,58,58,72,44,60,60,60,44,56,60,60,122,122,128,52,52,52,60,60,60,60,44,54,54,54,76,76,76,76,54,54,54,54,54,64,46,46,46,48,70,58,58,70,80,80,80,68,68,68,58,58,58,58,88,88,88,94,94,94,94,94,94,94,94,94,94,92,198,198,106,104,196,198,130,126,124,112,108,114,118,128,130,136,198,198,198,198,158,158,168,174,196,196,196,196,188,188,188,188,194,194,194,18,18,32,32,32,22,30,30,30,22,24,42,42,42,42,18,18,18,18,24,18,14,14,14,14,18,18,18,18,26,26,26,26,32,32,36,36,36,36,42,42,38,38,34,34,34,34,26,26,24,24,24,24,16,16,16,48,58,58,58,58,46,46,46,46,64,64,64,64,40,40,40,40,68,68,68,68,34,34,34,34,78,78,78,78,26,26,26,26,88,88,88,88,84,84,74,74,72,72,64,64,58,58,52,52,50,50,46,46,46,46,34,28,28,28,98,108,108,108,18,18,18,18,18,18,104,104,104,104,70,70,68,68,34,34,32,32,22,22,22,22,20,20,12,12,2,2,2,2,14,14,42,42,50,52,128,128,144,144,144,144,100,100,96,96,92,92,92,92,82,80,66,66,66,66,70,70,80,80,100,100,102,102,122,122,126,126,136,136,136,136,122,122,120,120,88,88,86,86,64,64,62,62,42,42,42,42,50,50,54,54,60,60,60,60,58,58,58,58,48,48,18,18,18,18,142,142,144,144,144,144,144,144,142,142,126,126,118,118,118,118,98,98,74,74,72,72,68,68,68,68,66,66,66,66,68,68,84,84,90,90,90,90,100,100,116,116,130,130,130,130,122,122,116,116,112,112,68,68,68,68,66,66,58,58,54,54,54,54,50,50,48,48,46,46,42,42,40,40,38,38,34,34,32,32,30,30,30,30,62,62,64,64,96,96,98,98,108,108,108,108,112,112,112,112,122,122,144,144,146,146,146,146,136,136,122,122,112,112,106,106,106,106,108,108,130,130,132,132,132,132,152,152,154,154,158,158,158,158,150,150,150,150,72,72,64,64,60,60,60,60,44,44,42,42,30,30,30,30,30,30,38,38,46,46,46,46,34,34,34,34,32,32,34,34,34,34,32,32,30,30,24,24,24,24,26,28,28,28,34,34,34,34,18,18,38,38,44,44,48,58,60,60,42,42,46,46,48,48,64,64,64,64,50,50,48,44,44,44,56,56,70,70,84,84,96,96,102,102,102,102,112,112,112,112,110,110,96,96,96,96,94,94,62,62,58,58,58,58,60,60,60,60,58,58,56,56,60,60,80,80,80,80,82,82,120,120,120,120,66,66,84,84,86,86,88,88,88,88,68,68,66,66,40,40,36,36,36,36,38,38,60,60,90,90,90,90,94,94,94,94,104,104,108,108,108,108,96,96,84,78,50,50,46,46,16,16,12,12,10,10,10,10,6,6,2,2,2,2,12,12,14,14,18,18,18,18,8,8,16,16,40,40,40,40,38,38,36,36,36,36,26,24,18,18,2,2,2,2,8,8,20,20,36,36,38,38,38,38,54,58,58,58,62,62,72,72,78,78,86,98,112,116,132,132,134,134,158,158,158,158,178,178,182,182,190,190,190,190,150,150,150,150,158,158,174,174,178,178,178,178,190,190,190,190,154,154,154,154,182,182,194,194,194,194,164,164,164,164,182,182,192,192,198,198,198,198,196,196,174,174,172,172,172,172,144,144,144,144,156,156,182,186,186,186,168,168,168,168,182,182,182,182,176,176,154,154,152,152,138,138,136,136,132,132,132,132,170,170,160,160,76,76,72,72,60,56,54,54,52,52,42,42,42,42,40,40,32,32,24,24,20,20,16,16,14,14,12,12,10,10,8,8,6,6,4,4,2,2,2,2,6,6,8,8,36,36,36,36,38,38,56,56,172,172,172,172,94,94,94,94,96,96,96,96,140,140,176,176,192,192,192,192,156,156,154,154,154,154,174,174,174,174,192,192,194,194,194,194,190,190,190,190,190,190,164,164,98,98,96,96,92,92,90,90,90,90,80,80,80,80,82,82,104,104,110,110,110,110,132,132,132,132,124,124,68,68,62,62,62,62,70,70,76,76,86,86,86,86,52,52,52,52,56,56,70,70,78,78,80,80,84,84,90,90,90,90,94,94,96,96,106,106,106,106,78,78,78,78,60,60,6,6,6,6,2,2,2,2,6,6,22,22,22,22,18,18,4,4,4,4,20,20,30,30,40,40,42,42,48,48,52,52,62,62,74,74,76,76,76,76,78,78,80,80,84,84,86,86,88,88,90,90,90,90,92,92,92,92,94,94,94,94,96,96,96,96,98,98,98,98,100,100,100,100,100,100,100,104,126,126,126,126,134,134,146,146,146,146,146,146,140,140,114,114,112,112,88,88,88,88,88,88,74,88,98,98,100,100,100,100,118,118,136,136,136,140,160,160,164,164,178,178,178,178,152,150,150,150,150,150,132,132,132,132,142,142,142,142,144,144,166,166,166,166,186,186,186,186,182,182,174,174,162,162,156,156,154,154,146,146,102,102,102,102,124,124,126,126,142,142,152,152,154,154,160,160,160,160,162,162,192,192,192,192,142,142,20,20,18,18,4,4,4,4,8,8,20,20,20,16,2,2,2,2,20,20,22,32,32,32,26,26,26,26,34,34,72,72,74,74,74,74,84,84,84,84,136,138,140,140,142,142,142,142,168,168,170,170,170,170,118,118,74,74,10,10,2,2,2,2,4,4,48,48,48,48,122,122,144,144,160,160,160,160,120,116,116,116,122,144,152,152,192,192,192,192,184,184,156,156,126,126,126,126,124,124,58,124,134,134,134,134,126,126,126,126,106,106,106,106,90,90,90,90,150,150,156,156,158,158,158,158,142,142,142,142,184,184,198,198,198,198,180,180,160,156,96,92,76,70,66,66,20,20,18,18,18,18,30,48,48,48,30,30,30,30,32,32,62,62,62,62,66,66,70,70,70,70,54,54,54,54,48,48,48,48,28,28,28,28,32,32,44,46,44,44,40,40,34,34,34,34,36,36,40,40,52,52,50,50,48,54,56,56,56,56,38,38,36,36,34,34,34,34,34,34,46,46,58,58,68,68,152,152,152,152,164,164,164,48,48,48,48,48,52,52,72,72,86,86,86,86,82,82,50,50,42,42,42,42,50,44,36,36,34,34,34,34,42,42,84,84,94,94,106,106,106,106,104,104,98,98,90,90,86,86,86,86,80,52,60,60,64,64,64,72,72,58,58,58,62,62,66,66,66,66,64,64,60,60,54,54,54,54,62,62,54,54,54,54,60,60,64,64,64,64,54,60,62,62,68,68,72,72,74,74,74,74,82,82,82,52,52,56,60,60,60,60,50,50,46,46,46,46,50,56,56,56,54,54,54,54,48,48,46,46,46,46,60,60,60,36,26,26,24,24,24,24,40,40,86,86,92,92,92,92,84,90,100,100,104,104,104,104,88,88,88,88,86,84,78,78,78,78,70,70,70,70,80,80,80,80,110,110,118,118,118,118,124,124,136,136,140,140,178,178,180,180,180,180,188,188,190,190,190,190,186,186,138,138,138,138,170,170,178,178,178,178,174,174,166,166,164,164,164,164,114,118,128,128,140,140,146,146,146,146,128,128,124,124,124,124,84,22,22,22,24,24,38,38,52,52,52,52,44,44,36,36,18,18,18,18,22,22,24,28,28,36,42,38,36,36,36,36,40,40,40,24,14,14,14,14,4,4,2,2,2,2,14,160,160,160,158,158,150,150,150,150,152,152,160,160,160,160,158,158,150,150,150,150,152,152,160,160,160,160,154,154,152,154,162,152,150,150,150,150,156,156,158,156,152,152,148,148,148,148,154,154,156,156,156,156,156,156,156,156,156,156,146,146,146,146,154,154,168,168,168,38,38,38,38,38,62,62,62,62,2,60,60,28,28,28,68,68,68,24,18,32,32,32,66,66,66,60,60,60,38,38,38,8,8,8,74,74,74,60,60,60,30,30,30,30,60,64,40,40,40,40,64,64,64,44,44,64,72,72,72,72,88,88,88,88,74,74,74,80,4,4,4,4,14,36,36,48,48,48,38,38,38,38,62,62,62,62,54,6,6,6,68,68,68,4,64,64,64,64,32,32,32,60,60,48,48,48,66,66,66,66,74,74,74,74,82,82,82,82,94,94,94,94,84,84,84,84,64,64,64,64,78,78,78,78,66,66,66,66,42,42,42,42,198,198,198,198,46,100,100,100,94,94,94,94,84,84,84,84,116,116,116,116,110,110,110,110,102,102,102,102,98,108,108,104,104,88,92,98,98,98,114,114,114,100,100,100,106,106,106,98,92,92,92,92,104,104,104,104,118,118,118,96,96,96,114,114,114,96,96,100,104,104,98,112,122,122,122,122,130,130,130,130,146,126,126,122,122,108,108,108,114,114,114,116,156,92,92,92,100,100,100,104,124,124,124,130,130,130,100,96,96,114,122,126,130,134,150,150,150,150,198,198,152,152,152,152,198,198,144,144,144,144,198,198,138,138,138,138,128,124,112,150,150,150,198,198,160,160,160,160,198,198,174,148,198,1,2,104,104,104,86,86,86,86,72,72,76,76,88,88,88,88,50,50,50,50,60,60,60,60,50,50,50,50,36,36,36,36,72,72,72,98,24,24,24,24,100,64,64,50,50,38,38,26,26,24,20,20,20,20,26,26,26,26,30,30,30,30,26,26,22,22,20,20,20,20,24,24,20,20,20,20,24,24,28,28,32,32,34,34,40,40,36,36,30,30,28,28,28,28,26,26,26,26,30,30,30,30,20,20,16,16,14,14,14,14,18,18,22,22,26,26,26,26,18,20,24,24,26,26,22,22,20,20,14,14,14,14,16,16,20,20,20,20,18,24,20,20,14,14,14,14,12,12,6,6,6,6,10,10,14,14,14,14,10,10,4,4,2,2,2,2,10,10,10,10,8,8,8,8,16,16,18,18,18,18,12,12,10,10,6,6,6,6,8,8,8,8,6,6,4,4,2,2,2,72,72,72,70,70,58,58,60,60,74,74,86,86,86,86,58,58,58,58,42,42,44,44,60,60,74,74,74,74,72,72,64,64,58,64,74,74,74,74,64,64,64,70,76,76,76,76,68,68,68,68,68,68,62,62,62,62,70,70,70,70,62,62,62,62,72,82,94,94,94,94,100,100,100,100,80,80,80,84,90,90,90,82,82,96,96,96,102,102,102,102,108,108,108,108,94,94,94,94,102,102,94,94,102,108,108,108,108,108,98,98,98,98,90,90,90,90,80,80,80,80,96,96,96,96,104,104,104,86,86,86,80,80,80,80,88,88,88,88,80,80,80,80,90,90,94,94,94,94,90,90,90,90,90,90,78,78,78,78,90,86,86,82,82,74,78,78,74,74,78,78,74,74,78,78,74,48,28,32,32,38,38,42,42,86,90,90,90,90,86,86,86,110,110,136,146,146,146,146,130,130,130,130,174,174,174,174,160,160,160,160,168,168,168,168,134,134,134,134,172,172,172,172,154,154,154,154,172,172,172,172,128,128,128,128,152,152,152,152,132,132,132,132,176,176,176,176,168,168,168,168,148,148,148,148,140,140,140,140,136,136,134,134,134,134,128,128,128,128,132,132,176,176,176,176,142,144,152,152,152,152,140,140,176,176,176,176,126,126,126,126,142,142,134,134,126,126,126,126,130,130,176,176,176,176,136,138,146,146,174,174,174,174,126,126,126,126,138,134,126,126,126,126,130,122,130,130,130,130,122,122,122,134,178,178,178,178,134,134,134,118,118,118,174,174,174,174,116,116,116,116,110,110,118,122,128,128,128,128,142,142,142,142,150,150,150,150,154,154,162,162,170,170,170,170,160,160,160,160,150,150,150,150,142,142,142,142,140,140,124,124,118,118,118,118,122,118,124,124,124,120,122,122,128,130,134,134,138,134,142,178,180,180,198,198,198,176,178,178,198,198,198,172,198,198,198,174,176,176,198,198,198,170,172,172,198,198,198,168,172,172,190,190,198,158,160,160,176,176,180,152,156,156,164,148,164,164,166,148,162,162,164,122,124,124,134,134,138,116,116,116,118,118,126,126,126,126,138,150,152,152,156,116,120,116,118,118,120,108,108,108,120,130,134,134,134,174,178,178,198,172,174,174,198,174,176,176,198,198,198,176,178,178,198,198,198,174,176,176,198,198,198,176,178,178,198,198,198,150,150,150,162,174,178,178,198,198,198,176,180,180,198,198,198,134,134,134,136,132,134,134,136,136,138,132,134,134,142,144,162,146,148,148,168,168,178,174,176,176,198,198,198,176,178,178,198,198,198,146,148,150,172,134,136,136,162,126,128,128,138,172,198,198,198,170,198,198,198,152,154,156,164,172,198,198,198,172,174,174,198,198,198,174,178,178,198,198,198,174,198,198,198,132,134,134,144,144,146,146,154,132,134,134,144,144,146,146,162,26,22,22,20,20,20,20,28,28,32,32,38,38,38,38,30,30,26,26,22,22,22,22,24,32,32,32,28,18,14,14,6,18,18,18,14,30,30,36,40,40,50,36,62,62,50,50,46,46,44,34,34,22,22,28,20,20,12,12,10,10,2,2,2,2,12,12,14,14,14,14,24,24,26,34,34,34,40,40,40,40,32,36,16,32,14,8,8,12,12,24,48,30,30,22,46,32,32,32,48,48,48,48,40,40,40,26,52,52,52,22,50,50,50,42,34,34,34,34,42,42,42,24,24,24,38,38,38,38,48,48,48,32,40,40,40,40,32,32,32,40,30,30,30,30,40,40,36,36,30,30,22,22,22,22,30,24,24,24,18,18,18,18,32,32,32,32,24,24,24,24,32,18,42,42,42,42,30,30,30,30,30,30,24,24,24,24,36,36,36,40,26,26,26,26,26,26,34,34,34,34,26,26,26,26,20,34,42,42,42,42,48,48,48,48,48,48,48,48,48,116,102,102,102,102,126,126,126,126,110,110,120,120,120,124,124,104,92,92,92,92,104,94,94,94,50,94,94,94,140,118,118,106,106,100,100,110,142,142,142,142,136,136,136,136,144,144,148,148,152,152,152,152,148,148,140,140,136,136,136,136,134,134,124,124,122,122,122,122,114,114,110,110,104,104,104,104,108,108,118,118,122,122,122,122,122,122,130,132,166,166,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,160,152,152,152,152,156,156,156,156,146,146,146,146,152,152,152,152,106,106,106,106,112,112,112,112,76,76,76,76,66,66,66,66,90,90,90,90,84,84,84,84,90,90,90,90,78,78,78,78,70,70,70,70,78,78,78,78,66,66,66,66,72,72,72,72,46,46,46,46,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,20,26,32,38,44,48,54,60,66,72,80,86,92,98,102,102,102,98,98,98,98,98,98,98,98,98,98,92,92,116,116,116,118,118,120,120,120,120,118,118,118,118,122,122,124,124,124,124,128,128,130,130,132,132,132,132,128,128,126,126,124,124,122,122,122,122,132,132,136,136,142,142,142,142,146,146,148,148,148,148,152,152,156,156,158,158,162,162,162,162,154,154,154,154,158,158,158,158,160,160,160,160,166,166,170,170,172,172,174,174,176,176,176,176,164,164,164,164,160,160,156,156,154,154,152,152,152,152,156,156,160,160,164,54,54,54,54,54,92,92,92,92,82,82,76,76,62,62,36,36,36,36,62,62,62,62,38,38,34,34,34,34,66,66,66,66,76,76,80,80,76,76,68,68,64,64,64,64,68,68,70,70,70,70,70,70,70,64,78,78,78,78,76,76,74,74,72,72,68,68,66,66,66,66,72,72,72,72,76,76,80,82,82,82,90,90,90,90,80,80,88,88,82,82,90,90,80,80,86,80,80,80,76,76,70,70,58,58,58,58,72,72,78,78,80,80,102,102,104,104,108,108,114,114,114,114,116,116,124,124,126,126,126,126,122,122,114,114,104,104,102,102,96,96,94,94,92,92,92,92,96,96,102,102,104,104,108,108,114,114,118,118,122,122,122,122,124,124,126,126,130,130,130,130,118,118,114,114,106,106,104,104,102,102,102,102,94,94,94,94,102,102,104,104,106,106,112,112,116,116,122,122,122,122,126,126,128,128,132,132,132,132,126,126,122,122,120,120,120,120,118,116,110,110,108,108,100,100,98,98,96,96,96,96,100,100,112,112,114,114,124,124,130,130,130,130,128,128,126,126,120,120,116,116,112,112,108,108,90,90,84,84,80,64,54,54,54,54,58,58,64,64,66,66,66,66,62,62,62,62,64,64,66,60,38,38,36,36,36,36,38,38,46,46,48,48,48,50,54,60,62,62,48,48,46,46,44,44,38,38,38,38,44,44,48,114,114,114,112,112,110,110,106,106,106,106,100,100,104,104,108,108,108,108,114,114,114,114,110,110,108,108,106,106,100,100,110,110,112,112,112,112,116,116,116,116,116,116,116,116,116,116,116,116,112,112,108,108,104,104,102,102,110,110,114,116,116,116,112,112,100,100,96,96,96,96,88,88,76,76,76,76,84,84,84,84,88,88,96,96,96,96,102,102,102,102,104,104,114,114,120,158,166,166,170,170,170,170,164,164,146,146,132,132,128,132,166,166,170,170,170,170,166,166,158,158,152,152,152,152,152,152,154,154,160,160,166,166,166,166,152,152,134,134,134,134,138,138,182,170,166,166,166,166,178,178,178,178,174,174,138,170,176,176,176,176,162,172,178,178,178,178,170,170,160,170,174,174,176,176,176,176,174,174,160,160,160,160,164,164,176,164,162,162,154,154,154,154,166,166,180,180,180,180,178,178,172,160,156,156,152,152,152,152,156,156,170,170,172,172,172,172,164,164,160,156,152,164,188,188,194,194,194,194,192,192,186,186,164,150,150,150,150,18,32,32,32,32,28,28,2,14,14,2,20,20,28,28,28,28,12,12,12,12,14,14,16,16,18,18,18,18,16,16,16,16,18,18,26,26,28,28,28,28,22,22,14,12,8,12,24,24,24,24,22,22,8,8,8,8,24,24,24,24,20,20,18,18,18,18,14,14,8,14,20,8,8,8,12,12,20,20,24,24,24,18,12,12,8,8,8,8,12,12,16,38,32,32,30,30,30,30,34,34,40,40,42,42,42,42,48,48,50,50,54,54,54,54,50,50,48,48,48,48,26,26,26,26,34,34,32,32,24,22,20,20,24,24,34,34,44,130,168,168,136,136,128,128,122,44,120,120,120,120,40,40,36,36,36,44,44,44,36,36,32,32,26,42,34,34,34,34,30,30,26,26,24,42,34,34,26,26,24,44,42,42,40,40,32,32,32,32,30,62,62,62,68,68,68,68,64,64,64,62,62,62,68,68,68,68,60,66,60,66,66,64,70,70,70,70,66,74,74,74,78,78,78,78,70,74,78,78,78,78,70,48,48,48,52,52,52,58,58,58,62,62,62,66,66,66,70,70,70,76,76,76,80,80,80,88,88,88,92,92,92,98,98,98,102,102,102,110,110,110,114,114,114,114,114,114,110,110,110,102,102,102,100,100,98,98,98,92,92,92,90,90,88,88,88,88,90,90,90,80,80,80,76,76,76,68,68,68,66,66,66,66,62,62,62,54,54,54,50,50,48,52,52,22,22,22,68,68,68,56,34,34,66,46,46,38,78,30,38,34,34,16,32,16,64,16,16,16,34,34,34,46,38,38,38,38,58,58,58,58,82,82,82,82,74,36,36,28,28,20,70,34,60,40,80,14,66,36,60,56,88,112,112,112,168,168,168,168,112,112,112,112,112,114,96,96,96,96,112,112,112,106,106,100,100,96,114,130,130,130,142,142,142,142,128,128,142,142,142,142,126,126,126,134,130,130,130,130,134,134,134,134,130,130,134,134,134,148,148,148,158,158,158,158,146,146,158,128,120,126,118,126,118,126,112,118,118,118,124,124,124,136,136,136,142,142,142,150,150,150,154,154,154,166,166,166,162,162,162,162,162,162,170,170,170,170,164,156,156,156,150,150,150,138,138,138,134,134,134,120,120,120,114,114,114,88,88,88,118,118,118,138,138,138,166,166,166,170,170,172,172,176,176,166,166,166,154,154,180,180,180,180,166,164,164,164,142,142,142,142,190,190,190,190,188,186,164,164,162,184,192,192,192,192,188,160,160,160,160,160,160,160,160,174,174,174,174,174,174,174,174,162,162,162,170,170,170,170,160,176,176,164,164,164,172,172,172,172,166,166,166,166,162,176,176,152,146,142,136,132,132,132,124,142,136,132,126,128,128,178,188,188,188,178,198,198,198,182,196,196,196,176,192,192,192,178,194,176,176,182,192,192,192,192,170,170,194,172,172,172,192,192,192,192,168,172,172,172,190,190,190,180,180,174,168,168,168,168,186,186,186,186,198,198,198,198,194,190,176,176,176,176,194,194,194,194,190,190,190,190,194,194,194,194,188,188,188,188,196,180,180,132,150,150,150,150,146,146,146,100,116,116,116,116,112,112,112,96,96,96,112,112,112,112,108,108,108,108,94,112,108,108,108,108,112,112,112,98,98,144,136,124,112,100,86,68,56,12,26,26,26,26,22,22,22,12,12,12,32,32,32,32,28,28,28,16,16,6,24,24,24,24,18,18,18,56,56,56,76,76,76,102,102,102,92,124,124,124,114,138,138,138,146,88,94,100,106,74,74,74,100,100,100,100,116,116,114,114,94,94,64,64,64,64,54,54,54,54,68,68,68,68,40,56,56,56,42,50,50,50,72,54,54,54,86,86,86,86,72,72,72,72,86,106,32,32,32,32,50,50,50,50,52,52,64,64,98,106,110,116,120,126,132,138,144,152,158,164,170,176,184,190,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,192,186,180,174,168,162,156,152,148,144,138,134,128,124,120,114,106,102,96,90,84,80,74,68,60,54,50,44,40,36,30,26,20,14,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,10,16,20,24,30,34,40,44,50,56,62,68,72,80,86,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,94,98,104,108,112,172,172,172,172,150,150,150,150,128,128,128,128,128,128,144,144,150,150,150,150,142,142,122,122,122,122,160,160,160,160,160,160,122,134,134,134,126,126,126,126,140,140,140,126,126,126,142,142,144,144,144,144,140,140,122,122,122,122,118,142,152,152,154,154,154,154,156,156,162,162,166,166,166,166,158,158,158,158,168,168,168,168,162,162,162,162,168,168,168,168,162,162,162,134,134,134,132,132,126,126,122,122,128,128,134,134,144,144,144,24,24,24,68,68,74,74,74,74,102,102,108,108,108,74,30,30,22,22,22,22,110,110,112,112,112,112,108,108,102,102,98,98,98,38,38,38,32,32,28,28,26,96,96,96,98,98,102,102,114,114,120,120,130,130,132,132,144,144,146,146,156,156,160,160,172,172,174,174,182,186,186,186,184,184,180,180,176,176,176,176,170,170,166,166,150,150,150,150,180,180,180,180,176,176,150,150,138,138,138,138,150,150,154,154,166,166,166,166,186,186,190,190,190,92,92,92,106,106,128,96,118,118,122,122,122,122,124,124,132,132,152,152,152,152,132,132,132,132,118,78,38,38,30,30,30,30,36,36,72,72,82,82,82,82,72,72,70,32,32,32,40,40,46,46,54,54,62,62,70,70,74,74,84,84,84,84,74,74,38,38,32,66,66,66,66,68,78,78,78,78,76,76,68,68,58,62,72,72,72,72,70,70,56,56,54,42,42,42,42,14,14,14,14,14,14,14,14,74,74,74,78,78,82,82,92,92,96,98,104,104,112,112,114,114,122,122,124,124,132,132,140,140,142,142,150,150,152,152,158,158,160,160,168,168,172,172,178,58,58,58,58,58,54,54,48,48,50,50,60,60,68,10,10,10,22,22,32,32,32,32,20,20,18,18,4,18,18,10,26,8,8,8,10,10,70,70,86,8,62,8,8,8,8,46,64,44,64,8,8,8,10,10,20,20,22,22,32,188,198,194,194,194,198,198,198,192,192,192,184,188,188,188,198,198,198,198,186,186,198,192,198,198,198,194,194,194,184,188,188,188,198,198,198,198,194,194,188,188,188,188,196,188,188,188,194,194,196,192,192,192,188,188,186,194,194,194,198,188,188,188,196,196,196,196,194,194,188,188,182,188,188,62,104,104,104,104,54,54,54,54,82,82,82,82,84,82,70,70,70,70,82,82,82,82,82,82,68,68,68,68,84,74,74,74,74,74,82,82,82,76,70,70,70,70,78,78,78,72,72,72,80,80,80,80,74,74,74,74,82,82,82,82,78,90,98,98,98,98,88,88,88,88,100,100,86,86,102,56,42,42,42,42,46,46,46,56,50,50,50,50,42,42,42,42,46,46,46,56,46,46,46,46,34,34,34,34,38,38,38,38,30,58,44,44,44,44,32,32,32,32,26,26,26,26,34,34,34,30,30,30,26,26,38,38,34,26,18,18,18,18,18,18,22,22,30,30,30,30,14,30,30,30,36,36,40,40,40,40,34,34,32,42,36,36,36,36,44,44,46,46,46,46,42,40,32,46,46,46,46,46,44,44,34,34,34,34,36,36,42,42,48,48,48,28,28,28,32,32,36,30,24,38,38,38,44,38,34,40,40,40,46,46,48,46,58,58,40,44,44,44,34,60,52,52,52,52,46,58,56,54,46,46,34,34,34,26,26,26,32,32,34,34,42,100,122,100,122,122,122,122,118,118,118,118,128,128,120,120,120,120,122,122,132,120,136,136,136,136,154,154,156,122,134,134,152,152,198,198,156,156,198,198,150,132,198,194,190,190,190,190,194,194,194,182,178,178,178,178,186,186,186,168,164,164,164,164,168,168,168,156,150,150,150,150,156,156,156,156,154,154,154,92,92,92,62,62,62,62,88,88,120,120,132,132,132,132,122,122,122,122,118,118,94,94,82,88,112,112,112,112,100,100,100,100,114,114,114,114,82,82,82,82,98,98,98,98,88,88,88,88,88,88,116,116,116,116,88,80,74,74,74,74,82,82,82,92,120,120,120,120,90,90,90,122,122,122,126,126,126,126,122,78,78,12,20,26,34,38,50,58,70,60,50,42,30,22,10,10,20,28,36,44,52,58,64,60,50,42,32,26,18,12,2,2,12,18,26,34,44,50,60,68,76,76,68,60,50,44,32,24,14,8,2,82,90,98,108,116,124,132,140,126,116,110,100,92,82,72,62,52,40,30,20,14,4,6,14,22,32,40,50,58,70,8,18,26,34,44,52,60,68,78,86,92,104,112,120,128,136,108,116,124,132,140,152,2,10,14,20,4,12,16,24,24,16,12,4,4,8,14,20,2,6,6,6,6,16,24,34,42,52,60,68,74,86,92,104,48,56,62,68,36,10,10,10,10,24,24,24,24,38,38,40,70,44,44,44,44,70,54,54,78,84,90,108,138,108,108,108,108,138,138,138,138,106,198,164,164,164,164,198,172,172,172,182,182,182,182,170,170,170,170,184,184,184,184,170,186,194,194,194,194,184,164,150,166,144,166,150,168,150,150,168,182,182,182,176,176,166,166,152,152,152,152,174,174,192,192,194,194,194,194,190,190,190,190,180,180,178,178,178,188,164,164,164,164,178,178,178,178,164,164,164,164,192,192,192,192,180,180,180,188,192,192,174,174,174,174,192,192,192,172,166,166,166,166,170,170,170,180,192,192,192,190,190,190,186,186,186,186,190,182,182,182,170,170,170,170,184,62,64,100,100,100,100,100,100,100,100,100,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,100,1,2,2,2,2,2,8,8,8,4,4,4,198,198,198,198,198,166,166,166,166,116,116,116,116,84,84,68,68,68,68,114,112,112,112,128,128,128,128,146,146,146,146,158,158,158,158,168,168,178,178,198,90,90,90,92,92,92,92,90,90,90,90,88,104,110,110,112,112,112,112,102,72,72,72,136,136,178,178,178,178,156,156,156,68,68,68,42,42,42,42,42,42,32,32,44,44,70,70,70,70,70,70,80,80,122,122,150,150,154,154,168,168,184,168,148,148,148,148,154,154,154,154,146,146,146,146,164,166,164,162,146,146,156,150,150,132,112,112,112,112,118,118,124,124,124,124,112,112,112,112,122,126,126,126,128,128,136,136,138,138,138,138,134,134,122,134,136,136,136,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,86,84,78,76,70,68,66,60,58,54,30,30,30,30,50,50,50,50,76,4,6,82,86,86,188,194,188,152,144,144,144,144,114,114,92,92,68,62,2,2,2,2,6,6,76,76,116,126,124,124,108,108,108,108,120,120,128,128,128,128,128,128,2,74,88,88,114,114,164,164,166,166,166,166,126,126,98,98,80,80,26,26,2,2,2,2,2,2,26,26,34,34,34,34,32,32,2,2,2,2,34,34,100,100,104,104,126,126,152,152,188,188,188,188,188,188,130,130,130,130,116,116,26,10,2,2,10,10,100,100,114,114,114,114,142,142,144,144,164,164,164,164,176,164,144,168,168,168,170,170,172,172,172,172,174,174,176,176,178,178,182,182,186,186,198,186,132,1,4,4,20,20,36,36,52,52,114,114,116,118,174,176,176,184,188,188,192,196,196,196,198,198,198,198,164,164,156,156,130,130,130,130,114,114,114,114,128,128,146,146,154,158,158,158,2,2,2,2,84,84,68,68,48,48,16,16,2,2,2,2,2,2,14,14,32,32,46,46,68,68,92,92,92,92,98,98,104,104,122,122,130,130,138,138,140,140,154,154,158,158,166,166,170,170,178,178,178,178,188,188,198,198,198,198,172,172,162,162,140,140,140,140,128,128,118,118,88,88,84,84,58,58,58,58,30,30,10,10,10,10,32,32,66,66,92,98,118,118,152,152,176,176,198,198,198,198,196,196,164,134,90,90,44,44,44,44,68,68,68,68,70,70,132,132,132,132,70,70,48,48,48,48,48,48,62,62,62,62,64,64,106,106,110,110,110,110,86,86,86,86,100,100,102,102,102,102,134,134,134,134,192,192,192,192,164,164,148,148,148,148,192,192,192,192,178,178,178,178,176,176,166,166,142,98,50,50,18,18,18,18,20,20,56,56,56,56,74,74,86,86,138,138,138,138,100,100,100,100,94,94,68,68,68,68,48,48,40,40,32,32,24,24,20,20,10,10,10,10,12,12,28,28,40,40,42,42,42,40,40,40,12,12,12,12,20,20,26,26,32,32,32,32,40,40,56,56,62,68,68,68,46,46,50,50,68,68,68,68,50,50,54,54,72,74,74,74,84,84,106,106,140,140,144,158,144,144,136,136,136,136,132,64,176,176,186,186,186,186,180,180,174,180,184,184,190,190,190,190,180,180,178,178,178,178,150,148,148,148,148,148,148,148,148,148,178,180,176,176,176,132,140,140,178,178,178,178,176,176,174,174,128,100,94,94,90,90,90,90,100,100,108,108,118,118,118,118,106,106,102,102,92,106,106,106,102,102,102,102,104,104,104,104,106,106,106,106,110,110,110,110,110,110,108,108,106,106,100,100,102,102,104,104,104,104,112,112,118,118,118,118,112,112,106,106,94,94,94,94,96,96,100,100,108,110,110,110,112,112,114,114,118,118,118,118,118,118,108,140,154,154,162,162,162,162,144,144,142,142,130,130,130,130,140,66,44,44,30,30,30,30,46,46,74,86,62,58,62,60,60,60,62,62,74,74,74,60,84,86,62,72,72,72,64,64,62,72,72,72,80,80,62,62,62,62,66,66,80,70,70,50,50,50,50,50,62,62,62,62,48,46,56,56,58,58,58,58,48,48,46,48,56,30,30,30,30,30,30,30,30,30,30,30,30,30,28,28,26,26,16,16,16,16,6,6,2,2,2,2,10,10,10,10,2,2,2,2,16,16,18,18,30,30,30,30,20,20,20,20,30,30,30,30,20,20,20,20,30,30,30,30,22,22,22,22,34,34,50,50,60,60,48,48,24,134,124,124,120,120,120,120,132,132,138,138,144,144,144,144,134,142,144,146,152,154,160,162,166,168,174,176,182,184,188,188,190,192,192,192,182,180,174,172,152,146,142,140,134,130,128,126,124,122,112,108,106,104,100,96,92,90,88,86,84,80,72,64,62,60,52,50,48,152,190,194,194,194,190,190,162,162,140,140,138,138,136,136,136,136,118,118,104,114,152,180,190,190,190,122,122,122,122,122,136,136,144,144,144,144,138,138,132,132,130,130,120,120,114,114,106,106,104,104,100,98,98,98,102,106,88,88,86,86,86,86,76,76,76,76,90,90,90,90,108,108,108,108,112,112,130,130,142,142,144,144,144,144,136,144,148,112,112,112,132,132,140,140,140,130,130,130,132,132,134,134,138,138,138,138,138,138,138,138,138,138,136,136,128,128,128,128,128,128,132,132,154,154,162,162,162,162,154,154,148,148,140,140,126,126,118,118,118,118,122,122,146,146,146,146,140,126,110,110,94,94,92,92,84,84,84,84,86,86,102,102,104,104,112,112,114,114,124,124,126,126,132,132,134,134,138,70,50,50,48,48,48,48,64,64,36,36,36,36,56,56,56,56,44,44,42,20,20,20,20,108,124,124,136,136,152,152,164,164,186,186,190,190,190,190,176,176,158,158,158,172,172,172,162,162,160,160,160,160,154,154,148,148,144,144,138,138,132,132,130,130,130,130,104,94,90,90,80,80,74,74,66,66,54,54,44,44,42,40,40,40,56,56,62,62,72,72,74,74,82,82,90,92,98,98,104,104,108,108,114,108,108,108,114,114,134,134,140,140,140,140,132,132,132,132,124,124,122,122,122,122,110,110,106,106,106,106,102,102,100,100,98,98,98,98,100,100,100,100,122,132,138,138,138,138,148,148,154,154,154,154,174,174,174,174,168,168,158,158,158,158,146,42,42,42,38,38,16,16,16,16,4,4,4,4,18,18,18,18,10,10,10,10,14,14,18,18,30,30,32,32,40,40,44,44,56,56,60,60,66,66,84,84,86,74,44,42,18,18,18,18,12,12,10,10,10,10,8,8,8,8,18,8,8,8,20,20,20,20,10,10,10,10,42,42,44,44,52,52,54,54,62,180,180,180,194,194,194,194,184,184,184,184,192,192,192,192,182,182,182,182,182,198,168,168,120,120,120,162,162,170,170,170,168,168,164,164,160,160,160,160,168,168,170,170,170,170,164,164,160,160,160,160,166,166,168,168,170,170,170,170,170,170,168,168,166,166,162,162,168,168,170,170,172,164,160,160,158,158,158,158,166,166,168,168,174,174,174,174,162,160,160,160,166,166,168,168,176,176,176,176,170,170,168,168,162,174,174,174,174,174,172,172,172,172,182,182,170,170,170,170,170,152,162,162,170,164,120,118,104,104,104,104,120,144,144,144,134,134,134,134,150,150,150,150,154,154,160,160,150,150,150,150,160,160,160,160,150,150,148,148,148,148,148,148,144,144,132,138,148,136,136,106,98,98,94,94,88,88,82,82,82,82,86,86,96,96,96,96,86,86,78,78,58,58,58,58,62,62,64,64,74,74,74,74,60,60,50,50,32,32,32,32,36,36,38,38,50,50,50,50,28,146,160,160,160,150,162,150,148,140,160,158,146,146,146,146,162,162,156,156,154,154,140,140,140,140,148,148,142,142,140,140,140,140,146,146,154,154,154,30,22,22,18,18,18,18,22,22,30,30,40,40,40,40,34,34,32,34,38,38,42,42,42,42,34,34,22,22,14,14,14,14,22,22,16,16,10,10,10,10,18,18,22,24,28,16,14,16,10,10,8,8,8,8,16,16,18,18,26,26,18,18,14,14,8,8,8,22,22,22,32,32,18,18,18,4,4,4,14,140,178,180,186,186,192,192,192,178,178,178,190,190,192,192,192,192,190,190,174,156,186,186,186,112,112,112,112,146,146,146,140,140,136,136,136,136,144,144,144,144,136,136,136,136,142,142,142,142,134,134,134,134,142,98,96,96,88,88,88,100,100,100,98,82,86,86,94,94,94,86,78,76,76,76,76,76,88,90,90,90,96,96,108,108,128,132,136,136,148,148,160,160,160,160,152,152,152,152,160,160,160,160,154,154,144,144,142,142,134,134,132,132,122,122,108,108,94,94,94,94,84,84,84,84,72,72,72,72,76,76,84,84,84,84,86,86,92,92,102,102,102,102,118,118,118,118,122,122,142,142,148,164,164,164,166,52,52,52,54,54,60,60,60,60,48,52,50,50,48,48,48,48,52,52,58,58,60,60,60,60,56,56,52,52,56,46,58,58,56,58,64,56,48,62,62,56,60,64,72,64,60,56,54,56,54,54,54,54,60,60,64,64,70,70,70,70,42,64,66,60,56,64,54,64,68,60,58,58,58,58,58,58,60,64,64,64,62,62,56,56,56,56,58,58,66,66,70,70,70,88,88,88,78,92,92,92,102,88,88,88,92,92,98,98,98,98,102,102,108,46,46,46,46,46,38,38,34,34,26,26,26,26,38,42,42,34,34,148,152,152,152,148,156,156,158,164,178,178,178,172,180,172,156,134,148,148,150,150,150,122,122,122,134,134,140,140,140,140,134,134,130,130,130,128,142,54,54,54,54,56,64,66,66,66,66,66,66,66,56,8,42,20,20,20,2,20,42,42,42,42,10,10,14,14,40,28,28,4,28,28,28,28,14,28,36,36,40,40,40,40,40,40,36,36,32,30,6,6,6,6,38,38,38,56,56,56,70,70,74,74,76,76,76,76,52,52,56,56,70,72,56,52,42,42,46,46,56,56,64,64,70,70,70,60,60,74,42,42,42,42,46,46,60,60,60,60,42,42,50,50,66,66,74,74,42,40,72,42,42,42,46,46,70,72,70,70,46,46,44,44,50,50,72,72,40,40,40,40,40,46,68,40,40,40,40,40,46,46,50,50,66,66,66,114,110,110,94,94,96,96,96,96,110,110,116,116,134,134,134,134,106,106,106,106,122,122,122,122,116,116,112,104,102,102,98,98,98,98,108,108,108,108,98,98,98,98,100,100,102,102,106,106,106,106,94,94,92,92,80,98,84,100,110,110,110,110,88,132,132,132,172,172,174,174,180,180,180,180,172,172,170,170,178,178,178,178,170,170,170,170,180,180,180,180,176,176,172,176,184,184,184,184,176,176,174,176,180,180,186,186,186,186,182,182,178,178,134,126,124,122,124,126,126,126,114,124,114,120,120,120,130,116,120,120,130,130,132,120,134,134,134,134,134,134,126,126,124,134,132,132,120,138,134,134,126,126,126,136,136,136,144,140,136,136,130,130,130,172,190,190,190,190,198,190,186,186,186,186,190,190,194,194,196,196,196,196,194,176,176,176,180,180,190,190,190,190,198,198,198,198,194,194,190,190,188,188,188,188,188,188,186,186,172,172,170,170,170,170,190,190,190,190,190,190,162,172,162,174,174,174,176,176,188,184,176,176,170,170,170,170,178,178,188,178,176,188,188,188,186,186,180,186,190,190,190,180,158,168,168,168,174,174,184,184,184,184,182,182,182,182,146,146,146,146,152,152,166,166,166,166,166,166,180,184,186,186,188,188,188,188,178,178,178,170,170,170,170,170,168,168,164,164,142,168,186,186,186,186,186,186,186,186,148,148,148,8,28,16,16,16,6,16,26,24,4,8,10,10,24,8,8,8,12,8,12,12,24,14,14,6,26,8,8,8,24,24,10,10,28,14,28,28,12,12,18,18,18,18,12,12,12,12,12,12,20,20,20,20,10,16,16,22,28,28,36,36,36,36,28,90,198,90,90,90,96,96,96,96,122,122,122,122,128,128,128,128,132,132,198,198,126,118,196,196,110,100,198,198,94,94,198,196,196,196,196,196,172,172,172,172,164,164,160,160,160,160,168,168,168,168,176,176,176,176,198,166,166,166,166,166,166,186,186,186,188,188,192,192,192,192,196,196,188,188,188,188,184,184,184,192,188,188,188,188,184,184,184,190,190,190,196,188,182,182,182,182,184,184,188,192,192,192,198,194,186,186,186,186,182,182,178,178,178,178,174,174,174,174,170,170,170,170,174,174,174,174,176,176,180,180,180,180,184,184,184,184,196,124,124,124,110,126,126,126,126,126,122,122,112,112,112,112,104,104,102,102,102,102,104,104,112,112,112,116,116,116,122,122,128,128,128,128,126,126,112,112,108,108,104,104,104,104,110,110,114,114,116,78,78,78,74,74,62,62,60,60,60,60,62,62,70,70,74,74,74,74,74,74,78,138,184,184,126,126,126,126,134,134,134,134,138,138,138,142,142,154,154,162,162,168,168,172,172,114,108,108,104,104,104,104,102,102,92,92,88,96,96,96,92,92,88,88,88,88,78,78,76,76,76,46,46,80,80,80,80,80,126,126,126,126,102,102,94,94,80,80,78,78,78,74,112,112,112,112,116,92,92,92,106,106,132,132,132,132,146,146,146,146,126,126,106,106,84,92,92,72,68,68,64,64,64,64,70,72,78,78,78,78,74,78,72,72,72,72,72,76,70,76,76,76,74,80,112,92,92,92,106,106,126,126,130,130,130,130,138,138,138,138,134,134,122,134,134,134,128,128,102,98,98,98,88,118,118,118,114,112,112,112,104,104,104,128,116,116,116,128,132,132,132,16,16,16,2,10,10,18,18,18,26,26,26,26,34,34,34,34,42,42,42,18,20,20,50,12,12,12,14,14,18,18,22,22,22,22,26,26,26,20,54,56,22,20,24,24,24,24,28,28,28,28,32,32,32,32,36,36,36,36,44,44,58,58,58,4,4,4,4,8,12,12,12,12,8,80,80,100,100,100,100,100,100,100,100,100,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,100,80,80,100,100,100,100,100,100,100,100,100,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,100,2,4,4,198,198,198,198,180,180,156,156,154,154,94,94,94,94,88,88,52,52,2,2,2,148,192,192,198,198,198,198,188,188,188,188,186,186,162,162,162,162,148,148,144,144,144,144,120,120,120,120,56,56,56,56,62,62,80,80,90,90,110,110,126,126,142,142,154,154,154,154,144,144,144,144,142,142,142,142,144,144,130,82,70,70,64,64,64,64,62,62,46,46,46,46,28,28,28,28,12,12,12,12,2,2,76,76,86,86,86,86,42,42,42,42,10,10,2,2,2,2,10,10,10,10,2,2,2,2,12,12,12,12,2,2,2,52,52,52,62,62,74,74,74,74,96,96,104,104,104,104,126,126,134,78,78,78,90,90,90,90,90,90,90,90,78,90,94,94,106,106,106,106,92,92,92,108,108,108,90,90,88,88,96,96,114,114,114,96,88,88,88,88,86,86,80,80,80,90,90,90,90,90,102,110,120,130,140,148,160,78,78,70,70,82,82,82,90,90,90,110,118,108,116,102,106,98,98,98,102,74,74,66,66,88,88,114,122,106,114,110,120,78,78,100,100,100,100,100,100,100,100,100,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,100,44,72,72,146,146,178,172,144,138,64,64,38,46,60,60,78,78,132,132,146,146,162,162,168,168,154,154,132,132,76,76,54,54,44,60,60,60,66,66,76,76,76,76,74,74,66,66,66,84,100,100,108,108,110,110,124,124,126,124,116,116,108,108,98,98,82,82,80,20,28,28,36,46,50,38,30,30,26,26,20,34,18,18,14,14,12,12,20,30,38,32,26,14,6,6,14,8,18,18,26,26,34,36,44,32,24,24,32,38,46,52,44,42,32,32,24,12,4,4,12,12,20,20,28,42,50,50,58,50,54,54,56,56,58,38,28,28,22,12,4,4,12,8,2,2,10,10,18,18,26,6,2,82,84,84,98,98,114,114,124,124,134,134,140,140,134,134,130,130,130,130,126,126,92,92,82,26,26,26,56,56,88,88,88,88,88,88,116,116,116,116,186,186,186,186,194,194,194,194,174,174,174,174,126,126,126,126,94,94,162,162,162,162,190,190,190,174,174,174,146,146,146,146,88,76,76,84,84,84,96,96,96,96,180,176,146,146,122,122,90,50,50,50,36,36,36,36,52,52,52,52,32,32,32,32,54,54,54,54,24,24,24,24,36,36,36,36,18,18,18,18,38,38,16,16,16,16,38,38,38,52,52,58,58,62,62,62,68,68,74,74,78,78,78,78,72,72,66,66,66,66,66,66,68,68,112,16,16,16,8,8,8,8,14,14,18,18,20,20,20,20,28,28,28,28,24,24,6,6,6,6,8,8,22,8,6,6,4,4,4,4,8,8,8,8,14,14,14,14,66,66,66,66,98,98,98,98,110,110,110,110,126,126,126,126,124,124,112,112,112,112,128,128,130,130,130,130,144,144,160,160,162,162,162,162,160,160,126,126,126,116,120,18,70,70,98,98,98,98,94,94,20,20,20,20,2,2,2,2,12,12,14,14,14,64,96,96,146,146,76,76,76,76,76,76,88,88,96,96,96,66,38,38,38,68,68,68,80,72,90,90,90,90,88,54,66,66,66,66,64,66,66,66,44,44,40,40,40,40,44,44,48,48,48,56,76,76,78,78,78,78,102,102,106,106,106,106,144,144,144,144,138,138,126,124,100,138,198,186,144,186,190,190,192,192,192,152,152,152,150,150,102,100,54,38,30,30,14,14,14,14,22,22,32,32,40,40,44,52,56,56,56,56,118,118,128,128,128,146,154,154,158,158,158,158,162,162,174,174,182,188,196,82,38,38,6,54,144,144,198,198,198,188,186,186,168,168,166,166,162,162,162,162,160,160,100,100,90,90,66,66,56,56,50,50,26,26,2,2,2,30,30,30,14,14,14,12,6,6,2,2,2,2,2,2,14,4,4,72,100,100,102,102,102,102,182,182,184,146,114,70,62,62,56,56,46,46,32,32,22,22,18,18,10,10,6,6,2,2,2,2,40,40,54,66,82,82,84,84,84,84,86,86,102,102,164,164,164,164,124,124,112,112,102,102,102,102,96,96,86,88,98,98,102,102,108,108,114,114,126,126,160,160,184,184,184,184,162,184,188,188,188,188,166,166,166,166,178,178,178,178,164,164,162,162,154,154,152,152,140,140,132,132,126,126,126,126,122,122,120,120,118,118,112,112,100,100,88,88,78,78,76,76,66,66,66,66,54,54,52,52,40,40,30,30,26,26,10,10,10,10,18,18,18,18,4,4,4,4,36,36,36,176,176,176,178,178,186,186,186,186,182,182,182,182,178,178,176,176,154,154,154,154,160,160,166,166,166,166,68,68,68,68,82,82,96,96,96,96,152,152,158,158,178,180,198,198,198,198,188,188,188,188,198,198,198,198,196,196,192,192,110,110,108,108,76,76,74,74,66,66,66,66,94,94,94,94,70,70,68,68,50,50,50,50,70,58,36,36,36,36,36,36,46,52,114,114,114,114,128,128,128,128,128,122,34,34,2,2,2,2,34,34,36,36,38,38,38,38,64,64,76,76,78,78,90,90,106,106,108,108,122,122,124,124,136,136,138,138,154,154,156,156,170,170,172,172,188,188,188,188,170,170,170,170,168,168,152,152,142,142,128,128,102,102,88,88,88,88,58,58,36,36,70,70,168,144,144,144,148,148,144,144,136,136,126,126,122,120,108,108,2,108,198,198,198,198,198,198,198,198,198,198,198,198,2,2,2,2,2,2,2,2,2,2,2,2,2,2,6,6,36,40,94,94,104,104,152,152,152,152,154,154,174,174,174,174,158,156,156,156,180,180,134,84,64,64,62,62,62,66,76,76,98,98,120,120,138,138,154,154,154,154,104,104,92,92,92,92,96,132,132,132,74,74,74,74,86,86,86,86,84,84,78,78,78,78,98,98,100,100,100,100,102,102,114,120,120,120,132,132,158,158,158,158,154,152,110,110,110,110,118,120,156,156,170,170,170,170,168,158,158,158,158,158,92,92,70,70,70,70,94,94,114,118,122,122,122,122,156,124,94,94,88,88,56,54,54,54,100,42,42,42,74,74,74,74,34,42,42,70,146,146,146,146,76,76,76,86,86,86,132,132,132,132,132,132,90,90,90,148,176,176,176,176,148,148,174,174,174,174,150,52,52,52,56,56,56,56,52,52,52,52,56,56,56,56,52,62,62,90,138,138,138,138,92,92,92,92,98,98,120,120,128,128,140,140,140,140,122,122,122,122,122,122,104,104,96,96,96,148,150,148,146,144,148,144,154,144,148,72,80,72,72,72,78,78,78,50,18,18,12,12,12,12,32,32,38,38,72,72,48,48,48,38,38,20,20,6,6,172,104,104,104,104,86,86,86,86,108,108,108,108,174,174,174,174,170,174,174,14,28,28,28,28,10,10,10,10,28,8,8,8,2,2,2,2,12,48,48,48,56,56,56,56,36,36,32,32,32,32,34,34,62,62,62,62,26,26,26,26,30,30,68,68,68,68,66,66,38,38,38,38,38,38,60,60,60,78,182,182,182,182,172,172,172,172,168,142,142,142,156,156,156,156,72,72,72,72,134,134,144,144,152,152,154,154,158,158,174,174,178,178,192,192,192,192,182,182,182,182,192,192,192,192,182,182,182,182,194,194,194,194,184,184,184,184,194,194,194,194,160,160,130,130,126,126,126,126,124,124,68,68,56,56,56,56,76,76,76,76,58,58,58,58,56,56,42,42,20,20,20,20,26,26,26,26,32,32,40,40,48,52,52,52,62,1,4,4,14,14,14,14,62,62,62,62,98,98,98,98,136,136,136,136,160,160,198,198,198,114,114,114,134,134,134,134,156,156,156,74,24,42,42,110,154,154,154,154,88,88,88,90,150,150,150,150,162,162,162,162,154,150,86,86,86,86,86,118,108,108,108,108,136,136,136,136,142,142,150,150,150,150,162,162,162,162,172,172,172,172,168,168,166,166,158,158,158,158,150,76,18,44,36,36,36,36,44,44,44,28,22,22,22,22,36,36,36,36,40,40,52,52,52,52,66,30,28,22,20,28,18,18,18,18,48,48,52,52,52,52,62,62,66,66,66,66,66,66,70,70,84,84,92,92,92,92,78,78,78,78,86,86,86,86,80,80,80,80,84,84,84,84,42,42,42,42,56,56,58,58,58,58,66,66,72,72,72,72,52,52,30,30,18,18,18,18,30,30,30,30,22,22,22,22,28,28,28,22,26,26,26,26,28,28,58,58,58,58,92,92,92,92,108,108,108,108,146,146,196,18,70,70,70,34,68,50,50,24,24,24,36,36,46,46,68,68,74,74,80,80,80,80,68,68,68,20,20,20,60,60,62,62,62,62,68,44,60,60,84,84,88,88,98,98,162,164,164,164,152,152,12,12,12,12,28,28,28,28,44,16,16,16,22,22,94,94,108,108,108,12,12,12,72,72,84,84,84,98,88,88,78,78,78,78,82,82,98,98,118,88,80,80,76,76,76,76,84,84,90,90,114,122,122,122,164,166,166,166,120,120,120,120,132,132,168,164,164,70,70,70,112,112,114,114,126,126,130,130,132,74,108,90,90,74,74,74,106,108,108,108,72,72,72,82,86,86,104,18,20,100,100,100,100,100,100,100,100,100,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,100,28,28,28,18,18,18,18,8,8,8,8,2,2,16,16,16,16,30,30,30,30,40,40,40,52,52,52,40,40,40,40,26,26,26,26,2,2,14,14,14,14,24,24,24,24,36,36,36,46,46,46,34,34,34,34,18,18,18,18,2,2,30,30,30,30,46,46,46,46,60,60,60,198,190,190,190,190,180,180,180,180,172,172,172,160,160,160,172,172,172,172,188,188,188,188,198,198,180,180,180,180,156,156,156,156,144,144,144,198,188,188,188,188,178,178,178,178,168,168,168,154,154,154,166,166,166,166,176,176,176,176,198,198,164,164,164,164,152,152,152,152,138,138,138,116,116,116,112,112,66,66,76,76,114,114,114,114,114,98,98,98,98,98,48,48,50,50,94,92,92,92,100,100,100,72,72,72,68,68,16,16,16,16,72,72,72,96,96,96,110,110,110,110,134,134,134,134,162,162,162,162,112,112,104,104,82,82,68,68,68,68,64,64,54,54,50,50,50,50,6,6,6,6,62,62,62,62,90,90,90,124,124,124,66,66,64,64,64,74,74,74,82,82,130,130,144,144,144,144,142,142,74,60,60,60,64,64,116,116,118,118,118,190,190,190,184,184,168,168,168,168,172,172,176,176,176,176,196,196,196,196,176,176,160,160,158,158,158,158,172,172,196,196,196,196,176,176,166,166,162,162,162,162,168,168,196,28,28,100,100,100,100,100,100,100,100,100,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,100,192,192,192,114,114,92,92,92,92,120,120,144,144,154,154,154,154,158,158,178,178,180,180,180,180,184,184,188,188,188,194,194,194,174,174,174,174,166,166,164,164,160,160,160,160,150,150,110,110,110,110,104,104,104,104,84,84,84,84,78,78,78,78,120,120,164,164,164,164,170,170,170,170,174,174,174,174,128,128,106,106,104,104,84,84,82,82,60,60,58,58,32,32,10,10,10,10,16,16,16,16,8,8,8,8,16,16,20,20,22,22,22,22,14,14,12,12,8,8,8,8,22,22,22,22,18,18,14,14,8,8,8,8,20,20,20,20,6,6,24,24,18,18,14,14,8,8,8,8,24,24,76,76,76,76,28,28,28,28,76,76,76,76,26,26,26,26,76,76,76,76,30,30,30,30,78,76,76,76,30,30,30,30,76,76,76,76,30,30,30,30,76,76,76,76,26,26,26,26,78,78,34,34,34,34,76,76,76,76,78,78,30,30,30,30,86,86,86,86,90,90,92,90,26,26,26,26,24,24,16,16,16,16,30,30,30,30,14,14,14,14,22,22,22,22,14,14,14,14,32,32,40,40,64,64,94,94,118,118,140,140,142,142,198,198,198,198,186,186,186,186,190,190,190,190,182,182,182,182,196,196,196,196,180,180,180,180,198,198,198,198,116,116,116,116,110,110,110,110,94,94,94,94,124,144,146,146,160,160,160,160,164,164,164,52,86,86,88,88,128,128,128,128,88,88,44,44,60,60,60,60,56,56,40,40,40,40,78,78,78,78,72,72,72,72,58,58,62,62,62,62,60,60,54,54,50,50,48,48,50,50,90,90,96,96,96,96,136,136,98,98,96,96,60,54,48,138,138,138,150,150,152,152,152,152,146,146,144,140,132,132,132,132,156,156,156,156,132,132,132,152,152,152,152,152,132,136,136,136,156,156,156,156,132,132,132,132,132,132,164,164,164,164,132,132,132,132,132,132,142,142,168,168,182,182,182,182,164,164,136,136,136,136,118,118,118,118,170,170,170,170,128,128,128,128,168,168,168,168,126,126,126,126,174,174,174,174,122,120,120,120,142,168,172,172,172,172,126,126,126,126,126,126,182,182,182,182,182,182,122,122,122,122,174,178,178,178,172,126,116,116,116,116,110,110,102,102,88,88,88,88,94,94,106,106,114,114,114,114,110,110,94,94,88,88,88,88,90,90,98,98,104,104,104,104,98,98,92,92,92,92,94,94,106,106,108,108,108,108,102,92,94,94,100,104,106,106,106,106,106,96,96,96,100,100,100,98,96,96,96,96,96,96,104,106,104,104,102,102,102,102,106,106,112,112,112,112,102,102,88,88,84,84,84,84,92,92,102,102,106,106,106,106,100,100,96,96,96,106,102,102,96,96,96,96,98,98,102,102,108,108,108,108,104,104,100,100,100,100,102,106,108,108,112,112,112,112,106,106,90,90,88,88,88,92,94,94,100,100,102,104,104,104,100,90,88,88,84,84,84,84,86,86,96,96,100,80,128,128,128,128,38,38,32,32,32,32,96,102,104,108,120,120,120,120,46,44,44,44,120,120,120,120,114,114,102,98,90,90,40,40,40,40,80,104,104,104,58,58,58,96,96,96,62,62,62,62,92,92,92,92,66,66,66,66,86,86,86,86,68,68,80,88,88,88,98,98,108,108,118,118,118,118,104,104,98,98,98,98,108,108,114,114,114,114,108,108,102,102,92,92,92,92,94,106,112,112,112,112,110,94,88,94,98,112,112,112,104,104,90,90,86,86,86,86,92,110,112,112,110,110,100,100,94,94,94,94,96,104,108,108,108,108,104,92,88,88,92,94,104,104,108,108,114,114,114,114,110,104,96,94,90,90,90,90,92,126,132,132,132,132,126,126,122,122,116,116,116,116,120,120,128,128,132,132,132,132,128,128,120,120,116,118,124,124,128,128,128,128,126,120,122,128,132,132,132,128,122,122,116,116,112,112,112,112,120,120,122,126,128,130,130,122,120,120,116,116,116,116,118,118,126,126,128,128,124,124,122,122,122,122,126,84,80,80,76,76,76,76,78,78,82,82,86,86,86,86,84,84,84,84,88,90,94,94,94,94,90,90,84,82,78,78,78,78,82,82,88,88,90,90,90,90,86,86,76,76,72,72,72,72,78,78,88,88,90,90,90,90,80,80,76,76,74,74,74,74,78,78,82,86,90,90,90,90,88,88,86,82,76,76,76,76,78,78,86,86,88,88,86,86,82,82,78,78,78,78,80,88,88,88,82,80,80,62,60,60,60,60,70,70,76,80,84,84,84,84,78,78,72,72,64,64,64,64,66,66,78,78,82,82,82,82,72,72,66,66,66,66,72,72,76,80,82,82,84,84,84,84,74,72,68,68,64,64,64,64,68,68,78,78,80,80,80,80,66,72,74,80,80,80,74,74,62,62,60,60,60,60,64,64,78,78,80,80,80,80,74,68,64,64,64,66,70,70,80,80,84,84,82,76,68,68,68,68,72,46,46,46,122,122,34,28,28,28,122,130,196,196,130,142,146,146,196,196,196,196,190,190,188,194,126,126,126,126,194,190,190,126,126,126,126,126,188,192,126,198,194,194,126,30,30,30,20,20,20,20,20,20,4,4,4,4,20,20,22,22,34,86,80,26,22,26,28,28,36,36,40,40,48,48,80,86,122,120,120,120,2,2,2,2,6,2,10,2,2,2,104,104,122,124,118,118,112,112,112,112,114,114,114,124,128,128,128,128,146,146,146,146,156,166,168,168,168,168,164,164,162,162,162,162,164,164,164,164,162,164,166,166,166,166,162,162,160,160,156,156,156,156,160,160,164,164,164,162,160,160,158,158,152,152,152,152,154,154,160,164,166,166,166,166,162,162,158,158,154,154,154,154,156,160,160,160,166,166,168,168,182,182,182,182,174,174,154,164,158,158,130,130,130,130,138,138,146,146,170,170,170,170,160,160,156,156,150,150,146,146,136,136,128,144,144,150,136,160,160,160,172,172,176,176,184,184,184,184,152,152,150,150,148,148,148,148,150,150,154,142,142,142,146,146,160,160,162,162,162,162,146,146,142,142,150,164,164,164,180,180,196,196,196,196,188,188,178,178,174,174,174,174,174,174,174,174,172,172,162,158,158,158,162,162,164,156,152,148,146,142,140,132,130,128,124,122,120,116,124,126,130,132,134,138,140,142,144,112,112,112,114,116,122,124,126,130,130,116,122,124,126,130,132,108,110,110,118,120,122,122,124,126,128,130,134,82,82,82,80,80,80,88,88,88,90,90,96,96,96,96,94,94,84,80,68,68,60,60,60,60,68,68,68,68,48,48,48,48,60,60,70,70,86,86,86,86,76,76,76,76,84,84,80,80,72,72,66,66,62,62,62,62,66,68,78,78,78,78,74,74,66,66,62,62,62,62,60,42,42,42,48,48,48,48,40,38,38,38,76,76,78,78,78,12,22,22,12,12,22,22,14,12,22,6,6,6,20,20,20,8,22,22,22,8,18,102,102,102,118,118,118,118,110,110,86,86,76,76,76,76,104,94,94,94,94,94,94,94,94,94,94,94,94,94,94,100,100,108,108,108,100,100,76,76,70,70,38,38,38,162,192,192,192,192,164,164,164,164,164,164,156,156,156,156,164,148,168,146,164,164,164,140,140,140,144,144,144,166,194,194,190,190,180,180,166,166,160,160,158,158,154,154,152,152,150,150,150,150,146,146,140,140,140,140,136,136,136,136,132,132,132,132,130,130,130,130,126,126,126,126,124,124,124,124,118,118,118,118,116,116,116,116,114,114,114,2,2,2,2,2,198,198,198,198,2,2,2,2,2,34,34,34,50,50,50,50,60,62,50,50,50,50,36,50,50,52,98,98,98,98,48,56,56,56,62,62,64,64,64,64,72,58,58,58,62,62,64,64,64,62,64,64,78,78,198,62,78,78,114,114,118,118,142,142,146,146,156,156,158,158,168,168,198,100,102,102,110,98,98,98,92,92,92,102,96,96,72,72,72,72,76,76,76,76,96,92,92,92,106,106,106,106,102,102,98,102,198,98,98,98,92,98,198,166,166,166,192,180,180,156,156,156,170,170,170,170,156,156,170,160,160,146,146,146,128,128,128,128,142,126,144,132,132,132,132,58,82,82,60,60,58,58,58,58,86,58,46,46,46,46,44,44,42,42,30,30,28,28,28,28,24,24,22,22,20,20,18,18,16,16,14,14,10,10,8,8,4,40,198,196,178,76,76,68,68,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,38,38,38,44,44,44,40,40,48,48,48,38,46,46,38,38,38,198,134,130,58,38,38,28,28,26,26,26,26,20,20,18,18,18,18,14,14,12,12,8,8,6,6,6,6,8,8,10,10,10,10,14,14,14,14,20,20,22,22,24,24,26,26,28,28,30,30,32,32,34,34,36,36,40,14,14,14,16,16,18,18,18,18,20,20,22,22,20,20,22,22,24,24,24,24,22,22,22,22,20,20,18,18,14,14,12,12,10,10,8,8,6,6,6,6,8,8,8,8,10,10,10,10,14,14,10,10,10,10,12,12,12,12,14,14,14,14,18,18,22,22,22,22,20,20,20,20,18,18,10,10,10,10,6,6,6,6,8,8,8,8,10,10,10,10,12,12,12,12,14,14,14,14,18,18,18,18,14,14,14,14,16,16,16,16,12,12,10,10,6,164,198,164,44,44,38,38,38,38,50,50,36,36,36,36,36,36,38,50,50,50,54,54,58,58,58,58,62,62,64,64,64,54,54,54,36,36,36,36,54,38,54,46,46,46,74,2,2,100,100,100,100,100,100,100,100,100,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,100,78,78,78,62,62,62,62,76,68,78,78,78,78,62,62,62,62,68,70,90,90,90,86,98,98,98,98,86,44,48,48,52,52,52,52,48,48,48,48,56,56,56,56,50,50,50,50,56,56,56,56,52,52,52,52,144,144,154,154,154,154,140,140,130,130,126,126,126,126,128,128,134,134,136,136,136,136,134,134,40,40,36,36,36,36,40,40,30,30,30,30,36,36,28,28,28,28,34,34,34,34,32,32,30,30,30,30,34,34,40,40,46,52,62,76,104,104,116,116,116,116,96,114,144,144,144,144,140,140,72,72,72,72,62,62,62,62,70,70,70,70,148,148,160,160,160,160,166,166,198,198,156,60,50,50,48,48,48,48,42,42,34,34,24,24,36,36,38,38,38,38,36,36,34,34,18,18,16,16,8,8,8,8,12,12,20,20,20,20,16,16,12,12,12,12,16,16,20,20,22,22,22,22,18,18,12,12,12,12,18,18,30,30,34,34,34,34,40,40,44,44,44,44,52,52,70,30,60,36,36,36,60,60,38,18,18,18,64,64,80,80,80,80,64,64,64,64,22,22,22,22,16,1,2,2,2,2,160,160,174,174,174,174,148,148,142,142,134,134,62,62,42,42,42,42,62,62,72,72,72,98,98,62,62,62,102,102,102,102,56,56,56,56,80,80,80,80,80,80,56,56,56,56,100,100,100,100,100,100,54,54,54,54,98,100,98,76,76,76,76,176,114,114,114,114,170,172,176,138,138,138,150,150,150,150,154,154,182,182,198,198,198,198,166,42,22,22,22,22,2,2,2,2,2,2,26,26,28,28,38,40,48,64,64,64,26,24,62,46,46,60,30,48,48,48,48,80,80,80,82,82,108,108,114,114,114,114,104,104,100,100,100,100,94,94,90,90,88,88,88,88,96,96,98,98,110,110,110,110,100,100,88,100,106,98,92,92,92,92,98,98,108,108,98,98,86,98,104,96,90,90,90,90,100,100,106,106,108,108,100,100,96,96,88,88,88,88,92,92,104,104,104,104,90,90,88,90,96,96,98,104,116,90,110,96,96,96,96,134,134,134,146,146,176,176,176,176,174,174,166,166,168,168,178,178,164,164,164,164,172,172,174,174,174,174,166,166,164,164,160,160,160,160,164,164,172,172,174,174,174,174,166,166,156,166,172,164,160,160,160,160,166,166,176,176,160,160,160,160,162,162,164,164,164,164,162,162,158,158,158,158,160,160,160,160,164,164,170,170,170,170,166,166,158,158,156,156,156,156,168,168,170,170,170,170,166,152,152,164,164,154,148,148,144,144,144,144,148,148,162,162,176,176,176,160,154,154,144,144,144,144,152,152,160,160,164,164,174,174,174,10,10,10,28,10,10,10,14,14,16,16,20,20,24,24,30,20,20,28,28,28,24,24,16,16,16,16,20,20,24,14,28,20,20,20,28,28,28,28,18,18,18,18,32,28,30,30,30,32,40,40,40,28,28,28,18,18,30,12,12,12,12,12,16,16,18,12,16,16,16,26,28,52,52,52,54,54,80,80,80,80,74,74,72,72,68,68,56,56,56,124,124,124,142,142,142,142,138,138,134,134,134,134,138,138,142,142,142,136,134,134,118,118,118,118,132,132,132,132,120,120,120,120,124,124,124,124,114,114,116,116,118,118,122,122,122,134,136,136,136,58,74,52,56,56,62,62,62,50,68,56,54,54,48,48,48,48,58,58,60,60,64,60,78,58,58,58,66,68,72,72,72,72,74,74,78,70,70,70,62,12,32,12,32,20,20,64,82,60,80,70,70,192,172,180,194,192,192,192,188,188,174,180,192,66,66,66,76,76,80,80,92,92,92,92,80,80,76,76,60,60,60,70,70,70,72,72,92,92,98,98,98,98,92,80,74,90,78,78,76,76,76,76,80,80,94,94,96,96,96,96,94,94,66,66,60,60,60,60,72,72,80,80,80,80,68,68,62,62,50,50,50,50,70,70,70,70,72,72,90,90,92,92,92,92,50,50,46,46,46,46,48,48,58,58,60,60,60,60,60,60,74,74,76,76,76,76,76,76,54,54,44,44,44,44,60,60,64,54,50,50,38,38,38,38,50,50,60,44,38,38,38,38,36,36,24,24,24,24,58,58,58,58,36,36,36,36,66,66,66,66,66,66,54,54,52,52,52,52,50,50,38,38,36,36,18,18,18,18,20,20,36,36,62,62,86,86,86,86,90,94,100,100,102,102,102,102,90,90,88,88,88,88,86,86,44,44,40,40,40,40,38,38,22,22,10,10,10,10,18,26,28,28,38,38,42,42,56,56,64,64,68,68,78,78,96,96,96,96,96,96,78,78,74,74,74,74,72,72,28,28,22,22,22,22,12,12,10,10,10,10,32,32,32,32,56,56,58,58,58,58,96,78,74,74,74,74,72,72,32,32,30,30,30,30,28,28,12,12,10,10,10,10,22,22,22,22,22,22,40,40,40,40,56,56,56,56,70,70,74,74,74,74,80,80,80,80,70,56,40,22,16,16,14,14,14,14,14,14,14,14,44,44,44,44,56,56,62,62,62,100,172,144,144,124,124,142,132,132,132,132,142,142,142,142,154,154,154,154,152,152,152,152,150,150,142,126,112,112,112,112,116,124,120,120,110,124,126,126,134,134,134,118,136,136,136,124,124,142,142,142,138,138,134,142,138,138,132,142,146,146,152,138,148,148,148,124,116,116,116,124,132,132,138,122,128,128,134,124,116,116,110,174,174,176,162,186,148,180,166,184,152,180,170,182,158,184,180,180,168,168,166,182,160,180,164,184,174,174,170,170,164,176,160,160,164,164,176,176,184,172,160,160,178,160,164,164,176,176,184,170,184,166,170,170,178,178,180,2,6,6,60,42,44,44,56,32,48,24,24,24,36,14,28,6,6,6,18,48,52,52,60,112,112,112,112,132,132,156,156,144,136,126,116,1,4,4,6,6,10,28,50,60,60,60,72,56,56,74,74,74,48,76,76,80,80,80,80,80,74,54,54,54,54,58,58,58,64,64,64,64,64,64,56,56,56,56,56,56,68,62,74,74,74,74,76,76,76,76,78,78,78,78,82,82,86,86,92,92,92,74,60,58,58,66,66,74,74,74,56,56,56,56,60,60,68,70,70,70,82,82,82,82,70,70,64,62,72,74,74,74,44,42,42,60,60,60,60,64,74,76,76,74,68,66,66,66,66,66,72,74,74,74,74,74,74,74,78,78,80,78,76,76,74,74,72,72,68,68,68,68,72,72,76,76,76,76,72,72,72,72,72,72,58,58,58,58,70,56,46,46,46,58,58,66,42,46,62,62,66,66,66,66,68,68,68,68,68,68,70,70,70,70,72,72,74,74,74,74,72,72,72,76,80,80,82,82,82,82,82,82,80,76,56,54,54,54,54,54,54,54,74,76,76,76,86,88,88,88,108,110,110,110,96,94,92,92,92,98,104,106,106,106,96,96,96,96,100,100,102,102,102,104,108,108,108,108,90,90,90,90,94,94,100,100,100,94,94,94,98,98,110,110,110,110,98,98,90,88,88,88,88,88,88,96,100,102,102,102,104,104,106,106,106,106,86,86,86,86,90,90,90,90,86,86,86,86,100,102,102,102,86,86,86,92,94,94,94,94,92,92,94,94,96,96,96,96,96,96,98,98,98,98,98,98,100,100,100,84,68,68,62,62,62,62,72,72,84,50,50,50,66,78,94,94,104,104,184,184,184,184,176,176,44,42,42,42,88,92,92,92,108,108,118,118,132,132,198,198,198,198,188,188,88,88,80,80,80,150,150,150,188,188,198,198,198,78,78,78,76,76,36,36,8,8,8,8,74,76,76,76,76,76,76,76,76,76,74,74,42,42,32,32,32,32,40,16,16,16,66,66,78,176,192,192,192,192,182,182,182,182,180,180,20,20,12,34,20,20,20,20,40,40,40,40,30,4,30,30,30,30,30,30,30,30,30,30,30,40,66,66,66,66,32,32,32,32,18,2,2,2,14,14,108,118,150,188,196,196,198,198,198,198,198,198,144,144,104,104,56,56,56,56,78,2,2,2,26,26,26,26,6,6,6,6,30,30,30,30,2,2,2,2,30,30,30,30,2,2,2,2,46,46,50,50,54,54,64,64,64,64,58,58,58,58,52,52,52,52,46,46,46,46,62,62,62,62,60,60,60,60,58,58,58,58,60,60,58,58,56,56,58,58,86,86,122,122,124,124,156,156,158,158,180,180,180,180,152,152,104,104,72,72,72,72,60,60,70,70,88,88,88,88,98,98,98,98,110,110,116,116,130,130,174,174,194,194,194,194,188,188,188,188,110,110,106,106,106,106,90,90,68,62,56,56,56,100,100,100,108,108,116,116,116,116,112,116,128,128,138,138,140,140,144,144,150,150,158,158,162,162,162,162,154,154,154,144,142,142,142,142,148,148,172,172,190,190,190,190,154,156,190,190,190,190,162,162,162,162,150,150,144,144,144,144,142,142,120,120,62,40,40,40,38,38,46,46,46,46,32,32,32,32,46,46,46,46,32,56,56,56,84,84,106,106,122,122,122,122,98,98,96,96,96,96,74,74,74,74,52,76,84,84,100,100,102,102,132,132,132,132,120,120,120,120,156,156,164,164,174,174,190,190,190,190,166,166,154,154,152,152,152,152,164,164,164,164,148,132,150,150,152,152,152,140,132,144,152,198,198,198,194,194,86,96,96,96,96,96,80,80,64,64,46,46,46,46,70,70,70,70,60,70,82,82,100,100,100,100,96,96,90,90,90,90,44,44,44,68,68,68,46,46,38,38,38,38,20,20,44,44,50,50,50,50,20,20,20,20,36,36,80,80,86,86,114,114,120,120,120,120,118,118,116,116,102,102,96,96,88,88,88,88,88,88,88,100,100,100,106,106,124,124,136,136,136,136,148,148,180,198,198,164,162,162,162,162,162,162,162,162,160,160,156,156,156,156,156,156,162,162,176,176,176,192,194,198,198,198,190,190,182,190,198,198,198,198,180,180,174,174,170,170,156,156,154,154,152,152,150,150,148,148,148,148,148,148,124,122,44,44,30,30,30,30,52,52,64,64,96,96,110,140,142,142,144,144,150,150,160,160,160,166,166,166,148,148,148,148,150,150,162,150,130,130,30,30,30,30,46,46,72,72,72,72,82,82,84,84,84,84,82,82,82,82,80,80,64,64,60,60,48,48,44,44,44,44,46,46,72,72,154,1,4,4,8,8,28,28,28,28,68,68,68,68,38,36,66,66,66,66,72,72,80,80,198,198,198,188,70,42,42,42,44,44,44,44,44,44,38,38,36,36,36,36,194,188,64,18,4,18,60,60,60,60,34,34,34,34,158,158,158,158,166,166,188,188,192,192,198,198,198,198,198,198,2,10,104,104,104,106,198,198,198,198,198,198,106,96,18,14,18,18,18,24,30,30,30,30,30,30,30,30,184,184,184,184,172,172,172,172,166,166,166,166,152,152,140,140,140,62,20,24,58,58,20,20,56,56,22,42,42,42,52,40,40,44,52,52,54,22,56,56,56,38,38,20,20,74,80,80,104,80,84,84,100,100,102,92,92,92,102,92,76,76,104,78,78,78,86,86,86,86,94,94,102,128,128,128,154,154,154,154,166,166,166,150,150,150,144,144,144,144,166,166,170,170,170,170,152,160,160,160,170,170,160,160,160,160,140,164,174,174,174,142,142,142,168,168,168,168,158,158,156,156,156,156,156,156,168,152,172,152,148,148,144,144,144,144,136,144,168,152,152,152,164,164,164,164,174,174,174,156,156,156,156,198,178,178,178,178,186,186,186,186,176,176,176,176,194,194,184,184,184,184,196,196,196,186,198,190,190,190,196,188,188,188,178,178,178,178,182,182,186,178,196,196,196,182,192,192,192,192,180,192,198,198,198,32,64,104,108,98,104,108,172,98,64,28,28,28,28,28,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,152,150,150,142,142,140,140,140,140,134,134,132,132,132,132,130,130,124,124,124,124,112,112,112,112,98,98,98,98,84,84,84,84,68,68,68,68,52,52,52,52,66,66,66,66,68,84,90,90,90,90,100,100,100,100,110,110,110,110,118,118,118,118,128,128,128,128,136,136,136,136,136,136,158,158,152,150,144,2,10,10,10,10,18,18,18,18,26,26,26,26,36,36,36,36,28,28,28,28,20,18,18,18,2,2,2,2,10,10,10,10,18,18,18,18,28,28,28,28,18,18,18,18,10,10,10,10,2,10,198,198,198,198,198,198,152,150,46,42,46,42,2,2,198,198,198,70,70,68,58,58,58,58,58,58,178,68,68,72,72,72,72,72,174,180,178,180,186,186,196,196,196,196,190,190,184,184,180,174,184,184,186,186,186,184,192,180,184,132,132,132,138,138,138,138,130,130,130,130,146,146,146,146,122,122,122,128,130,128,122,122,122,130,122,122,122,122,130,130,130,130,134,108,116,116,116,116,108,108,108,108,108,108,108,114,114,114,102,102,102,102,78,112,112,112,98,98,98,98,98,98,98,98,76,76,76,76,76,76,76,76,76,76,76,76,80,80,80,84,84,84,100,100,100,100,82,82,82,84,100,100,80,82,82,86,88,86,80,80,80,88,96,96,96,96,80,84,98,84,76,82,96,76,72,72,72,76,80,76,72,72,76,72,68,72,72,72,78,78,78,46,46,46,46,46,46,46,158,158,158,158,46,46,46,50,50,50,50,50,36,36,34,34,34,34,24,24,24,24,28,28,28,28,18,18,18,18,24,24,28,28,28,28,22,22,18,18,10,10,6,6,6,6,14,14,16,16,24,24,24,24,22,22,18,18,14,14,12,12,10,10,10,10,14,14,14,14,4,4,4,4,10,10,16,16,22,22,24,24,32,32,32,32,20,20,20,20,38,38,38,38,48,48,50,50,50,50,50,88,88,88,92,92,94,94,94,94,92,92,88,88,88,88,86,86,86,86,84,84,84,84,84,84,86,86,86,86,88,90,94,94,94,92,92,92,94,94,98,98,98,98,96,96,94,92,88,92,92,88,88,88,90,90,92,94,96,82,60,60,60,60,70,70,70,70,56,56,56,56,88,88,88,88,70,70,70,70,70,70,86,86,86,86,82,82,82,76,76,84,84,84,80,80,70,70,68,68,64,64,54,54,54,54,62,62,62,64,68,68,68,68,74,74,80,80,82,82,84,84,88,92,96,100,96,82,54,54,54,54,58,60,76,76,76,76,48,48,48,48,80,80,82,82,82,82,82,82,82,82,58,58,58,64,58,64,84,84,86,86,86,80,78,110,110,110,114,114,134,134,140,140,140,140,132,132,132,132,128,132,134,128,116,116,116,116,106,116,144,144,144,144,118,120,120,120,118,118,114,114,114,114,108,108,108,108,116,138,122,122,114,114,98,98,98,98,100,100,114,114,114,114,142,142,142,142,106,106,88,88,88,88,96,98,100,100,102,106,106,106,114,118,144,142,138,124,124,124,124,120,120,190,156,156,156,156,164,164,164,164,168,168,172,172,178,178,180,180,184,184,184,184,192,192,192,192,188,188,186,186,186,170,170,170,166,166,166,172,172,172,172,172,164,164,164,164,196,164,164,164,156,156,154,154,154,154,158,158,162,164,164,164,152,162,198,198,198,56,154,154,158,158,158,158,152,152,64,62,56,56,56,74,74,74,78,78,84,84,90,90,90,90,84,84,78,78,72,72,72,80,80,80,82,82,90,90,90,90,82,82,88,88,92,92,92,92,84,84,82,82,78,78,78,78,82,84,84,84,94,94,94,94,86,122,132,132,132,132,124,124,124,124,128,128,130,130,130,130,124,124,124,70,70,70,70,96,66,66,66,66,98,84,84,68,98,72,72,72,78,78,80,80,86,92,92,92,100,98,66,66,66,66,98,86,86,142,144,142,118,118,118,120,120,120,134,134,134,138,150,152,152,128,148,150,150,150,130,150,156,168,196,168,172,172,176,176,178,178,186,186,186,190,194,194,196,196,198,198,198,172,198,56,50,50,50,50,42,42,42,42,32,32,32,32,42,42,42,42,50,50,50,50,60,60,60,34,28,28,26,26,26,26,32,32,36,94,110,110,110,110,106,106,106,106,100,100,100,100,94,94,94,90,110,110,110,110,100,100,100,100,94,94,94,94,86,94,88,82,88,84,84,80,90,82,82,62,64,1,4,4,40,40,72,72,72,72,78,78,78,114,114,74,84,84,116,120,122,122,128,128,128,128,128,128,144,154,150,108,92,92,76,76,76,76,86,86,92,92,96,96,102,102,104,172,190,192,190,116,124,174,178,178,198,198,198,188,94,94,94,94,138,138,142,120,120,120,92,92,92,92,104,104,180,160,96,92,84,84,84,84,94,94,164,164,172,172,172,164,136,136,92,92,82,82,48,48,48,48,62,62,70,70,66,66,2,2,2,2,40,40,40,40,52,52,62,62,62,62,62,62,48,48,48,48,62,62,76,76,76,76,94,94,154,154,160,160,160,160,160,160,180,180,182,182,182,182,198,198,198,198,182,182,182,4,10,10,70,58,54,54,26,24,40,40,58,58,160,164,186,198,198,192,44,44,44,44,80,80,80,80,48,48,48,48,76,76,76,76,32,32,32,32,36,36,136,136,148,148,148,148,116,116,68,68,66,64,64,64,130,130,162,162,164,164,164,164,152,152,152,152,156,156,142,142,110,110,110,110,134,134,134,134,130,130,116,116,116,116,6,6,6,6,50,50,62,62,62,62,76,76,78,78,82,82,82,82,84,138,92,58,18,18,2,10,66,66,68,68,68,68,70,70,108,108,114,114,114,114,120,138,138,138,130,130,114,114,64,44,112,114,114,114,124,118,118,118,50,50,50,50,36,36,36,36,44,36,32,32,30,30,30,30,28,28,28,28,26,26,26,26,28,28,28,28,26,26,26,26,30,30,30,30,28,28,28,28,38,58,42,42,42,42,48,48,48,48,58,58,58,58,46,58,58,58,48,58,58,58,56,56,52,52,36,36,32,32,32,32,42,50,50,50,52,58,52,52,56,56,82,82,82,82,70,96,96,94,94,94,94,94,72,72,72,72,48,48,48,48,52,66,66,26,26,26,52,52,52,38,70,72,72,72,92,98,98,98,96,68,94,96,94,68,68,68,68,68,70,114,120,120,130,130,130,130,130,130,142,142,144,144,152,174,178,178,178,178,174,174,174,174,126,126,120,110,106,106,92,88,88,88,48,28,60,60,60,60,66,66,66,72,104,104,104,90,84,84,84,84,94,94,98,98,98,100,120,120,124,124,124,114,92,92,90,88,88,88,88,84,74,74,26,18,18,18,28,28,30,30,30,30,50,50,52,52,52,52,52,54,58,62,70,70,78,86,88,88,88,88,110,110,116,116,116,116,116,116,152,156,156,156,170,170,176,176,176,176,178,178,182,182,184,184,184,184,128,128,128,128,120,114,114,114,112,112,86,56,56,56,56,60,70,70,104,104,116,120,148,148,148,148,140,140,134,134,134,134,126,126,108,108,108,108,138,138,138,138,186,186,118,118,118,118,122,132,122,98,82,82,82,82,100,100,100,100,92,92,110,112,112,112,92,92,82,82,80,68,52,1,4,4,24,24,24,24,48,34,28,28,28,28,30,30,32,32,36,36,36,70,88,82,80,80,80,80,86,86,92,92,92,86,28,20,24,24,76,78,78,78,90,90,90,90,70,70,70,74,86,92,144,146,146,146,74,74,74,74,78,78,54,54,54,54,74,74,74,74,62,62,62,62,70,70,70,70,66,70,120,120,124,124,86,86,86,86,90,90,120,120,120,120,92,92,92,92,118,92,92,92,118,118,108,108,102,102,102,102,106,106,110,110,110,110,108,108,104,108,118,98,96,96,96,96,98,98,98,98,112,112,112,112,108,108,100,100,100,100,114,114,114,108,98,98,96,96,96,96,98,98,100,100,100,100,98,98,98,100,104,104,104,104,94,94,94,94,96,96,100,100,102,102,102,102,100,100,94,94,90,90,90,114,148,114,146,118,118,132,136,136,136,116,116,116,148,148,148,114,150,152,150,182,182,182,176,176,176,176,186,186,186,186,178,178,178,178,188,188,188,188,178,178,178,178,190,190,190,190,180,180,180,180,190,190,190,190,180,180,180,180,192,192,192,192,180,180,180,180,194,50,50,50,108,112,112,112,74,74,74,74,74,74,74,74,64,64,64,64,56,56,56,56,48,48,48,64,82,82,82,82,94,94,94,94,110,110,110,110,90,90,90,90,80,80,80,80,68,68,68,68,68,68,68,68,62,62,90,90,98,98,102,62,54,54,54,60,64,66,64,60,50,66,94,78,78,70,70,176,172,172,158,158,148,148,148,148,158,158,158,158,150,150,150,150,158,158,166,168,168,168,162,162,162,162,168,168,174,174,174,130,102,108,108,108,108,108,122,122,130,128,124,42,24,24,24,24,42,34,34,72,64,72,102,102,102,102,100,100,70,70,70,66,62,60,58,56,56,56,72,74,66,62,60,58,56,56,56,56,68,68,68,68,66,66,64,64,62,62,62,62,66,66,66,66,66,66,70,70,76,76,76,76,66,66,64,60,60,134,138,138,124,124,76,70,44,44,26,26,26,26,36,36,134,148,160,160,160,160,134,134,134,134,148,148,148,148,140,140,140,144,144,144,142,142,134,134,134,100,96,96,86,86,84,84,80,80,80,80,86,86,90,90,90,90,90,90,98,98,104,104,106,106,106,106,102,102,100,114,114,114,122,122,122,122,110,110,110,110,114,126,122,114,112,114,114,114,114,108,108,108,110,110,118,118,122,122,122,122,116,114,106,24,10,10,8,8,8,22,6,22,26,20,12,12,12,12,16,16,18,18,18,18,12,12,22,22,12,12,12,12,16,16,20,20,24,16,16,16,24,16,16,26,16,16,16,26,22,22,22,22,28,66,68,68,68,68,66,66,66,66,68,68,70,70,72,72,72,72,64,64,64,64,64,64,76,76,84,84,86,86,86,86,74,74,64,64,64,64,70,72,72,72,68,68,58,58,54,54,54,54,58,58,72,72,72,72,70,70,64,64,56,20,20,20,20,20,16,16,16,16,16,16,16,16,26,16,16,16,20,20,22,22,22,22,20,20,18,18,16,44,46,46,46,46,50,50,52,52,54,54,54,54,52,52,52,52,58,58,66,66,70,70,70,70,70,70,66,66,64,64,64,64,66,66,68,68,66,66,64,64,64,64,62,62,54,54,54,54,52,52,52,52,54,1,4,4,6,6,68,68,68,68,88,88,88,88,90,100,132,132,132,130,90,90,82,82,82,82,92,98,108,108,108,108,96,96,90,90,74,74,74,74,132,132,132,132,136,136,144,144,144,144,134,138,148,148,148,148,142,140,140,164,164,164,186,186,186,186,198,198,198,198,196,196,162,162,162,162,196,196,196,196,186,186,136,136,134,134,134,134,130,130,70,70,46,44,44,44,44,44,72,72,80,80,80,80,146,146,166,164,158,158,122,122,122,122,122,122,108,108,104,104,104,104,74,74,14,14,14,14,14,14,28,28,28,28,28,28,12,12,12,12,20,24,24,24,12,12,12,12,28,28,32,32,58,58,58,58,58,58,46,46,40,40,40,40,44,44,48,48,48,48,48,48,62,62,62,36,62,62,50,50,32,32,88,88,88,88,32,32,32,32,88,86,86,32,32,32,32,32,40,36,42,62,92,94,94,94,88,92,104,104,104,104,100,100,86,86,80,80,80,80,88,88,120,120,120,120,168,168,168,168,134,134,134,134,132,132,54,54,22,24,24,24,26,26,30,32,40,40,40,88,96,96,96,96,2,2,2,2,46,46,150,150,176,176,176,176,132,132,132,132,180,176,176,176,192,182,182,182,174,182,190,182,172,182,196,196,196,196,194,194,172,178,194,178,176,176,176,176,180,180,186,186,186,186,190,190,198,180,198,188,188,184,184,198,198,184,196,188,188,188,194,188,198,188,180,184,196,184,182,182,182,182,184,184,188,188,192,192,192,194,198,198,198,186,186,186,198,198,198,192,192,192,184,192,198,188,196,188,178,178,178,178,180,180,182,182,198,198,198,198,196,190,186,186,186,186,190,190,196,196,198,198,198,198,192,192,188,188,188,188,178,178,178,178,186,190,196,196,196,196,176,176,172,172,172,172,174,174,184,184,184,184,180,180,178,178,178,178,180,186,192,192,192,192,186,186,178,178,178,178,190,190,180,178,170,170,170,170,178,178,180,180,180,182,184,184,192,192,194,194,194,194,188,188,182,182,182,176,190,190,194,194,194,194,190,190,186,186,186,186,198,198,198,198,188,188,196,44,108,108,108,108,84,84,84,84,108,108,108,108,84,84,84,84,106,106,106,88,136,136,136,136,148,148,148,148,136,136,136,136,108,84,54,54,54,44,32,32,32,32,108,104,120,120,120,124,128,128,128,128,124,126,130,130,130,130,134,134,134,134,138,138,138,138,142,142,142,142,138,138,138,138,134,134,134,134,130,130,130,130,124,142,134,84,110,106,84,84,112,106,82,106,112,84,82,82,84,114,110,84,80,80,82,106,114,146,164,164,146,162,162,162,198,198,198,198,196,196,196,196,198,198,198,164,164,164,198,198,198,198,194,194,194,194,198,198,198,198,198,198,198,88,32,32,32,190,118,118,118,120,190,190,190,116,112,112,64,60,112,152,152,152,132,132,132,132,156,156,156,156,132,132,132,132,154,154,132,146,146,146,146,146,146,132,154,186,158,158,158,158,188,172,172,72,52,52,52,52,90,90,90,90,114,114,114,114,88,88,114,102,102,102,102,54,54,54,52,52,44,44,44,44,42,42,42,42,36,36,34,34,30,30,18,70,70,194,160,160,156,156,150,150,150,150,152,152,166,166,166,166,162,162,162,166,170,170,170,170,162,166,166,166,170,170,170,162,162,162,162,164,164,164,178,178,178,178,196,196,196,194,198,198,198,198,196,198,190,190,190,190,196,196,198,198,190,190,188,188,188,188,198,196,190,190,190,190,190,190,186,186,186,154,158,158,158,158,154,154,154,156,156,156,150,150,150,158,158,158,156,156,148,120,120,120,114,114,114,114,120,120,120,120,114,114,104,104,102,102,102,102,122,122,122,122,114,114,114,114,122,122,122,122,98,98,98,98,124,124,124,124,74,74,62,62,62,62,124,124,124,124,98,98,98,98,122,122,122,122,100,100,100,158,158,158,192,192,192,192,156,166,166,166,176,176,176,176,166,170,170,170,176,144,144,144,126,126,126,126,124,124,92,92,90,90,90,90,132,132,132,132,146,102,102,102,112,112,112,112,100,100,116,106,106,106,106,106,106,106,122,122,122,122,122,122,120,120,118,110,108,110,118,122,120,108,104,104,122,116,116,142,134,142,144,134,124,124,124,124,148,138,138,128,128,128,132,132,132,132,126,126,126,2,12,2,2,2,8,8,2,2,2,2,2,2,14,14,14,2,10,10,10,88,88,88,70,70,56,56,56,56,74,74,92,30,30,20,20,20,20,20,20,20,20,12,12,12,12,12,12,12,12,12,12,36,38,44,46,50,52,58,60,72,74,78,82,86,88,98,102,34,34,34,38,44,48,52,54,60,62,64,66,68,70,72,74,76,78,82,84,92,94,96,98,100,104,106,108,60,44,44,34,34,34,34,64,64,64,64,60,60,60,38,36,40,42,40,38,36,36,36,42,46,48,50,54,56,66,68,74,76,82,84,90,92,96,98,104,106,108,46,50,52,54,58,60,64,66,68,74,76,78,82,84,86,92,94,96,118,48,46,46,46,86,86,86,86,54,54,54,54,118,118,118,144,182,154,154,144,144,144,172,172,172,150,168,142,142,142,128,128,144,146,176,178,178,170,142,140,140,140,140,66,66,66,54,54,110,110,122,122,122,122,62,22,22,22,146,148,148,128,128,140,120,140,196,88,84,16,16,16,28,28,28,28,22,22,14,14,14,14,14,14,18,18,30,30,30,30,28,28,14,14,6,24,26,26,26,26,16,16,16,16,18,18,18,6,6,6,12,12,12,12,20,198,184,184,184,186,174,174,174,152,146,164,164,164,148,148,148,148,134,134,134,134,150,150,150,150,158,148,148,148,148,148,154,144,168,168,168,168,152,172,182,186,182,172,168,184,184,184,184,184,184,184,184,184,184,184,196,102,102,102,124,124,124,124,136,124,124,124,120,120,108,116,100,102,112,90,90,90,68,68,68,68,90,90,90,84,84,84,78,78,78,78,84,84,62,154,154,154,154,154,84,84,78,78,78,78,144,144,144,122,102,102,100,100,100,100,108,108,124,106,86,86,86,86,92,92,106,106,110,78,58,58,56,56,56,56,74,74,82,70,70,70,70,70,70,80,80,80,80,80,80,124,124,124,124,124,124,42,42,42,2,42,34,28,22,44,32,26,18,42,34,28,22,62,64,34,34,34,2,34,34,6,6,12,12,18,18,26,26,26,38,26,26,26,26,30,30,30,18,28,28,116,116,116,92,92,76,76,70,70,70,70,82,82,104,104,124,124,124,124,112,112,112,112,112,112,114,114,124,124,134,134,134,134,118,118,112,118,150,150,154,154,154,154,150,150,148,148,148,66,118,118,118,118,108,108,100,100,88,88,88,64,120,82,82,134,134,134,138,138,156,156,174,174,174,174,166,166,154,154,142,142,128,128,126,126,126,126,140,140,140,140,138,138,130,12,42,42,42,26,26,10,10,10,42,36,10,38,34,34,8,8,32,22,22,160,188,190,190,190,186,186,180,180,168,168,168,168,168,168,162,162,158,158,150,150,150,150,154,160,146,166,198,156,142,56,42,42,32,32,50,50,50,50,48,48,36,36,34,34,34,34,50,54,54,54,66,66,70,70,70,70,86,86,104,104,110,118,132,132,132,132,124,112,110,112,124,110,106,106,104,104,104,104,126,126,140,140,140,140,136,128,100,100,100,100,108,108,110,110,110,110,122,122,122,122,114,114,112,112,108,108,108,108,112,112,116,116,118,118,118,118,112,110,102,102,96,96,96,96,52,50,50,50,52,52,64,64,74,74,74,74,66,66,46,56,56,56,60,60,66,66,66,66,64,64,54,62,56,56,52,52,52,52,58,58,62,62,62,66,66,60,60,54,54,22,38,20,20,20,22,22,26,26,32,32,32,32,40,40,42,20,40,22,22,22,38,28,28,42,28,28,28,28,30,30,42,42,42,42,32,32,32,32,102,102,102,102,34,104,126,126,126,126,100,100,128,128,128,128,98,70,70,70,78,78,78,72,62,62,62,62,64,64,66,66,74,74,74,74,78,78,78,78,92,92,92,92,74,102,102,102,114,74,74,74,78,78,86,86,86,76,64,64,64,64,78,82,82,78,78,78,68,68,68,68,76,76,76,76,84,88,100,100,100,100,86,58,58,58,66,66,66,60,60,60,66,66,60,60,60,60,66,66,66,66,58,58,68,68,68,68,62,62,62,76,76,76,82,82,82,82,78,78,78,86,86,86,78,94,104,104,104,104,96,96,96,96,86,86,96,30,50,82,90,84,84,84,20,20,2,2,2,2,2,2,12,12,12,12,24,24,28,38,96,100,132,132,132,130,128,128,104,104,104,104,2,104,148,150,198,198,198,198,64,64,64,64,64,64,166,166,166,166,198,198,198,198,192,192,172,172,172,172,172,172,198,198,198,196,176,176,164,164,164,164,198,94,94,94,46,44,44,44,54,54,92,92,92,92,52,52,52,58,88,88,88,88,88,88,58,58,58,58,58,58,58,58,60,60,70,70,76,76,76,76,70,70,64,64,54,54,54,54,54,54,38,38,38,38,16,16,16,20,26,26,26,26,40,40,42,42,52,52,64,64,64,122,134,124,124,124,114,114,114,108,102,108,116,104,104,100,90,100,106,92,92,92,76,76,76,76,60,60,60,60,42,42,42,42,28,28,28,28,16,16,16,16,16,16,16,16,24,24,32,32,42,42,52,52,56,56,64,64,66,66,74,74,74,74,84,84,88,88,98,98,100,100,114,114,118,118,130,130,132,132,142,142,146,146,154,154,154,154,14,14,14,14,2,2,2,2,2,2,4,4,12,12,20,20,20,20,30,30,38,38,48,48,58,58,60,60,68,68,70,70,78,78,80,80,80,80,82,82,90,90,94,94,104,104,108,108,118,118,120,120,130,130,132,132,140,140,144,144,160,160,150,1,2,2,2,2,8,8,8,8,6,6,2,6,26,26,36,36,36,36,42,42,48,48,66,66,68,68,92,92,94,94,118,118,122,122,150,150,154,154,170,170,172,172,180,180,180,180,166,166,162,162,154,154,152,152,144,144,144,144,130,130,122,122,116,116,104,104,92,92,82,82,78,78,66,66,64,64,50,50,46,46,28,28,26,26,12,12,10,10,2,2,2,2,2,16,38,38,44,44,44,44,48,48,70,70,72,72,72,72,78,78,92,92,92,92,100,100,122,122,124,124,124,124,126,126,156,156,158,158,158,158,162,162,164,164,164,164,148,148,146,146,136,136,134,134,128,128,122,122,118,118,116,116,110,110,94,94,80,80,70,70,56,56,52,52,34,34,32,32,20,20,14,14,2,2,2,2,4,4,12,12,16,48,2,2,2,72,70,70,70,70,70,70,68,68,2,2,2,2,18,18,46,46,46,46,30,6,6,1,4,4,10,10,10,10,58,58,58,58,76,76,76,76,88,88,88,88,112,112,120,120,120,120,80,80,80,80,54,54,46,42,76,76,88,88,94,94,112,112,124,124,138,138,154,154,158,88,62,62,46,46,42,42,22,22,16,16,10,10,8,122,148,148,172,172,174,176,176,176,172,172,156,134,134,134,116,116,116,116,116,116,112,112,100,100,100,100,60,50,28,20,4,2,78,78,92,92,106,116,120,132,160,160,160,160,104,104,92,92,76,48,24,24,24,24,26,26,34,34,38,38,38,38,48,48,66,100,158,158,158,158,178,178,198,198,198,198,178,178,178,178,180,180,186,186,192,192,192,192,178,178,178,178,194,194,194,194,180,180,180,180,182,182,192,192,198,198,198,34,36,38,44,44,44,44,46,46,58,58,58,58,44,44,44,44,32,32,22,22,22,22,38,34,24,24,24,24,24,24,38,38,38,38,48,48,48,48,38,38,38,38,22,22,20,20,20,20,10,10,4,4,4,4,18,18,36,60,82,82,82,82,108,108,108,108,96,96,96,96,58,58,58,126,126,126,156,156,166,166,166,166,164,164,148,148,118,118,118,118,138,138,138,138,126,126,126,126,126,1,2,2,2,112,108,108,100,100,100,100,104,104,126,126,134,134,134,134,118,118,108,108,96,96,96,96,104,112,112,112,102,102,102,102,108,108,122,122,134,134,134,134,122,122,118,118,112,102,100,114,114,114,86,86,58,58,58,58,84,84,84,84,112,112,112,110,110,80,80,80,92,92,92,92,58,62,62,62,84,84,84,84,60,60,60,56,48,48,48,48,60,52,52,52,42,48,46,46,34,52,32,52,48,48,30,88,80,80,80,80,90,90,90,90,96,96,96,96,104,104,104,104,98,98,98,98,90,90,90,90,82,114,128,128,148,148,148,148,124,124,86,86,56,56,56,56,74,74,108,108,118,80,80,80,86,86,92,92,102,102,102,102,100,100,94,94,86,86,86,86,86,86,86,86,90,90,102,102,112,112,112,112,108,108,88,88,84,84,84,62,56,56,52,52,52,52,66,66,70,70,82,62,58,58,52,52,52,52,66,66,72,72,74,74,74,74,78,78,90,96,92,92,92,92,98,98,102,96,96,96,98,98,104,96,102,100,100,100,102,102,106,106,106,106,106,106,102,106,110,102,102,116,110,110,108,108,108,108,110,110,114,114,120,120,120,120,128,128,130,130,130,120,120,120,126,128,132,132,134,134,134,78,78,78,74,74,70,70,60,60,60,60,66,66,66,66,60,60,56,56,56,56,60,60,72,72,78,34,60,36,36,36,42,42,46,46,48,48,48,48,56,56,58,58,60,60,60,36,60,60,60,46,46,38,38,32,28,28,24,24,24,24,32,32,50,50,64,64,64,64,58,58,52,52,52,64,64,64,58,58,48,48,40,40,30,30,20,22,26,26,34,34,54,54,60,60,64,44,44,44,52,52,52,52,42,22,26,26,42,42,42,42,28,28,24,24,20,20,18,40,60,100,124,96,98,98,116,116,118,118,120,120,120,104,102,102,100,100,98,98,90,90,90,90,104,104,106,106,114,114,122,122,128,128,128,130,130,130,128,128,124,124,120,120,116,116,108,108,104,104,94,158,150,150,146,146,146,146,152,152,170,170,182,182,182,182,178,178,174,174,172,148,148,148,154,154,158,158,164,164,184,146,146,146,158,158,160,160,164,164,164,164,170,170,186,150,180,182,150,150,150,150,156,156,166,166,174,174,170,170,158,158,152,152,150,150,148,148,152,158,182,182,152,152,152,152,164,170,170,180,192,162,158,158,154,154,152,152,144,144,144,144,154,154,170,170,190,190,192,192,192,192,180,180,180,30,30,30,54,54,54,54,76,76,76,76,58,58,58,58,116,128,156,156,156,156,132,132,132,132,154,154,154,154,100,100,100,100,74,74,74,74,102,102,102,116,116,116,124,124,124,124,144,144,144,144,142,142,122,170,170,170,188,188,188,188,196,196,196,196,166,166,166,166,192,192,192,192,70,70,70,70,52,52,52,52,8,8,8,8,22,22,22,22,26,26,34,34,34,34,48,48,48,48,62,62,62,62,172,172,172,172,40,40,40,40,8,8,8,8,22,22,22,22,48,48,48,48,138,138,138,180,180,180,182,182,198,198,198,198,182,182,182,182,174,174,174,100,100,100,126,126,104,104,104,104,124,124,124,174,174,174,170,170,170,170,162,162,162,162,98,98,98,98,86,86,86,86,68,68,68,68,64,64,64,64,52,52,52,52,44,44,44,44,40,40,40,40,44,44,44,44,50,50,50,50,62,62,62,62,68,68,68,68,76,76,76,76,82,82,82,82,94,94,94,94,100,100,100,100,156,156,156,156,162,162,162,162,168,168,168,166,174,70,70,70,60,60,60,60,68,68,68,68,84,84,84,84,64,64,64,44,44,44,52,52,52,52,40,80,80,80,88,88,88,88,74,74,74,74,84,84,84,84,68,26,26,26,40,40,42,42,42,42,22,20,4,22,2,22,22,36,36,46,46,36,56,42,58,36,36,20,20,22,22,22,2,170,190,190,190,162,196,166,166,186,186,1,2,2,2,2,26,26,42,42,42,42,44,44,56,56,78,78,78,78,62,62,62,62,78,78,82,82,86,86,86,86,122,122,148,148,162,162,162,162,138,138,156,156,168,168,180,180,180,180,150,150,176,176,176,176,158,158,156,156,142,142,128,128,128,128,138,138,138,138,128,128,116,116,114,114,100,100,84,84,82,82,68,68,56,56,50,50,40,40,38,38,26,26,26,26,40,40,52,52,66,66,82,82,92,92,106,106,140,140,140,140,120,120,120,120,140,140,140,140,110,110,110,110,120,120,120,120,112,112,110,110,72,72,70,70,44,44,42,42,30,30,2,2,2,2,4,4,16,16,28,28,46,46,58,58,60,60,68,68,70,70,88,88,88,88,68,68,12,12,2,2,2,2,38,38,38,6,36,38,38,38,4,4,4,4,4,4,10,10,18,18,32,32,32,32,26,26,2,2,28,28,28,12,12,2,2,66,84,70,70,70,78,78,82,82,88,88,88,88,96,96,98,98,110,82,74,74,64,64,64,64,78,78,84,84,118,82,78,78,70,70,70,70,86,86,90,90,120,138,166,166,166,166,138,138,138,138,166,166,170,170,170,170,162,140,146,146,168,168,168,168,142,142,168,168,168,154,154,140,140,180,180,64,64,64,36,36,32,32,32,32,42,42,44,44,50,50,62,62,62,62,64,64,78,78,78,78,74,74,66,66,66,82,74,82,100,100,104,104,104,104,92,92,86,86,78,78,82,82,88,98,98,98,98,90,90,100,96,96,96,96,92,100,92,102,132,132,136,136,136,136,104,114,114,114,122,122,122,116,116,116,120,120,126,126,126,106,106,106,110,110,110,110,98,98,98,98,106,100,100,100,96,100,104,96,94,106,106,106,110,106,100,100,102,102,102,102,104,108,108,108,134,134,134,134,126,126,118,118,110,106,102,126,126,126,130,130,130,130,124,124,124,122,128,126,122,124,126,112,112,112,114,114,122,122,122,116,116,116,124,124,124,124,114,114,114,114,116,116,116,116,116,140,156,156,156,156,140,136,156,140,140,140,136,136,134,134,134,134,142,132,140,88,88,88,90,90,92,90,82,82,72,72,72,72,76,76,84,86,86,86,88,88,88,88,84,84,84,84,86,86,88,88,88,88,86,78,74,74,68,68,68,120,118,118,114,114,114,114,116,116,118,118,118,118,116,116,116,116,122,122,128,118,124,124,124,124,130,130,130,64,72,70,62,62,68,68,68,68,70,70,70,70,64,64,60,60,60,60,64,64,64,64,64,64,66,66,66,54,58,58,66,66,66,66,66,66,68,68,74,74,74,74,68,68,68,68,66,66,64,64,64,64,64,64,66,66,68,68,70,70,70,70,72,72,72,72,74,74,76,76,76,76,76,76,74,74,74,74,64,70,70,70,70,70,70,70,84,84,82,82,80,80,78,78,72,72,70,70,68,68,70,70,72,72,72,72,70,70,68,68,70,192,170,170,166,166,164,164,156,156,150,150,148,148,146,146,144,144,142,142,128,128,126,126,126,126,124,124,116,116,114,114,108,108,106,106,102,102,100,100,98,98,96,96,96,96,96,96,92,92,88,88,86,86,84,84,82,82,76,76,68,68,66,180,162,162,158,158,146,146,140,140,116,116,114,114,110,110,108,108,96,96,92,92,90,90,88,88,82,82,80,80,76,76,74,74,50,50,48,48,46,46,44,44,32,32,28,28,10,30,18,18,8,8,8,8,32,32,36,36,42,42,50,50,56,56,68,64,52,52,50,50,36,36,34,34,32,32,30,30,22,22,20,20,20,20,20,20,24,28,30,30,36,36,46,46,50,50,54,54,62,62,62,62,66,66,72,72,74,74,76,76,80,80,82,82,82,82,84,84,86,86,98,98,98,98,102,102,104,104,118,118,120,120,122,122,126,126,134,134,138,138,144,144,144,144,146,146,152,152,158,158,184,188,162,162,160,160,144,144,142,142,138,138,136,136,134,124,118,104,102,102,100,100,98,86,74,74,70,66,44,36,36,36,32,32,32,32,30,30,28,28,24,24,24,24,28,28,36,36,40,40,42,44,50,50,52,52,56,56,64,64,70,70,80,80,82,170,166,164,162,162,154,150,144,144,142,142,136,136,128,128,128,128,116,116,108,108,100,100,98,98,86,86,84,84,76,76,74,74,74,162,158,158,150,146,138,138,134,134,126,126,124,124,114,114,112,112,108,108,100,100,94,94,92,92,92,92,90,90,82,82,78,78,72,72,72,72,82,82,82,82,90,166,156,156,154,154,150,150,142,142,130,130,122,122,118,106,100,100,94,92,86,86,80,80,74,74,74,72,88,152,142,138,134,134,128,128,118,118,112,112,98,98,96,96,92,92,90,90,88,88,86,86,78,78,76,76,72,72,70,70,68,68,66,66,66,66,70,146,138,138,130,130,116,116,108,108,98,98,94,94,84,84,76,76,74,74,70,70,68,68,64,64,64,64,66,66,80,186,174,174,164,164,156,156,150,150,146,146,136,136,134,134,124,124,116,116,116,116,122,122,134,188,184,184,180,180,180,180,168,168,160,160,160,160,130,130,130,130,136,136,138,8,2,2,2,2,4,4,8,8,8,8,26,26,36,36,38,38,42,42,42,42,38,38,32,32,32,32,24,24,20,20,14,14,14,14,14,14,10,10,2,2,2,2,2,2,6,6,10,10,10,10,8,8,6,6,2,2,2,2,2,2,10,10,12,12,16,16,16,16,18,18,26,26,30,30,30,30,26,26,22,22,22,22,22,22,20,20,20,20,16,16,14,14,4,4,2,2,2,2,6,40,42,40,38,38,14,14,14,14,24,24,26,26,28,28,28,28,32,32,36,36,36,36,36,36,42,42,44,44,54,54,60,60,62,62,62,62,58,58,54,54,52,52,52,52,50,50,50,50,46,46,30,30,28,28,28,28,32,32,40,76,66,66,60,60,60,60,70,70,72,72,82,82,84,84,100,100,104,104,104,104,102,102,92,92,92,92,112,112,112,112,106,106,92,92,82,82,78,78,76,76,76,76,80,80,84,80,78,78,78,78,88,88,88,88,86,86,66,66,64,64,64,64,74,74,76,76,76,76,76,76,74,74,48,82,82,82,74,74,72,72,64,64,54,54,54,54,52,52,50,50,46,46,46,46,48,48,50,50,50,50,50,50,62,62,64,64,64,64,64,64,82,82,86,86,86,86,78,78,74,74,72,70,66,132,132,132,128,128,112,112,100,100,100,100,104,104,152,152,168,168,168,168,160,160,140,140,128,128,126,126,118,118,118,118,118,136,136,154,154,162,166,166,198,198,198,198,198,198,158,158,158,158,160,160,190,190,190,190,176,176,136,136,136,136,90,140,140,48,50,50,54,54,54,54,40,40,30,30,30,30,20,20,20,20,34,34,34,34,46,52,60,60,74,74,74,74,62,62,62,62,54,54,54,54,42,42,40,56,22,22,22,22,10,10,10,10,16,16,18,18,24,24,24,24,58,22,22,30,30,50,50,110,110,110,100,100,100,100,92,92,92,92,108,108,108,108,112,112,112,112,116,116,116,116,108,108,108,108,112,112,112,112,116,116,116,116,108,108,96,96,96,96,86,86,86,86,74,74,74,88,88,88,84,84,76,76,76,94,94,94,88,88,88,100,100,100,104,104,104,110,110,110,104,104,104,132,132,132,142,142,166,166,172,172,172,172,166,166,164,164,164,164,170,170,170,170,162,162,162,162,156,156,154,154,154,154,148,148,148,148,152,152,152,152,146,146,146,146,136,136,136,136,142,142,152,152,152,132,132,132,122,122,122,140,140,140,146,146,158,158,158,158,136,136,156,188,188,188,166,166,166,166,154,154,154,154,172,172,172,172,190,190,190,190,196,196,196,196,186,186,194,194,194,194,186,178,178,178,170,170,170,170,162,162,162,162,162,162,166,166,166,170,170,170,182,182,182,144,152,142,142,142,150,150,150,150,142,142,150,144,144,144,140,160,156,156,150,150,150,150,140,146,154,144,146,146,162,130,108,108,108,108,108,100,100,100,78,78,78,78,102,84,94,94,94,94,84,84,84,84,94,94,94,94,82,74,56,56,56,44,44,44,54,54,30,30,30,30,20,20,20,20,26,26,30,30,44,44,50,50,50,28,28,28,42,42,42,42,24,46,46,46,50,50,44,44,44,90,90,80,96,74,74,74,96,96,96,96,72,84,76,76,84,84,84,84,76,76,76,108,108,108,114,114,130,130,142,142,142,142,134,134,128,128,114,114,110,110,110,110,106,106,106,106,122,122,136,136,150,150,150,150,140,140,120,120,106,106,106,126,98,98,98,98,90,100,100,100,90,112,112,112,138,138,138,138,136,114,118,118,140,140,140,140,148,148,148,148,148,140,132,132,132,132,132,118,118,108,108,108,120,120,134,134,140,140,140,158,158,158,174,174,184,184,184,184,160,160,160,160,158,158,156,166,166,166,154,154,150,166,154,154,148,160,164,168,166,166,160,92,64,64,36,36,36,36,44,82,52,52,52,52,68,68,68,36,32,32,16,16,32,32,38,38,46,46,56,56,60,60,98,36,98,90,90,90,80,80,80,80,90,90,90,90,78,78,78,78,88,88,88,88,78,78,78,78,88,88,88,88,80,80,80,80,72,72,72,72,84,84,84,84,76,76,76,76,88,88,84,84,8,8,8,8,80,28,28,52,52,68,68,142,62,60,60,60,50,50,50,50,60,52,52,50,44,44,44,44,52,42,46,62,142,2,8,8,10,10,10,10,8,8,8,8,10,10,16,16,18,18,18,18,20,20,22,22,24,24,24,24,20,20,20,20,24,24,26,26,28,28,30,30,32,32,32,32,34,34,34,34,40,40,42,42,48,48,50,50,58,58,60,60,62,62,62,62,64,64,74,74,70,70,70,70,72,72,74,74,78,78,78,78,76,76,80,80,86,86,88,88,88,88,84,84,82,82,80,80,78,78,78,78,84,84,86,86,92,92,94,94,96,96,96,96,94,94,92,92,92,92,94,94,96,96,100,100,102,102,108,108,106,106,104,104,104,104,136,136,136,136,136,136,198,198,198,132,132,132,132,132,156,156,156,156,172,172,172,172,198,128,128,128,128,128,136,136,136,136,126,126,124,124,122,122,122,122,124,124,124,124,122,122,114,114,112,112,110,110,106,106,106,106,112,112,116,116,118,118,118,118,114,114,114,114,122,122,124,124,130,130,132,132,136,136,136,136,132,132,130,130,128,128,126,126,120,120,120,120,128,128,128,128,130,130,136,136,140,140,142,142,148,148,148,148,142,142,142,142,148,148,150,150,152,152,152,152,146,146,132,132,150,150,132,132,148,148,150,150,150,150,160,160,160,130,130,130,130,130,130,124,124,124,136,136,136,120,120,120,124,130,96,96,84,80,76,72,68,62,58,52,48,128,110,104,100,96,92,88,84,80,74,70,64,58,54,130,126,126,116,116,116,116,104,100,96,92,88,84,80,120,110,106,102,98,92,86,82,76,72,68,64,60,56,52,48,42,38,32,28,22,18,14,10,124,108,98,94,92,90,78,74,70,66,124,124,124,124,124,100,100,100,100,124,124,124,124,124,100,82,82,72,72,72,72,86,86,86,86,88,88,96,96,102,102,86,86,78,78,78,78,86,86,86,86,78,78,78,124,20,20,20,20,124,90,90,68,68,48,48,30,30,170,42,42,42,42,32,32,32,32,32,32,166,166,166,166,166,58,56,42,42,42,42,42,32,120,120,120,122,162,162,162,160,160,118,118,164,164,164,118,118,118,86,86,86,86,92,92,118,118,118,100,100,100,86,86,78,78,72,72,64,86,70,70,70,70,64,64,64,64,62,62,58,64,46,46,46,46,40,40,40,40,48,48,48,58,58,58,50,50,50,50,56,56,56,20,20,20,26,26,46,46,46,46,40,40,20,20,18,18,18,46,52,52,52,42,50,50,50,42,48,34,34,26,26,26,32,20,20,20,34,92,92,92,100,100,100,100,92,92,100,100,100,100,92,92,92,104,110,110,110,110,102,102,102,104,104,104,112,112,112,112,104,104,112,112,112,112,102,102,102,102,102,102,108,108,108,108,100,100,108,108,108,108,100,100,100,46,46,50,50,58,58,62,62,62,64,72,72,52,52,176,176,182,196,188,188,182,182,182,196,180,182,182,196,196,196,196,192,192,190,190,190,190,182,182,180,182,190,190,196,178,186,186,198,182,186,186,198,190,190,182,196,182,178,178,178,178,178,196,198,178,178,178,182,186,198,198,198,186,182,178,176,178,198,186,186,186,178,186,198,178,178,178,196,196,196,196,178,196,198,198,198,198,192,196,196,196,196,196,196,196,178,178,178,178,192,198,196,194,194,194,194,194,198,28,28,28,50,50,50,50,58,58,62,62,64,68,74,74,74,74,62,72,72,72,64,64,60,60,58,50,48,52,54,54,26,54,74,60,60,60,40,40,40,40,46,40,30,60,72,72,72,72,68,68,56,56,56,56,52,60,74,60,56,68,68,76,30,28,28,28,48,48,48,48,28,28,28,28,76,90,120,88,88,102,102,122,80,80,80,80,122,100,100,80,126,82,82,82,96,96,96,96,96,122,112,124,124,124,116,116,112,112,94,78,78,78,124,124,74,100,100,100,100,100,114,122,80,78,124,104,104,150,186,186,186,148,148,148,162,162,162,162,162,162,186,150,186,148,148,148,182,158,158,180,148,146,146,146,176,176,176,176,146,146,188,146,146,146,188,160,160,160,160,154,154,154,172,172,172,172,154,154,154,154,172,172,172,172,152,154,154,154,174,174,174,174,154,136,136,136,176,176,176,176,152,152,152,152,134,156,176,176,156,156,156,152,152,152,136,152,136,132,132,152,138,138,156,152,144,144,154,142,142,138,138,138,130,130,130,130,140,140,140,178,178,178,182,182,182,182,178,178,178,178,182,182,182,182,176,178,184,184,184,184,178,178,178,178,178,178,184,184,184,184,178,178,178,178,186,186,186,186,178,178,178,178,180,180,180,180,182,182,182,182,184,184,184,184,180,180,180,128,118,118,116,116,114,114,110,110,96,96,90,90,76,76,74,74,72,72,70,70,60,60,52,52,50,50,48,48,46,46,36,36,34,34,26,26,26,26,20,20,18,18,14,14,14,14,4,4,4,4,12,12,12,12,20,20,20,20,28,28,28,28,32,32,32,32,38,38,44,44,52,52,58,58,68,68,74,74,82,82,90,90,94,94,94,94,84,84,84,84,76,76,76,76,84,84,84,84,76,76,76,76,82,82,82,82,88,88,94,94,94,94,90,90,90,90,96,96,96,96,102,102,104,104,104,104,114,114,114,114,116,116,116,116,126,126,130,110,108,108,104,104,102,102,100,100,98,98,96,96,94,94,86,86,86,86,90,90,90,90,84,84,82,82,76,76,76,76,72,72,68,68,64,64,64,80,78,78,70,70,66,66,64,64,62,62,60,60,52,52,50,50,46,46,46,46,40,40,32,32,28,28,28,28,18,62,62,62,60,60,60,60,58,58,56,56,52,52,50,50,44,44,44,44,40,40,36,36,32,32,32,32,28,28,18,18,12,12,12,12,10,10,6,54,54,54,50,50,50,50,52,52,54,54,54,54,58,58,60,60,60,60,64,64,66,66,66,66,72,72,74,46,46,46,48,48,54,54,58,58,58,58,66,66,66,66,70,70,72,72,74,74,74,44,36,36,30,30,30,30,28,28,28,28,24,24,20,20,14,14,14,168,168,168,168,168,168,176,176,176,182,182,182,182,176,176,176,176,182,182,182,182,174,174,174,174,178,178,178,178,174,174,174,174,178,178,178,178,174,176,176,176,180,180,180,180,176,176,176,176,180,180,180,180,174,154,136,136,156,156,134,134,154,154,134,134,156,134,134,134,134,134,134,130,130,130,130,130,130,154,170,154,170,166,152,152,170,170,154,154,170,182,182,182,182,182,182,182,182,182,182,182,182,188,188,86,52,52,52,52,84,84,84,70,64,64,64,64,72,72,72,72,70,68,68,72,64,64,64,64,74,74,74,74,72,70,70,74,78,78,78,78,74,82,98,84,98,98,98,98,112,112,112,112,98,98,98,98,118,118,118,118,106,106,106,106,144,144,144,144,104,104,104,104,74,74,74,74,100,100,100,100,98,56,56,52,38,38,38,38,30,30,30,30,34,34,34,78,64,64,70,70,76,62,74,92,98,104,90,90,90,90,82,82,82,82,70,70,70,70,52,52,46,144,166,166,166,166,150,150,150,150,170,170,170,170,174,174,144,168,180,164,182,160,178,178,160,178,178,178,184,184,184,184,176,176,186,186,186,186,182,182,182,186,186,186,174,184,184,184,188,188,188,188,182,182,182,182,192,192,192,192,180,180,180,180,186,186,186,190,198,198,198,198,188,190,198,198,198,198,188,32,24,24,24,24,44,44,44,44,60,60,62,62,62,62,58,58,52,64,28,46,46,46,30,42,44,44,58,58,60,50,36,36,36,36,60,44,44,64,26,26,26,26,26,26,28,28,36,36,38,24,56,56,56,44,44,76,76,76,102,102,102,102,74,102,76,76,76,76,84,84,84,84,84,82,92,92,102,122,166,166,166,166,142,142,132,132,114,114,114,114,122,124,152,114,106,124,124,124,114,114,114,114,120,120,132,132,136,116,162,162,170,170,170,170,172,146,146,146,154,122,166,174,186,186,186,186,186,186,186,186,186,70,70,70,88,88,88,88,66,64,64,64,64,64,86,86,86,86,70,70,70,64,64,64,70,70,64,82,82,82,82,70,70,70,70,82,82,82,74,74,74,74,78,82,72,74,80,80,80,80,72,72,72,110,114,114,114,114,106,106,106,106,110,106,106,106,116,116,116,116,106,56,56,56,44,44,44,44,56,56,56,56,48,48,48,48,54,54,54,138,136,136,124,124,124,124,130,130,134,126,126,126,128,128,128,128,134,134,140,140,140,140,146,146,150,150,150,150,140,146,142,142,142,142,136,148,148,148,142,136,134,134,134,140,114,114,114,114,118,142,142,142,142,142,150,150,150,150,140,140,140,140,116,116,116,130,130,130,130,142,158,158,152,156,156,156,112,112,112,162,162,146,146,146,146,144,144,120,86,86,86,86,120,102,102,102,102,86,118,86,86,86,86,86,88,118,122,120,120,90,86,122,86,86,86,86,122,106,106,106,124,106,86,86,86,86,92,92,96,96,106,106,106,106,108,108,112,112,122,124,144,144,144,144,124,130,142,124,124,124,130,130,144,144,150,150,150,150,140,140,126,126,122,122,122,146,124,132,144,144,122,122,122,122,130,130,136,136,146,146,146,146,144,144,140,140,136,136,136,32,30,30,26,26,26,26,32,32,48,48,56,56,56,56,44,44,30,30,26,26,26,26,26,26,14,28,14,28,26,26,14,26,26,26,20,36,36,44,50,50,60,56,66,66,72,52,52,52,62,42,42,32,32,32,24,18,24,24,24,24,20,20,20,20,20,20,24,24,24,24,20,20,20,20,16,28,32,32,32,32,28,28,28,40,48,48,48,48,40,40,40,48,44,44,44,44,50,40,44,44,44,44,40,48,44,44,44,44,50,40,44,44,44,44,40,44,40,48,44,44,44,44,48,176,182,182,184,184,190,190,194,194,194,194,186,186,180,180,174,174,174,174,170,170,170,170,174,174,178,178,180,180,180,180,178,178,176,178,188,188,190,190,190,180,180,180,172,172,190,176,176,176,172,172,168,172,188,170,170,170,172,172,176,176,186,186,188,188,194,194,194,180,180,180,182,182,186,186,190,190,190,180,192,188,184,184,184,184,186,186,192,192,192,192,180,180,180,184,184,192,178,186,188,188,188,188,182,188,194,194,182,186,190,190,190,190,196,190,184,184,192,182,194,182,182,182,182,182,182,182,190,190,190,84,100,90,96,90,90,90,92,92,92,92,96,96,96,82,96,82,94,94,94,94,80,80,80,80,94,86,86,94,82,88,96,96,82,82,82,82,94,94,94,90,90,72,76,76,76,76,74,74,74,74,72,72,72,74,74,36,36,40,66,66,70,70,70,70,60,60,36,34,34,38,38,38,38,38,72,72,72,72,42,38,34,38,42,46,62,62,62,62,58,58,56,56,54,52,60,52,44,44,44,44,44,44,44,44,60,60,60,60,40,40,40,40,48,48,48,24,20,24,28,20,18,18,18,18,22,22,26,26,26,26,26,26,26,26,26,26,16,16,16,16,20,20,26,26,26,26,26,26,20,20,20,20,14,18,18,18,18,18,24,106,100,100,100,100,112,112,112,112,100,104,100,100,100,100,104,104,116,116,116,116,108,108,102,102,102,102,116,118,118,118,118,128,128,100,118,118,118,118,118,118,116,116,112,138,162,162,164,164,164,164,156,156,132,138,138,138,148,148,148,148,138,138,150,150,150,150,144,144,136,132,132,132,122,122,122,122,132,132,132,132,122,122,122,122,132,132,132,132,122,122,122,122,132,20,54,54,66,66,66,66,60,60,20,20,4,4,4,4,22,22,22,52,36,38,54,54,54,10,10,10,10,10,68,68,68,68,64,64,64,64,64,64,44,38,32,10,4,4,4,4,22,22,22,22,16,16,16,16,2,2,2,2,18,18,18,18,50,50,50,50,36,36,36,36,66,66,66,66,48,64,64,6,6,6,24,24,24,24,24,24,66,66,66,48,34,36,36,80,80,80,114,114,114,114,130,130,130,130,78,100,100,114,134,134,134,134,94,94,94,94,104,104,104,104,94,94,94,98,98,94,74,74,74,74,98,98,98,98,132,132,132,132,136,136,136,136,86,86,86,108,108,108,104,108,114,148,196,180,180,180,176,176,176,176,152,174,180,180,198,198,160,160,160,160,166,166,186,186,186,186,182,182,174,174,156,156,194,194,194,194,170,170,170,170,156,156,156,176,176,176,166,166,166,176,186,186,186,186,186,186,180,180,180,180,172,172,168,168,168,2,2,2,18,18,20,20,20,20,22,22,30,30,34,34,34,34,38,38,60,60,60,60,82,82,90,90,90,90,78,78,74,74,64,64,56,56,28,28,28,28,112,112,112,112,124,124,124,124,128,128,144,144,166,166,168,168,168,168,152,152,148,148,148,148,152,152,174,174,176,176,176,176,178,178,190,190,194,194,194,194,154,154,154,154,142,142,140,140,140,140,138,138,118,118,118,118,128,128,128,128,126,126,124,112,106,106,96,96,96,96,128,128,128,128,142,142,142,142,138,138,138,138,134,134,20,20,20,20,56,56,56,56,52,52,52,52,56,56,74,74,76,76,76,76,78,78,96,96,96,96,98,98,144,144,146,146,146,146,152,152,168,168,170,170,170,170,166,166,20,20,12,12,12,12,16,16,68,68,70,70,70,70,76,76,112,112,112,112,106,106,44,44,28,28,2,2,2,8,22,12,28,28,40,40,46,46,46,46,34,34,24,120,136,120,134,140,152,152,154,154,154,154,138,92,110,110,110,110,92,92,92,94,102,92,102,102,106,104,108,108,108,104,110,168,186,168,168,168,174,174,174,186,186,186,172,172,172,164,188,172,174,174,174,174,164,164,164,164,182,166,172,172,188,186,186,186,172,172,170,170,170,170,174,174,188,170,172,168,176,176,178,178,186,178,178,178,188,188,188,180,180,180,168,168,168,168,180,180,180,180,188,188,188,188,198,198,198,198,184,184,184,184,174,170,186,188,194,196,198,170,166,160,160,160,174,174,174,174,190,40,40,40,32,32,32,32,26,26,26,26,34,34,34,34,40,40,40,40,46,46,46,46,36,48,44,48,78,78,78,78,44,52,58,58,58,58,52,52,52,52,60,54,54,64,64,64,68,68,68,72,72,72,68,68,68,68,72,36,38,38,40,40,40,40,34,34,34,34,40,40,40,62,58,58,40,40,40,40,44,44,62,62,32,32,32,32,38,38,50,50,58,58,60,60,60,88,88,88,92,92,102,102,118,118,118,118,96,96,92,92,86,86,86,86,94,96,96,96,92,92,90,90,84,84,84,84,86,86,92,92,100,100,102,102,102,102,106,106,106,106,94,94,94,94,94,94,92,92,86,86,86,86,92,88,86,86,80,80,80,80,94,94,94,94,102,102,102,102,104,104,110,110,110,110,108,108,100,100,100,100,98,98,92,92,92,92,84,84,84,84,90,84,74,74,74,74,82,82,82,82,82,82,74,74,74,74,80,80,80,80,68,68,68,68,72,72,78,78,78,78,76,76,76,76,70,70,70,70,84,84,84,84,76,76,76,76,84,84,86,86,86,86,82,82,76,76,76,76,78,78,80,80,80,80,80,80,72,72,70,70,70,70,72,72,80,80,72,72,74,74,88,88,96,96,104,104,106,106,106,106,104,104,100,100,98,98,98,98,94,94,90,90,90,90,98,98,108,108,114,114,114,114,110,110,106,110,116,116,116,116,108,108,108,108,104,104,104,104,108,116,116,116,124,124,124,124,116,116,116,116,108,108,108,108,98,98,98,98,94,94,94,94,88,88,88,88,88,88,82,82,82,82,96,124,124,124,130,130,132,132,132,132,128,132,136,136,138,138,138,138,130,130,130,130,142,142,142,142,132,132,132,132,134,134,134,134,130,130,130,130,124,124,124,124,126,126,128,128,128,128,122,122,120,120,124,124,124,124,122,122,120,120,120,120,114,114,114,114,110,110,110,110,122,122,122,122,120,120,120,120,116,116,116,116,110,110,110,110,110,110,112,112,114,114,118,116,116,116,120,120,120,120,128,128,128,128,122,122,122,122,114,114,114,114,112,112,110,112,116,116,116,116,114,114,110,106,106,106,114,114,114,150,150,150,152,152,160,160,160,160,150,150,150,150,158,152,152,152,152,152,152,152,146,146,146,146,154,154,154,154,162,162,162,162,152,152,160,160,160,160,154,170,174,174,174,174,166,166,166,166,176,176,176,176,170,170,170,170,170,174,184,184,184,184,178,174,160,160,160,160,172,164,164,164,158,164,174,174,174,158,158,158,172,158,158,158,172,166,166,166,172,166,160,160,160,160,164,164,164,164,164,164,170,170,170,190,180,180,180,180,192,192,192,192,190,184,184,184,184,184,198,198,198,198,186,186,198,198,198,198,180,180,180,176,186,176,174,174,174,174,196,196,196,36,14,14,14,14,14,14,26,26,26,26,38,38,38,38,26,38,40,26,26,26,40,40,40,14,42,42,42,28,36,36,44,42,46,54,46,64,48,48,48,48,64,54,46,62,50,50,50,50,62,58,58,48,48,48,54,54,64,64,64,48,64,40,46,50,50,50,52,52,68,66,66,76,76,76,94,94,94,84,84,84,78,84,86,86,86,86,92,72,72,72,78,78,88,88,96,96,98,72,72,72,72,72,78,78,84,92,102,102,102,102,90,90,74,74,70,70,70,74,74,74,78,78,88,88,102,88,76,76,74,74,74,74,82,82,84,84,98,98,98,98,80,80,70,22,14,14,14,14,34,14,14,14,32,32,32,32,22,22,22,22,14,14,14,14,34,100,118,100,96,96,96,96,104,104,104,104,106,106,112,112,112,112,114,124,134,134,134,134,126,134,138,144,154,144,142,142,142,148,148,164,174,164,160,160,160,160,160,160,166,166,166,68,86,86,86,86,60,60,60,60,70,74,74,74,68,68,68,68,76,76,76,76,74,74,74,160,150,150,150,150,140,140,140,140,118,118,118,118,142,142,142,142,144,144,144,144,160,160,162,162,162,162,146,146,146,146,164,164,164,164,156,156,156,156,174,174,174,174,170,170,166,166,166,166,158,156,156,158,158,158,152,152,152,152,154,154,160,160,160,160,156,156,134,134,106,106,106,106,116,116,116,116,122,122,122,122,128,128,128,128,138,138,138,138,146,146,146,146,154,154,154,154,160,160,160,160,162,162,166,166,166,158,164,170,190,190,190,170,166,170,188,188,188,186,192,186,194,186,194,188,194,166,190,190,190,166,186,186,186,146,142,142,142,142,148,148,148,148,146,142,148,138,138,174,180,180,180,162,158,158,158,158,152,152,122,122,102,102,102,102,106,142,142,160,164,160,154,154,154,154,162,162,154,154,154,154,160,160,152,152,152,146,140,140,140,140,148,148,138,138,138,138,148,134,134,134,138,122,122,122,126,1,4,4,46,110,88,88,88,88,110,110,110,110,96,96,96,90,112,88,112,112,112,112,102,102,102,102,88,88,88,88,110,110,110,100,100,82,82,116,92,92,92,92,118,114,114,114,98,98,98,98,106,106,106,106,106,106,116,116,118,118,118,108,116,116,116,112,112,112,112,106,104,104,104,118,118,118,98,98,98,98,116,104,120,78,78,78,44,44,44,180,180,180,152,152,150,150,150,150,146,146,146,146,144,144,128,128,128,128,138,138,138,138,160,160,162,162,166,166,166,166,176,176,176,176,180,180,184,184,198,198,198,198,166,166,166,166,166,166,160,160,160,160,144,144,144,144,182,182,182,182,166,166,166,166,166,146,144,144,144,144,56,56,56,56,26,26,26,26,38,38,38,38,32,32,26,32,46,46,50,50,64,64,66,66,78,78,86,86,94,62,62,62,66,66,66,66,74,74,74,74,80,80,80,80,88,88,88,88,198,198,196,156,24,36,36,36,28,28,28,28,36,36,36,36,28,28,36,36,28,24,14,14,14,14,12,12,12,6,6,6,14,14,14,14,20,24,28,6,14,8,12,12,12,24,24,24,14,32,32,32,26,26,24,24,22,22,22,22,26,26,28,28,38,38,38,38,24,24,20,20,8,8,8,8,28,28,30,30,46,46,46,46,28,28,14,14,2,42,46,46,42,46,46,46,54,54,54,50,50,50,60,52,56,56,64,46,46,46,48,48,54,48,50,50,58,36,26,26,22,150,158,150,140,140,136,136,136,136,138,138,146,146,148,148,138,138,138,138,142,142,160,72,96,96,108,108,108,108,92,92,56,56,44,44,44,44,48,48,68,68,68,68,78,78,82,82,82,64,64,64,56,56,58,66,58,58,56,56,56,68,68,68,68,68,74,74,74,74,66,66,76,76,76,76,74,74,70,68,68,68,70,74,88,88,88,88,90,90,92,92,96,96,96,100,100,68,72,68,76,76,68,68,76,50,36,28,24,20,20,20,46,46,50,50,58,58,74,52,52,52,44,88,88,100,114,114,114,112,112,112,130,130,130,130,118,116,116,116,110,112,112,46,46,46,42,42,42,42,46,46,46,40,40,64,88,88,64,66,66,66,66,66,66,66,80,80,80,80,66,66,74,74,74,66,76,76,76,76,72,58,58,66,72,72,72,72,64,56,56,68,68,68,68,68,78,78,78,78,66,66,72,72,72,72,78,78,66,68,76,76,76,110,110,94,94,108,108,108,102,102,116,116,116,116,106,106,116,116,116,116,104,118,124,124,118,98,100,94,92,86,86,86,86,152,152,170,170,136,136,136,136,136,136,136,144,144,148,148,148,148,148,148,156,156,156,156,156,156,170,138,138,138,144,144,154,156,162,162,174,4,14,14,24,24,28,28,28,28,26,26,10,10,8,8,26,26,26,26,6,6,6,6,34,34,34,34,28,28,28,28,36,36,40,6,6,6,8,8,8,8,12,12,26,26,26,26,14,14,14,14,42,42,42,42,38,38,38,38,46,46,52,154,162,162,164,164,166,166,166,166,170,170,170,170,138,138,138,138,150,142,142,142,146,146,146,146,142,136,126,126,138,144,144,156,156,156,150,158,158,158,150,150,150,150,144,144,144,106,122,106,78,122,122,122,78,78,90,90,90,90,80,56,130,128,128,128,130,126,126,130,136,136,138,138,140,140,188,188,188,188,132,136,46,80,106,106,106,106,110,110,122,122,126,70,70,70,82,82,82,82,68,76,76,72,82,82,82,82,68,68,68,68,82,82,82,74,74,72,82,142,142,142,148,148,140,140,148,148,140,140,152,140,154,142,152,152,170,140,148,148,158,158,168,168,170,170,170,150,150,62,62,62,62,52,60,52,8,8,8,8,38,38,38,38,18,18,18,18,46,46,54,54,54,54,52,50,20,20,20,20,54,54,54,54,8,8,8,8,32,34,34,34,14,14,14,14,46,46,46,46,12,12,12,12,46,14,42,14,14,14,20,20,34,34,34,34,42,46,72,80,100,100,100,106,124,124,124,146,172,146,146,146,146,164,186,186,186,164,186,172,172,172,172,178,196,196,196,184,184,176,176,14,14,14,28,28,28,28,40,40,40,50,66,58,54,54,54,78,78,78,92,92,92,92,106,106,118,124,148,126,126,126,126,158,180,180,180,168,168,156,156,174,194,172,174,178,188,188,182,182,172,172,168,172,198,48,48,48,58,46,50,50,64,64,58,58,52,52,44,64,78,70,80,72,80,152,170,150,164,150,158,198,80,80,80,80,80,80,38,38,38,38,82,82,82,82,198,64,64,64,66,66,76,76,80,80,66,66,64,64,60,60,60,60,70,64,48,48,48,48,66,66,66,66,66,66,64,60,60,60,66,66,76,76,80,80,80,80,68,68,64,64,58,58,58,58,58,58,60,60,60,60,68,68,78,78,88,78,78,78,74,74,64,64,62,62,62,62,68,68,70,70,78,78,88,88,94,94,112,112,144,144,144,144,146,146,160,160,160,160,156,156,146,146,124,124,124,124,156,156,158,158,164,164,166,166,170,170,170,170,158,158,152,152,148,148,148,148,128,128,122,122,112,112,84,84,60,60,44,44,36,36,36,36,40,40,44,44,46,46,46,46,54,54,58,58,58,58,74,74,74,74,68,68,66,66,56,56,46,46,38,38,38,38,46,46,56,56,66,66,70,70,76,76,76,76,72,72,66,66,58,54,56,56,64,64,64,58,56,38,38,38,38,38,40,40,40,40,16,16,14,14,14,14,38,38,38,38,42,42,42,42,38,38,36,74,76,76,88,68,68,68,76,72,72,72,82,82,82,82,72,106,106,86,102,102,102,102,102,86,86,86,86,86,86,86,94,94,94,94,96,96,102,102,102,60,70,70,74,74,88,88,88,88,76,76,62,62,52,52,52,52,66,68,80,80,84,84,84,54,54,54,60,64,72,78,78,78,74,74,70,70,64,64,64,64,66,66,68,68,74,74,74,74,70,70,62,62,60,60,60,60,62,62,64,64,68,68,68,68,64,64,62,62,60,60,60,60,66,66,78,78,80,80,80,80,76,76,74,74,70,70,70,70,74,66,78,78,82,78,62,62,78,78,64,64,70,70,74,70,66,70,70,74,74,78,78,62,62,62,58,88,88,88,88,88,88,88,88,2,12,12,42,20,20,86,38,38,86,38,20,20,38,16,18,18,44,44,44,44,42,42,38,38,14,14,14,14,34,34,38,38,42,42,42,42,36,36,24,24,14,22,26,26,36,36,42,42,42,42,36,36,26,22,18,26,40,40,52,52,52,52,50,50,46,46,24,16,26,26,30,30,30,30,26,26,16,16,12,12,12,12,16,20,20,20,10,10,10,10,14,14,22,34,38,38,38,38,32,32,14,14,14,12,12,12,24,24,24,24,16,16,10,10,6,6,6,6,16,16,16,16,12,22,32,32,32,32,34,34,46,46,48,48,48,36,24,24,22,22,22,116,116,116,124,124,128,128,128,128,124,124,118,118,112,112,112,112,114,116,116,116,122,122,128,128,134,134,134,134,126,126,118,118,114,114,114,142,144,144,158,158,158,158,152,152,148,148,146,142,156,156,172,172,172,172,166,166,156,156,148,120,124,124,124,124,122,122,120,120,118,118,116,124,124,124,126,124,126,124,122,122,122,122,120,122,124,124,128,128,124,124,124,124,122,122,122,122,124,124,126,124,120,124,128,128,128,128,126,50,74,74,54,66,66,66,74,74,74,62,76,62,62,62,64,64,58,64,66,66,54,66,76,76,76,76,70,70,54,70,78,78,78,78,78,78,78,78,74,68,62,68,74,74,74,74,60,60,60,60,62,98,122,122,122,122,108,104,98,98,122,122,122,122,118,118,96,118,122,122,122,122,118,118,102,120,120,120,100,120,134,134,138,138,138,138,130,130,130,54,54,54,58,58,60,56,56,56,60,60,64,64,60,64,70,66,66,66,62,62,60,60,60,60,58,58,58,58,60,60,58,58,54,54,54,54,56,56,58,58,60,60,60,60,58,58,60,60,60,60,58,58,48,48,46,46,46,46,44,44,46,46,46,46,48,48,48,48,40,40,30,30,24,24,24,24,34,34,28,8,10,10,12,20,30,30,32,32,36,36,36,36,38,38,44,44,60,60,60,60,58,58,56,56,56,56,54,54,54,54,62,62,58,58,54,54,48,48,48,48,52,52,66,66,70,70,72,72,72,72,68,72,78,78,78,78,70,70,70,70,70,70,70,74,82,92,86,86,76,76,68,68,68,68,56,56,56,56,88,88,88,88,92,92,94,88,88,88,108,108,110,110,110,110,112,112,112,16,16,16,16,16,26,26,26,26,14,18,18,26,28,28,28,28,30,30,86,86,114,114,114,114,78,78,18,18,18,18,20,52,52,52,26,14,18,14,2,18,2,26,96,122,122,122,122,122,142,142,142,142,122,128,128,128,128,134,134,134,126,130,130,126,126,144,140,140,140,140,198,198,198,198,142,142,142,160,160,160,164,164,164,164,170,170,170,170,166,166,166,166,170,170,170,166,166,166,172,172,172,172,158,158,158,158,166,168,160,160,160,160,170,170,170,170,166,166,166,166,166,166,172,172,172,180,184,184,196,196,196,196,188,188,178,126,126,126,122,122,122,122,126,116,124,124,124,124,112,112,112,112,116,100,106,106,110,110,112,112,112,112,94,94,94,94,96,96,102,102,102,28,48,48,48,48,26,28,44,48,48,28,28,38,38,38,50,38,38,38,30,40,30,44,58,52,32,32,32,32,56,56,56,56,32,32,32,32,54,32,32,32,40,40,40,40,48,48,54,102,122,100,100,100,110,110,110,110,104,104,104,104,92,92,114,114,122,98,98,98,114,102,102,102,118,110,110,110,110,140,164,164,180,180,180,180,140,140,140,158,158,158,166,178,178,148,178,178,178,178,174,174,174,174,162,162,154,154,154,154,148,148,144,144,144,144,180,18,18,18,18,18,18,130,130,130,70,130,128,128,78,78,78,78,66,16,36,36,54,54,74,74,74,74,66,66,66,16,18,18,38,38,38,38,42,42,60,60,60,60,66,66,74,74,78,78,78,18,18,18,28,28,40,40,48,48,48,48,30,30,26,26,22,22,18,18,8,10,22,70,78,78,90,90,92,92,98,98,104,104,114,114,116,116,120,120,130,130,140,140,142,142,152,152,154,154,164,164,164,164,152,152,152,152,156,156,156,156,152,152,152,152,146,146,146,146,152,152,152,152,166,166,166,166,156,156,156,156,164,164,164,164,154,154,154,154,146,146,138,138,130,130,128,128,120,120,114,114,104,104,102,102,96,96,94,94,86,86,86,188,188,188,174,174,174,174,176,176,178,178,186,186,194,194,198,198,198,198,188,188,184,184,182,182,194,194,190,190,186,186,184,184,184,184,188,188,188,188,184,184,170,170,178,178,190,190,198,198,198,198,192,192,186,186,182,182,178,178,170,170,170,170,186,186,194,194,198,198,198,198,196,196,196,196,192,192,176,176,174,174,174,174,190,190,190,190,186,186,180,180,168,170,170,170,190,170,138,138,138,92,92,92,80,80,78,78,60,60,58,58,54,54,54,54,76,76,82,82,94,94,96,96,112,112,112,112,38,38,36,36,24,24,24,24,52,52,52,52,24,24,24,24,40,40,62,62,50,50,50,50,54,54,56,56,66,66,68,68,72,168,164,164,156,156,156,156,154,154,150,150,142,142,140,140,134,134,134,134,132,132,130,130,118,118,114,114,100,100,96,96,96,96,92,92,88,88,82,82,80,80,74,74,74,74,70,70,64,64,62,62,54,54,34,8,8,8,2,2,2,2,10,10,16,16,24,24,26,26,30,30,30,30,40,40,46,46,68,68,70,70,74,74,74,74,80,80,84,84,94,94,102,102,116,116,130,130,140,36,36,36,26,26,18,18,18,18,26,26,30,30,38,38,40,40,42,42,42,42,44,44,62,62,62,62,64,64,70,70,80,80,80,80,72,72,72,72,78,78,78,78,86,86,86,86,88,90,112,96,96,96,86,86,74,74,74,74,78,78,84,84,92,92,90,90,88,88,88,112,98,98,98,98,108,108,108,108,104,112,112,112,122,116,130,124,110,110,106,106,100,100,100,100,102,102,110,110,112,112,118,118,118,118,120,124,114,114,114,114,124,124,120,120,110,110,110,110,126,126,144,12,12,12,192,188,188,188,186,186,184,188,188,188,188,188,188,188,188,188,186,186,176,186,10,188,188,188,198,96,96,96,116,116,116,116,114,114,94,94,94,94,106,106,106,106,94,94,94,94,94,94,98,96,92,96,102,102,106,106,106,106,104,104,102,104,112,112,112,112,106,106,104,104,102,102,102,102,100,100,98,98,94,100,94,94,92,92,92,92,106,106,110,110,114,114,118,118,118,118,104,104,100,100,94,94,100,100,104,104,108,108,112,112,114,114,114,114,108,108,92,92,92,92,100,100,104,104,104,94,104,104,112,112,114,114,114,114,118,118,118,118,114,114,114,114,132,132,148,148,148,148,136,136,112,112,132,132,140,140,140,140,148,148,148,148,114,114,150,150,150,150,136,136,114,94,22,22,22,22,54,54,98,52,36,36,48,48,34,34,50,54,76,76,76,76,64,64,64,64,58,58,58,58,50,86,82,86,96,92,84,82,74,80,80,80,86,86,86,86,96,22,32,32,32,32,22,22,34,22,20,20,20,20,34,34,34,34,20,20,32,22,36,22,20,20,20,20,28,28,28,28,36,18,38,18,40,20,20,40,40,20,14,16,24,24,28,28,30,30,30,30,36,36,42,42,44,26,48,48,48,36,36,26,26,26,26,84,82,82,52,52,84,84,84,84,54,54,54,54,66,66,66,66,54,54,54,54,62,62,64,64,66,66,66,66,90,90,90,90,86,86,82,82,80,80,80,80,82,82,80,80,78,80,80,80,78,78,80,82,80,82,84,84,82,82,82,84,84,84,82,82,82,82,84,84,82,82,82,82,84,84,82,82,82,82,84,84,82,82,82,82,84,84,82,82,84,172,170,170,148,148,148,148,166,166,166,166,144,144,144,144,166,166,166,166,146,146,146,146,166,166,162,162,160,160,150,150,150,150,164,164,166,166,182,182,182,182,170,170,166,166,160,160,160,160,152,152,152,152,156,156,166,166,178,178,178,178,166,164,150,150,150,150,156,156,164,164,182,182,182,182,162,162,160,160,156,156,156,156,156,156,174,174,174,174,168,168,156,156,148,148,148,148,162,162,166,166,170,170,170,170,156,156,154,154,142,142,142,142,158,158,158,158,156,156,154,150,154,154,158,158,168,168,168,168,156,156,156,156,172,172,172,172,160,160,160,160,168,168,170,170,178,178,186,186,186,186,182,178,168,168,166,156,156,114,114,114,114,114,102,102,102,102,116,106,106,98,114,114,114,106,106,106,116,116,116,116,102,102,102,114,122,122,122,122,128,128,128,116,116,116,106,106,106,106,120,120,120,120,108,108,108,108,92,92,92,92,92,92,92,92,116,116,116,116,108,108,108,120,116,110,86,26,50,50,50,50,32,32,32,32,16,16,16,16,52,52,82,82,118,118,118,118,70,70,70,70,124,124,124,124,150,150,150,150,38,38,38,38,40,40,146,146,152,152,152,152,140,140,134,134,120,120,16,16,16,16,22,22,22,22,68,68,68,68,160,160,172,172,172,172,168,168,148,148,148,148,174,174,176,176,186,186,186,186,168,168,168,168,176,26,26,26,56,56,90,90,88,88,52,52,18,18,18,28,28,38,38,38,4,20,20,20,40,18,2,2,2,2,66,48,32,32,32,32,48,48,48,68,8,8,8,8,54,54,54,54,78,78,134,134,134,134,54,54,54,54,90,90,90,126,124,124,80,80,198,198,198,98,98,98,68,68,68,68,198,96,96,96,186,186,198,58,58,58,92,92,56,56,2,58,78,78,146,88,198,78,126,134,146,154,198,100,114,114,116,116,116,116,112,112,72,72,72,72,104,104,104,112,132,132,164,164,164,164,158,156,148,148,148,148,162,162,162,162,126,76,76,76,70,70,50,50,42,42,42,42,60,60,64,64,64,64,70,70,144,144,144,138,126,126,118,118,118,118,136,136,152,152,162,162,162,162,156,156,142,142,136,136,136,126,126,126,130,130,154,154,162,162,162,162,152,152,152,152,174,174,182,182,182,182,180,180,158,156,156,156,150,150,112,112,108,108,108,94,94,94,90,90,58,58,52,52,52,52,32,32,26,26,26,26,34,34,60,60,64,64,64,64,70,70,72,80,94,92,86,86,86,86,84,84,88,88,94,94,96,96,100,100,100,100,94,94,76,104,146,146,170,170,170,170,170,170,152,152,100,100,100,100,88,88,74,74,62,62,50,50,50,50,52,52,54,54,68,68,70,70,90,90,96,96,66,66,56,56,54,54,40,40,34,38,52,52,54,70,80,80,84,84,98,98,98,98,84,84,74,74,62,62,58,58,48,48,36,36,36,36,50,50,54,54,64,64,66,66,78,78,80,80,94,94,94,94,74,74,62,62,50,52,68,68,72,72,80,80,90,90,94,94,94,94,82,82,78,78,78,78,76,76,50,50,50,50,62,64,88,88,92,92,92,92,100,110,110,124,124,160,160,160,160,140,140,134,134,138,138,128,128,140,152,152,162,162,162,162,150,150,134,134,134,134,140,134,124,124,124,124,120,120,114,114,114,114,118,118,118,118,100,100,100,100,116,116,116,116,108,108,108,108,114,108,124,124,124,124,134,104,108,108,102,96,100,98,100,100,104,112,120,112,118,98,106,28,20,20,20,20,22,22,24,22,16,16,16,16,34,34,34,34,26,34,42,42,42,42,40,40,32,116,122,110,118,110,118,160,160,28,28,28,16,16,16,16,60,60,60,60,48,64,10,34,34,34,64,34,62,62,62,62,54,40,30,30,30,44,44,44,40,22,48,26,26,26,40,28,28,28,54,58,30,34,34,34,58,58,30,26,18,28,66,66,66,36,36,36,36,62,62,62,60,60,38,38,30,48,48,16,60,152,152,152,162,162,162,162,150,150,138,138,134,134,134,134,138,138,148,148,178,178,178,178,152,152,152,152,158,158,160,160,164,164,180,180,180,180,174,174,130,130,116,116,114,116,130,130,176,176,184,184,184,184,158,158,158,170,162,162,162,162,172,172,172,172,160,160,160,160,174,174,174,174,164,164,164,164,174,174,174,180,180,180,188,188,188,188,180,188,176,176,176,176,188,188,188,188,180,180,180,180,188,188,188,158,132,138,138,144,144,130,130,138,142,146,152,158,166,172,178,178,172,166,160,152,146,140,134,138,142,148,154,160,166,172,178,174,168,162,156,150,144,138,132,132,138,144,150,156,162,168,174,158,158,158,162,162,162,162,158,166,172,172,172,172,164,164,164,176,186,186,186,186,174,174,174,194,194,194,194,194,194,180,180,180,180,186,186,186,186,186,186,192,192,192,192,192,192,188,188,188,188,146,142,142,142,142,146,146,146,146,142,142,142,142,146,146,146,146,142,142,142,142,146,146,146,150,150,154,154,154,150,154,150,150,150,150,154,154,154,154,154,154,150,150,150,150,154,154,150,150,150,150,156,156,156,162,162,162,168,168,168,168,162,162,168,168,168,168,160,160,160,160,160,160,164,164,164,164,160,160,164,164,164,164,160,160,160,166,166,166,170,170,170,170,166,166,170,170,170,170,166,166,166,154,150,136,116,116,116,116,138,112,104,104,104,104,94,94,88,112,98,98,98,94,84,84,76,76,74,74,72,80,88,88,88,88,94,94,94,94,94,94,102,102,102,102,110,110,110,110,114,88,82,82,82,82,78,78,74,74,78,158,158,158,174,174,174,174,156,162,184,184,184,184,162,162,162,172,190,190,190,190,170,170,170,186,186,180,180,180,174,170,170,174,174,174,174,174,174,180,180,180,186,186,186,186,178,178,186,186,186,186,178,178,178,178,186,186,186,186,178,178,178,194,194,194,190,190,190,190,194,194,194,194,190,190,190,190,194,194,194,194,190,190,190,190,196,196,196,196,192,192,192,192,198,198,198,114,94,94,94,94,92,92,86,86,86,86,88,88,92,92,96,96,96,90,90,88,94,152,148,148,148,148,154,64,8,30,34,62,64,6,10,64,66,22,50,30,58,30,34,34,42,42,44,18,26,26,66,64,64,60,60,32,32,28,66,34,60,134,118,118,102,102,94,94,80,80,80,80,86,86,96,96,110,110,124,122,122,122,118,118,118,118,112,112,112,112,108,108,108,108,106,106,104,104,104,104,100,100,100,100,96,96,96,96,102,102,102,102,108,108,108,108,114,114,114,114,118,118,118,118,122,122,122,122,126,126,126,130,130,130,148,148,148,130,146,134,134,134,138,138,138,138,132,130,148,148,148,130,130,134,138,138,138,138,134,134,134,162,162,162,162,162,172,172,172,172,172,172,162,168,168,168,168,160,174,160,176,160,176,160,178,158,164,126,126,126,126,126,126,126,126,126,126,126,126,132,140,140,140,132,132,134,142,142,142,132,132,132,140,114,104,104,102,102,102,102,92,92,86,86,86,104,96,96,94,94,94,94,86,86,80,80,80,180,146,146,184,146,146,146,130,130,146,146,146,130,130,130,136,136,128,128,124,124,124,124,130,134,146,160,158,158,148,148,140,140,140,140,146,146,150,150,150,134,124,124,124,124,120,120,120,120,124,134,118,118,118,118,122,122,122,94,94,80,76,76,76,76,82,90,90,124,124,124,124,124,124,124,124,124,124,124,124,158,124,124,162,126,126,126,118,118,118,118,126,126,126,118,106,106,106,106,118,118,106,106,106,106,120,120,120,120,120,120,124,124,124,124,124,124,120,120,120,142,140,140,130,130,124,124,136,136,136,150,150,150,144,144,138,144,148,148,148,148,140,140,138,96,96,96,98,98,102,102,104,104,106,106,112,112,114,114,118,118,118,118,122,122,128,128,128,128,124,124,124,124,130,130,134,134,142,142,148,148,152,152,146,146,146,146,150,150,154,154,158,158,160,160,166,166,166,166,162,162,160,160,158,158,158,158,168,168,168,168,162,162,162,162,166,166,166,166,160,160,156,156,152,152,152,152,154,154,158,158,162,162,162,162,160,160,158,158,154,154,152,152,144,144,144,144,140,140,134,134,132,132,132,132,126,126,118,118,116,116,112,112,112,112,104,104,98,98,96,96,94,126,120,120,118,118,118,118,120,120,120,120,116,116,114,114,112,112,112,112,118,118,120,120,120,120,114,114,112,112,112,112,118,118,120,120,124,124,126,122,122,122,126,92,100,100,102,102,102,102,98,98,100,100,108,108,110,110,110,110,112,112,112,112,110,110,110,110,108,108,108,108,112,112,112,112,108,108,102,102,96,96,94,94,92,92,92,92,90,90,86,86,84,84,78,78,78,78,80,80,80,80,74,74,72,72,70,70,70,70,72,72,72,72,70,70,66,66,64,64,64,64,68,68,72,72,72,72,68,68,72,72,78,78,80,80,82,82,84,84,84,84,80,80,82,82,92,78,88,88,88,88,78,78,78,78,88,88,88,88,76,76,76,90,104,104,104,108,102,102,98,98,98,98,96,96,90,90,88,88,86,86,78,78,76,76,66,66,58,58,56,56,56,56,62,62,58,58,52,52,50,50,50,50,54,54,44,44,44,44,48,48,52,52,56,56,58,58,58,58,54,54,52,52,50,50,48,48,48,48,52,52,60,60,62,62,54,54,60,60,62,62,64,64,64,64,68,68,72,72,74,74,74,74,78,78,88,88,90,90,96,96,98,98,102,102,104,104,110,110,174,174,174,174,104,174,172,172,98,108,110,174,176,174,172,172,160,160,160,128,124,124,114,26,48,30,30,30,54,32,32,32,16,16,16,16,56,56,18,18,18,18,56,34,34,34,34,46,14,14,14,14,28,28,28,28,30,30,46,10,38,38,38,38,12,12,12,12,26,26,22,22,10,10,48,48,12,12,12,12,44,24,24,48,10,10,10,10,30,30,30,30,34,34,44,44,54,54,18,18,22,22,58,64,64,60,122,122,122,110,110,110,102,102,102,96,96,96,88,88,88,82,82,82,76,76,76,70,70,70,64,64,64,64,58,58,58,52,52,68,68,74,74,74,66,80,80,80,86,86,86,92,92,92,102,102,102,106,106,106,112,112,112,120,120,120,120,120,62,62,62,66,66,66,74,74,74,80,80,80,86,86,86,92,92,92,98,98,98,104,104,104,110,110,110,112,112,112,118,118,118,120,124,124,124,118,118,118,110,110,110,104,104,104,98,98,98,92,92,92,86,86,86,80,80,80,70,70,70,64,64,64,58,58,58,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,180,194,194,194,194,178,178,178,182,182,182,198,198,198,198,180,180,180,180,196,196,196,196,180,176,176,176,184,184,184,184,176,176,176,188,194,194,194,194,178,178,178,178,188,198,198,198,188,188,188,188,198,188,178,178,178,178,190,190,190,196,180,180,180,180,198,198,198,188,178,178,178,178,192,192,192,170,138,168,168,168,144,172,152,174,172,172,162,128,128,128,172,172,170,170,144,154,156,156,174,174,162,166,172,172,162,162,164,164,174,20,60,60,60,60,40,40,40,22,22,22,44,26,60,20,14,30,62,62,62,50,14,14,14,14,30,30,34,34,56,56,56,56,50,16,54,54,54,18,18,38,38,58,26,26,26,26,42,42,42,42,50,50,60,60,62,64,72,72,86,86,80,80,76,76,76,76,66,66,82,82,82,82,66,66,66,66,88,66,66,66,74,74,86,86,88,86,68,84,122,122,122,78,114,114,114,76,76,96,96,88,122,122,122,122,88,94,94,94,122,100,100,100,130,130,130,130,144,114,114,120,92,94,120,120,82,156,132,132,132,132,158,142,142,134,150,150,150,150,132,132,132,132,140,140,140,140,152,152,152,144,190,190,190,190,198,198,198,198,184,184,182,154,154,154,138,138,138,138,150,150,180,166,166,148,194,194,194,194,158,158,158,178,178,188,166,166,166,166,176,176,182,182,184,182,166,166,166,166,196,188,152,152,152,152,178,178,184,184,186,184,142,162,190,156,148,152,152,152,184,164,164,164,178,178,186,186,194,194,194,176,176,32,26,26,16,16,16,16,28,28,40,40,68,68,68,68,46,46,42,42,38,38,28,28,18,18,18,18,34,42,38,38,38,38,46,46,46,46,36,32,42,42,42,42,34,34,34,34,38,46,52,52,54,54,54,54,48,48,44,40,40,38,38,66,76,76,76,76,74,74,64,76,86,86,96,96,98,98,106,106,108,108,130,130,120,120,116,116,110,110,110,30,30,30,24,34,34,34,32,32,26,26,24,10,6,14,12,12,8,18,18,18,14,22,22,30,30,40,40,44,48,48,56,52,54,54,60,58,64,70,70,70,76,70,70,62,62,62,68,52,52,40,40,36,26,24,20,20,12,16,8,12,6,18,8,16,22,18,16,16,8,20,64,84,122,144,190,132,166,64,74,74,74,20,22,100,100,100,100,100,100,100,100,100,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,100,22,22,22,22,22,26,26,30,30,36,36,38,38,52,52,56,56,68,68,70,70,88,88,88,88,78,78,74,74,66,66,62,62,62,62,60,60,54,54,44,44,42,42,38,38,38,38,58,58,60,60,70,70,84,84,86,86,96,96,98,98,110,110,110,110,114,114,114,114,112,112,108,108,100,100,98,98,94,94,94,94,92,92,88,88,80,80,80,80,78,78,70,70,70,70,68,68,66,66,56,56,54,54,48,48,48,48,50,50,50,50,36,36,36,36,48,48,52,52,64,64,66,66,72,72,72,72,70,70,70,70,76,76,80,80,84,84,84,84,88,88,92,92,102,102,106,106,108,108,108,108,110,110,116,116,122,122,124,124,130,130,130,130,134,134,138,138,146,146,148,148,156,156,156,156,140,140,140,140,154,154,154,154,152,152,148,148,142,142,140,140,136,136,136,136,150,150,150,150,146,146,144,144,140,140,140,140,138,138,126,126,126,126,130,130,136,136,140,140,140,140,136,136,134,134,126,126,124,124,120,120,120,120,130,130,130,130,124,124,120,120,116,116,116,116,108,108,98,98,96,96,96,106,106,106,110,110,114,114,116,116,116,116,118,118,124,124,128,128,128,128,120,120,120,120,122,122,130,130,136,136,136,136,124,124,124,124,132,132,128,128,110,110,102,102,88,88,86,86,80,80,78,78,76,76,76,76,72,72,66,66,62,62,60,60,52,52,26,26,26,26,36,36,44,44,44,44,42,42,38,38,34,34,32,32,32,32,42,42,42,42,28,28,26,26,26,26,30,30,34,34,44,44,52,52,54,54,54,54,52,52,48,48,46,46,46,46,52,52,56,56,70,70,72,72,78,78,80,80,94,94,94,94,102,102,108,108,116,116,118,118,120,120,120,120,116,116,116,116,120,120,128,128,136,136,138,138,142,142,142,142,136,136,142,142,150,150,158,158,160,160,164,164,164,164,160,160,160,160,174,174,174,174,172,172,168,168,166,166,166,166,178,178,178,178,170,170,170,170,180,180,180,180,176,176,174,174,168,168,164,164,164,164,178,178,178,178,176,176,174,174,170,170,170,170,158,158,158,158,166,166,176,176,182,182,182,182,180,180,178,178,170,170,156,156,154,154,154,154,150,150,148,148,128,128,124,124,120,120,120,118,116,116,106,106,104,104,100,100,100,100,98,98,84,84,82,82,72,72,60,60,50,50,48,48,40,40,38,38,30,30,28,28,22,22,18,18,16,16,16,16,22,26,34,34,34,34,26,26,24,24,14,14,14,14,14,14,20,20,18,18,6,6,6,6,14,14,20,20,20,20,18,18,18,18,22,22,26,26,30,30,32,32,40,40,40,40,34,34,34,34,30,30,28,28,10,10,2,2,2,2,4,4,8,8,14,14,16,16,32,32,34,34,38,38,38,38,42,42,60,60,70,70,72,72,78,78,78,78,66,66,64,64,56,56,54,54,46,46,42,42,36,36,34,34,26,26,24,24,10,10,8,8,6,6,6,6,12,12,14,14,16,16,16,16,18,18,20,24,32,32,40,40,42,42,46,46,46,46,40,40,32,32,28,28,28,28,24,24,20,20,16,16,16,16,14,14,12,12,8,8,8,8,18,18,18,18,14,14,8,8,8,8,10,10,18,18,20,20,20,20,8,8,8,8,12,12,18,18,22,22,22,22,10,10,10,10,22,22,26,26,34,34,36,36,46,46,48,48,48,48,44,44,44,44,52,52,54,54,64,64,66,66,72,72,72,72,74,74,76,76,76,76,80,80,82,82,84,84,84,84,88,88,92,94,110,110,112,112,116,116,116,116,112,112,112,112,118,118,124,124,138,138,140,140,146,146,146,146,144,144,142,142,140,140,140,140,148,148,152,152,160,160,162,162,168,168,162,162,162,162,168,168,172,172,178,178,180,180,188,188,184,184,178,178,176,176,176,176,186,186,186,186,182,182,180,180,180,180,192,192,192,192,188,188,186,186,184,184,182,182,180,180,180,180,194,194,190,190,188,188,186,186,182,182,182,182,192,192,192,192,186,186,182,182,180,180,180,180,194,194,190,190,188,188,186,186,184,184,184,184,176,176,174,174,164,164,154,154,150,150,144,144,134,134,132,132,124,124,116,116,114,114,106,106,104,104,96,96,94,94,86,86,84,84,76,76,72,72,66,66,64,64,56,56,54,54,46,46,44,44,34,34,32,32,22,22,18,18,8,8,6,6,2,2,2,2,4,4,10,10,12,12,12,12,2,2,2,2,10,10,12,12,12,12,2,2,2,2,8,8,8,8,2,2,2,2,2,2,4,4,6,6,12,12,12,12,2,2,2,2,12,12,12,12,2,2,2,2,10,10,10,10,2,2,2,2,10,10,10,10,2,2,2,2,4,4,6,6,8,8,8,8,2,2,2,2,10,10,12,12,20,20,22,22,30,30,32,32,34,34,34,34,38,38,48,48,52,52,66,66,68,68,76,76,78,78,80,80,80,80,74,74,74,74,78,78,84,84,96,96,104,104,106,106,118,118,120,120,126,126,126,126,122,122,124,124,132,132,144,144,148,148,152,152,152,152,148,148,148,148,150,150,164,164,166,166,170,170,172,172,170,170,168,168,168,168,170,170,184,184,184,184,180,180,198,198,198,198,190,190,190,190,198,198,198,198,192,192,192,192,198,198,198,198,188,188,188,188,198,198,198,198,190,190,190,190,198,198,198,198,190,190,190,190,198,198,198,198,192,192,192,192,186,186,150,150,146,146,138,150,150,150,144,144,144,144,146,146,154,154,162,162,162,162,152,152,152,152,162,162,162,162,154,152,152,152,156,156,158,158,158,158,148,148,160,160,160,160,152,152,140,140,136,136,136,136,142,142,138,138,128,128,128,128,144,146,158,158,160,160,170,170,172,172,182,182,192,192,194,194,194,194,186,186,186,186,196,196,196,196,186,186,186,192,192,192,188,188,186,186,184,184,184,184,168,168,160,160,156,156,156,156,144,144,136,136,134,134,126,126,114,114,106,106,104,104,96,96,76,76,70,70,64,64,46,46,42,42,38,38,18,18,12,12,10,10,4,4,2,84,96,96,96,96,80,94,104,104,112,112,110,110,102,102,96,96,92,110,120,120,124,124,124,74,46,46,46,46,66,66,66,66,46,46,46,46,74,54,74,52,48,48,48,48,54,54,54,44,80,80,80,56,56,84,84,84,112,112,86,86,86,86,114,114,114,98,98,104,76,88,76,82,84,84,102,90,62,62,62,74,88,88,88,88,72,72,72,76,46,46,46,46,58,58,58,58,62,62,78,118,160,138,138,138,124,136,140,140,160,146,132,132,132,132,146,146,146,146,118,146,146,110,140,140,140,132,132,132,104,104,104,118,118,134,86,110,110,110,104,104,86,108,108,108,128,112,82,82,82,104,90,90,90,90,104,104,104,104,68,68,68,68,82,82,82,82,86,86,102,102,104,158,158,158,190,190,190,190,182,172,172,172,156,166,176,176,176,176,156,156,156,156,162,162,162,168,142,150,150,150,162,154,154,154,178,154,146,146,146,146,172,172,172,172,172,172,158,158,158,136,166,144,144,134,184,184,184,184,180,16,16,16,16,16,16,16,16,16,16,24,24,24,24,24,24,24,24,24,24,14,14,2,48,48,48,14,24,24,24,24,12,12,12,12,12,12,12,12,22,22,22,16,16,16,16,16,4,14,14,14,10,10,2,2,2,14,8,8,6,14,2,14,10,10,2,2,2,14,14,14,12,12,2,14,20,20,20,20,12,18,18,14,22,22,22,18,18,28,36,36,44,44,44,44,30,30,28,14,20,14,20,52,64,52,64,52,62,50,58,54,62,54,66,50,68,10,10,18,18,24,24,30,30,34,34,50,66,38,76,44,44,44,76,48,48,48,82,54,76,48,52,52,52,44,86,86,86,82,82,82,116,116,90,90,90,90,116,116,116,104,104,108,62,74,58,68,72,72,86,86,98,76,46,46,46,58,58,58,72,72,72,72,54,66,38,38,38,38,48,48,48,48,66,66,68,118,156,140,140,140,128,128,126,138,140,140,158,144,128,128,128,128,146,146,146,138,138,138,110,110,132,132,132,98,124,124,124,100,100,110,110,90,118,98,86,94,102,102,114,114,116,104,74,74,74,86,86,86,100,100,100,100,82,92,60,60,60,60,70,70,70,70,74,74,94,168,152,152,152,152,188,188,188,188,178,168,168,168,148,148,148,148,156,156,156,168,168,168,162,154,126,132,132,132,150,134,134,134,166,122,122,122,148,148,148,148,148,148,130,130,130,140,106,114,114,104,148,148,148,148,140,68,68,94,94,144,144,42,2,2,2,2,32,32,32,32,2,2,2,2,32,32,32,32,30,30,2,2,2,2,6,6,24,24,24,24,2,2,2,2,10,10,10,10,6,6,2,2,8,8,8,8,14,14,14,14,2,28,10,10,10,10,22,22,22,2,2,2,26,26,26,26,14,14,14,14,10,10,10,10,6,6,6,6,2,2,8,8,8,8,14,14,14,14,18,18,18,18,4,4,4,4,10,2,2,16,12,12,12,12,16,16,16,16,10,2,8,8,32,32,32,32,18,18,18,18,10,10,10,10,2,2,2,6,6,6,14,14,14,14,8,8,8,8,2,2,2,16,16,16,24,198,198,198,150,150,150,158,158,158,166,166,180,180,182,182,192,192,198,198,184,184,184,184,178,178,178,178,172,172,172,172,162,162,162,184,184,184,192,192,192,192,198,196,196,196,188,188,188,188,194,194,194,194,176,176,176,176,166,188,196,196,196,196,196,196,136,136,128,156,158,158,194,194,194,186,196,196,196,196,190,190,190,190,196,196,196,196,186,186,186,186,176,176,176,176,168,168,168,188,196,196,196,146,146,146,154,154,154,154,160,110,98,98,94,94,94,94,78,78,44,44,30,30,44,44,76,76,98,98,98,98,104,104,106,106,118,118,98,98,98,98,102,102,102,102,114,114,100,100,104,104,104,38,38,38,42,42,44,44,56,56,56,56,52,52,42,42,42,50,48,48,40,40,40,40,42,42,44,44,54,54,46,46,44,44,38,104,104,104,110,110,114,114,118,118,118,118,120,120,130,130,134,134,134,134,136,136,142,142,144,144,158,158,158,158,154,154,152,152,150,104,104,104,106,106,114,114,128,128,134,134,136,136,142,142,154,154,156,150,148,148,140,140,128,128,126,126,118,118,116,116,112,14,36,54,52,52,44,44,44,44,46,46,58,58,54,92,92,92,82,82,68,56,66,66,68,68,68,68,60,60,54,54,52,190,182,182,182,182,190,190,190,190,184,184,192,192,192,192,184,184,184,184,192,192,182,182,188,188,188,188,180,180,190,190,190,190,184,184,192,192,192,192,184,184,184,184,192,192,184,184,184,184,190,190,190,190,184,24,12,12,12,12,34,34,36,36,60,60,34,34,32,32,26,36,36,36,30,36,36,36,24,48,42,42,50,50,42,42,42,42,48,70,40,40,38,38,52,52,52,52,40,40,40,40,44,44,70,54,70,54,84,84,94,44,38,46,38,54,54,54,58,58,70,70,72,72,72,72,64,64,62,62,60,60,60,52,70,70,74,74,66,66,62,62,54,2,2,106,108,108,118,118,118,118,128,128,142,142,156,148,136,136,126,126,118,118,114,166,166,166,178,178,178,178,156,156,174,174,174,174,156,156,156,156,170,166,166,170,170,166,166,166,178,178,182,182,186,166,166,166,168,168,178,166,154,36,30,36,28,166,166,166,154,168,168,168,174,174,176,68,86,86,86,86,100,100,118,118,132,132,132,132,138,138,138,138,128,128,128,128,138,138,138,138,128,128,128,128,136,136,136,136,56,56,56,56,60,60,60,60,64,64,64,64,72,72,72,72,72,72,80,80,80,92,92,92,102,102,102,102,136,102,88,88,88,88,138,118,118,86,110,110,110,110,90,90,84,84,84,138,140,140,152,152,152,152,150,150,132,132,144,144,150,146,146,146,130,130,130,130,142,142,142,136,146,146,152,152,152,152,130,130,130,130,148,148,148,148,146,150,138,136,136,152,152,152,152,152,152,92,130,130,130,130,112,112,108,108,108,10,10,10,20,20,20,20,22,22,32,32,32,32,42,42,48,48,50,50,50,50,62,48,98,98,98,104,104,104,108,108,104,102,104,108,146,146,156,76,66,66,56,56,40,40,30,30,24,24,10,10,10,10,2,2,2,2,12,138,146,146,156,22,32,32,36,36,36,36,12,12,12,12,24,24,24,24,34,34,34,26,26,26,10,10,2,168,178,178,194,194,176,176,176,176,194,194,194,194,176,176,176,176,192,192,192,192,176,176,192,192,192,192,176,176,176,176,188,188,188,188,178,178,168,180,198,198,198,198,174,76,76,76,72,72,54,54,52,52,52,52,64,64,72,72,72,72,66,66,62,62,62,62,78,78,82,38,38,38,42,42,48,48,58,58,58,58,50,50,44,44,34,34,34,34,34,34,34,34,62,62,68,68,68,68,76,76,76,76,72,76,82,82,82,82,92,92,98,72,72,72,78,78,80,80,80,80,68,68,64,64,64,64,64,64,68,68,72,72,72,72,44,44,30,30,24,32,40,40,32,28,44,44,44,26,24,24,16,16,16,16,64,64,68,68,70,70,70,64,34,34,26,52,60,60,60,60,54,60,50,50,40,52,44,44,38,38,34,34,34,22,14,14,14,14,20,20,20,20,28,28,28,28,60,60,60,60,50,50,46,46,46,46,60,62,66,66,90,90,90,64,64,64,82,82,82,82,60,60,60,60,26,46,46,46,34,34,30,30,12,12,12,12,20,20,28,28,32,32,42,22,22,22,62,80,80,80,82,82,112,112,120,120,120,120,122,122,132,132,132,132,136,136,156,156,156,156,124,124,110,110,110,110,94,96,96,96,118,148,148,148,134,134,134,146,146,146,96,96,96,78,78,78,104,104,116,116,116,120,120,120,102,102,92,92,102,102,102,102,102,102,110,114,116,116,136,136,150,150,154,154,154,154,144,144,144,144,142,142,140,140,130,130,124,124,106,106,82,82,72,72,94,94,102,102,102,84,88,88,102,102,102,102,126,140,144,144,170,170,160,160,146,146,128,128,118,118,118,118,134,104,128,128,134,134,142,142,142,84,84,84,96,108,108,108,116,116,120,116,112,112,104,104,96,96,96,96,88,88,88,88,102,102,102,102,108,108,98,106,106,106,88,88,88,88,74,74,74,74,68,70,80,80,82,82,90,116,108,108,108,114,122,122,130,130,138,138,138,116,116,110,118,110,116,120,124,140,160,160,160,160,152,128,128,128,142,142,142,142,148,146,134,128,128,128,120,124,124,124,124,124,136,138,150,150,150,150,152,152,160,160,160,138,154,154,160,160,160,154,154,154,166,166,166,172,172,172,158,124,124,124,112,112,112,98,98,88,88,68,70,70,80,26,48,22,22,22,22,22,34,34,34,34,16,16,16,16,42,42,42,28,28,28,64,64,64,64,46,46,46,46,54,54,54,44,40,26,20,20,20,20,12,12,12,12,46,46,46,46,60,60,60,60,70,70,70,70,64,20,20,20,38,38,38,48,60,60,60,60,74,74,74,74,46,46,46,68,68,68,56,56,56,56,26,26,26,32,40,34,34,30,38,20,20,20,6,6,6,6,14,24,16,16,16,16,34,34,34,24,24,24,42,42,42,42,34,38,38,38,54,54,54,54,64,64,64,56,52,52,52,52,34,34,34,34,42,42,42,54,50,50,50,50,42,20,16,16,16,16,10,10,10,10,16,16,16,16,50,50,50,50,62,62,62,2,8,8,8,8,2,2,2,42,42,42,46,46,46,46,54,54,54,54,58,58,58,58,80,80,80,80,90,90,156,156,156,156,112,40,36,36,36,36,38,38,38,38,32,32,32,32,36,36,38,38,44,44,46,46,46,46,44,44,44,44,46,46,50,50,52,52,52,52,54,54,60,60,60,60,62,62,64,64,66,66,68,68,74,74,76,76,80,80,82,82,88,88,88,88,90,90,94,94,98,98,98,98,106,106,110,110,110,110,104,104,100,100,96,96,94,94,88,88,86,86,80,80,78,78,76,76,76,76,66,66,62,62,56,56,54,54,54,54,60,60,58,58,52,52,46,46,44,44,38,60,82,82,88,92,122,122,122,92,92,98,98,98,102,102,102,102,102,102,108,108,108,108,98,98,108,108,108,108,96,96,96,104,104,108,98,98,98,104,104,104,110,116,116,116,116,116,116,152,152,152,160,160,160,160,150,160,174,174,174,162,162,162,172,172,172,164,164,160,176,176,176,176,160,160,160,160,152,156,156,156,166,166,166,166,182,182,182,178,186,156,156,148,178,178,178,178,174,178,174,168,132,40,58,58,58,58,48,48,48,48,56,50,50,50,58,58,58,58,50,46,46,46,54,54,54,42,60,50,50,50,60,50,48,48,40,48,48,48,56,56,56,56,62,62,62,34,58,58,58,42,42,46,54,54,54,54,46,46,46,42,42,42,56,56,56,36,58,50,50,50,58,48,42,60,60,60,66,66,66,66,58,66,70,70,70,70,78,78,78,78,70,70,70,52,76,76,76,62,62,62,74,74,74,70,70,70,64,64,64,64,64,64,72,72,72,72,78,78,78,60,78,68,68,68,78,68,62,58,58,58,76,76,76,72,72,72,66,66,66,62,62,62,72,72,72,72,78,78,78,48,76,76,76,66,66,72,72,72,62,62,62,62,80,80,80,74,60,64,64,64,74,56,50,46,42,36,30,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,32,38,40,44,48,54,58,64,68,74,74,74,74,74,74,74,74,78,82,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,84,78,74,70,66,62,58,52,52,52,52,52,52,52,52,48,22,24,24,24,82,82,82,82,108,108,108,108,90,90,90,90,102,102,102,102,96,96,96,96,92,92,92,92,106,106,106,106,86,86,86,86,112,112,112,112,92,92,92,92,68,68,68,68,84,84,84,84,72,72,72,72,88,88,88,88,62,62,62,62,138,138,138,138,162,162,162,162,144,144,144,144,154,154,154,154,148,148,148,148,146,146,146,146,158,158,158,158,140,140,140,140,58,58,58,58,48,48,48,48,18,18,18,18,88,88,88,88,114,114,114,114,98,98,98,98,150,150,150,150,174,174,174,174,156,156,156,156,164,164,164,164,160,160,160,160,158,158,158,158,166,166,166,166,154,154,154,154,178,178,178,178,142,142,142,142,102,102,102,102,130,130,130,130,112,112,112,112,126,126,126,126,106,106,106,106,134,134,134,134,118,118,118,118,96,96,96,96,14,14,14,14,52,52,52,52,42,42,42,42,22,22,22,22,46,46,46,46,26,26,26,26,36,36,36,36,30,30,30,46,50,50,50,50,16,16,16,16,46,46,46,46,56,56,56,56,198,198,198,198,174,174,174,174,150,150,150,150,190,190,190,190,186,186,186,186,158,158,158,158,180,180,180,180,194,194,194,194,146,146,146,146,166,166,166,166,130,130,130,130,98,98,98,98,150,150,150,150,112,112,112,112,144,144,144,144,106,106,106,106,124,124,124,124,114,114,114,114,118,118,118,118,122,122,122,122,110,110,110,110,168,168,168,168,172,172,172,172,178,178,178,178,182,182,182,182,186,186,186,186,190,190,190,190,194,194,194,194,198,198,198,198,190,190,190,190,198,198,198,198,188,188,188,188,198,198,198,198,184,184,184,184,134,130,130,130,190,190,190,190,150,150,150,150,168,168,168,168,158,158,158,158,162,162,162,162,166,166,166,166,152,152,152,152,176,176,176,176,142,142,142,142,196,196,196,196,122,122,122,122,6,6,6,6,50,50,50,50,10,10,10,10,116,116,116,116,20,20,20,20,126,126,126,126,120,120,120,120,102,102,102,102,124,40,42,100,100,100,100,100,100,100,100,100,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,100,40,42,100,100,100,100,100,100,100,100,100,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,100,40,42,100,100,100,100,100,100,100,100,100,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,100,40,42,100,100,100,100,100,100,100,100,100,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,100,40,42,100,100,100,100,100,100,100,100,100,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,100,40,40,40,40,40,76,76,76,76,112,112,112,112,128,128,128,128,164,164,164,164,192,192,192,40,76,76,76,76,38,38,38,38,78,78,78,78,110,110,110,110,120,120,120,120,158,158,158,158,188,188,188,188,198,198,198,198,182,182,182,182,198,136,172,172,172,172,154,132,172,172,172,172,130,130,172,172,150,72,42,42,42,42,74,74,74,42,42,42,74,74,74,74,38,68,76,76,56,66,76,94,94,94,104,104,104,104,92,92,92,92,102,102,102,102,90,102,106,106,116,102,102,102,118,96,100,100,100,100,96,96,96,96,96,96,100,100,100,100,94,86,86,86,86,198,198,198,192,198,192,198,190,198,190,190,198,198,190,184,190,190,190,190,182,134,134,134,174,174,174,174,130,106,106,102,102,100,100,110,110,104,104,98,98,86,90,90,90,90,84,86,90,90,90,90,82,86,74,74,62,62,42,42,42,42,50,50,50,50,74,74,108,108,110,110,110,110,126,126,126,126,124,124,114,114,102,102,82,82,82,64,64,64,76,60,92,92,92,92,90,78,68,68,68,68,80,80,80,68,84,92,98,80,100,100,114,114,114,114,114,114,110,110,98,98,78,74,74,88,88,94,94,60,60,60,92,92,92,64,68,64,64,64,68,66,66,66,86,86,86,86,84,90,90,90,100,100,100,100,98,92,88,66,68,50,46,46,32,32,30,30,14,28,28,28,10,14,12,12,2,2,2,36,18,36,36,36,16,58,58,58,42,42,40,150,146,146,146,146,150,150,150,150,150,150,146,146,146,146,150,154,154,154,164,164,164,164,152,160,160,164,154,154,166,154,154,132,136,130,136,136,128,132,138,142,142,142,142,24,28,28,50,50,64,64,74,74,80,80,88,88,88,88,84,84,88,88,92,92,98,98,98,98,92,92,92,92,102,102,102,102,96,96,96,96,102,102,102,102,86,86,74,74,70,70,70,70,64,64,54,54,52,52,52,52,62,62,62,62,54,54,54,54,66,66,56,56,56,56,64,64,64,64,60,60,62,62,62,74,74,74,68,68,16,26,26,26,36,36,36,52,52,52,36,36,36,36,42,56,56,56,70,76,74,74,74,74,94,154,152,1,2,100,100,100,100,100,100,100,100,100,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,100,1,2,100,100,100,100,100,100,100,100,100,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,100,1,2,100,100,100,100,100,100,100,100,100,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,100,1,2,100,100,100,100,100,100,100,100,100,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,100,1,2,100,100,100,100,100,100,100,100,100,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,100,72,72,72,70,70,66,66,66,66,72,72,78,78,84,84,84,84,74,74,70,70,66,74,78,74,68,74,74,70,70,64,78,78,82,82,82,84,114,114,130,130,130,110,110,110,124,124,124,124,136,136,136,136,132,132,132,90,90,90,84,84,96,92,98,88,84,84,78,88,88,88,94,86,86,86,94,102,80,80,80,80,70,70,70,70,80,80,80,80,100,100,100,100,106,98,98,86,88,88,92,86,86,86,90,72,72,64,60,74,74,74,74,64,60,60,58,24,30,30,38,38,38,38,34,34,30,30,34,34,36,36,40,40,42,42,42,42,38,38,34,34,34,34,38,40,48,48,48,48,42,30,26,26,24,24,24,24,28,28,32,32,36,36,36,36,40,40,44,44,42,42,40,40,40,40,38,38,36,36,32,32,32,32,34,34,38,38,40,40,40,40,36,26,22,22,18,18,18,18,34,34,38,38,46,34,34,46,30,30,24,24,20,20,18,18,18,18,20,20,22,22,22,30,30,30,26,26,24,38,38,38,42,42,40,40,38,38,32,32,36,36,44,44,44,44,42,44,30,30,28,28,28,28,30,30,40,28,24,84,78,78,78,78,84,84,88,88,88,88,84,84,76,84,90,90,74,78,76,76,76,76,80,80,92,84,78,78,90,166,160,160,156,156,156,156,162,162,166,166,168,168,168,168,170,170,176,176,180,180,180,180,176,166,162,162,160,160,160,160,164,164,168,168,168,168,164,164,160,160,174,164,162,162,162,162,166,166,170,160,174,168,164,164,164,164,178,172,172,172,170,170,166,166,164,164,164,164,168,168,176,176,178,178,178,178,164,170,166,166,166,166,178,178,168,168,160,160,142,162,162,162,164,164,174,174,176,176,176,176,172,170,170,170,168,168,164,164,162,162,166,166,174,174,176,176,176,176,174,174,172,172,168,168,162,162,156,168,160,160,158,158,158,158,164,162,158,158,156,156,156,156,160,160,166,166,166,166,162,162,152,162,168,168,182,182,182,182,188,188,188,188,186,186,186,186,188,188,188,188,186,186,186,186,188,188,188,188,190,190,190,190,188,188,188,188,182,182,182,182,186,186,186,186,182,182,186,186,186,186,188,188,188,188,178,178,178,178,174,174,170,170,164,164,164,164,162,162,154,154,150,150,148,148,146,146,140,140,138,138,134,134,132,132,130,130,122,122,118,118,114,114,108,108,106,106,104,104,102,102,92,92,90,90,84,84,80,80,78,78,74,74,62,62,58,58,56,56,54,54,52,52,46,46,40,40,34,34,32,32,30,30,26,26,22,22,18,18,18,18,10,10,10,10,12,12,12,12,10,10,10,10,4,4,4,4,10,10,10,10,8,8,8,8,10,10,10,10,12,12,12,12,8,8,8,8,6,6,8,8,8,8,12,12,12,12,14,14,8,8,8,8,10,10,10,10,12,12,12,12,20,20,20,20,16,16,18,18,18,18,20,20,20,20,22,22,24,24,26,26,26,26,28,28,28,28,32,32,36,36,42,42,44,44,46,46,52,52,56,56,58,58,58,58,56,56,54,54,52,52,54,54,58,58,62,62,60,60,60,60,62,62,68,68,68,68,66,66,70,70,72,72,78,78,76,78,86,86,88,88,98,98,100,100,104,104,104,104,102,102,100,100,98,98,98,98,100,100,102,102,104,102,98,98,96,96,94,94,94,94,96,96,96,96,90,90,90,90,92,92,96,96,102,102,106,106,110,110,104,104,102,102,108,108,110,110,114,114,114,114,112,112,112,112,114,114,122,122,122,122,120,120,118,118,116,116,116,116,118,118,120,122,132,132,134,134,140,140,138,138,136,136,132,132,132,132,134,134,136,136,134,134,132,132,132,132,134,134,142,72,72,100,100,100,100,100,100,100,100,100,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,98,100,100,36,36,36,36,36,38,38,38,38,42,42,42,42,44,44,44,44,64,64,72,72,74,74,78,78,80,80,84,84,86,86,108,108,114,114,120,120,132,132,136,136,146,146,146,146,142,142,142,142,140,140,140,140,130,130,128,128,118,118,108,108,102,102,70,70,60,60,60,60,56,56,50,50,44,44,44,44,38,38,38,38,36,36,36,36,34,34,34,28,12,34,30,30,28,28,26,26,18,18,12,46,44,44,36,36,34,34,32,32,30,30,22,22,18,58,58,58,54,54,50,50,46,46,46,46,42,42,42,42,30,74,74,74,68,68,68,68,64,64,64,88,88,88,90,90,90,114,114,114,116,116,116,116,118,118,118,118,124,124,124,132,132,132,140,140,140,140,142,142,142,142,144,144,144,144,148,148,148,148,150,150,160,160,160,160,168,168,168,168,178,152,154,154,164,164,164,164,174,174,178,150,166,166,168,168,180,150,150,150,154,154,162,162,168,168,178,178,180,180,184,142,150,150,152,152,160,160,162,162,166,166,168,168,172,128,128,128,132,132,136,136,136,136,144,144,144,144,146,146,146,146,148,148,148,112,112,112,116,116,116,116,118,118,118,94,94,94,92,92,92,74,74,74,62,62,62,62,56,56,56,46,46,46,38,38,38,38,32,32,32,32,28,34,30,30,26,26,26,26,12,12,12,52,46,46,46,46,50,50,54,54,62,62,64,64,68,68,68,68,66,66,64,64,62,54,50,50,48,48,52,52,54,54,56,56,56,56,52,52,50,58,52,52,50,50,54,54,58,60,54,54,52,52,52,52,56,56,64,64,64,64,62,62,58,58,58,58,60,60,62,62,68,68,68,68,64,64,60,60,64,64,68,68,82,82,84,84,84,84,82,82,78,78,76,76,76,76,78,78,78,78,76,76,74,74,70,70,70,70,72,72,74,80,80,80,78,78,76,76,76,76,78,78,80,80,84,84,84,84,82,82,80,84,74,74,74,74,80,80,78,78,76,76,78,78,80,80,82,82,82,82,80,80,76,80,90,90,90,90,86,86,84,98,76,76,76,76,82,82,84,84,86,86,86,86,84,84,86,86,96,82,88,88,90,90,90,90,82,90,94,76,92,92,94,94,94,94,92,92,90,90,90,90,88,88,86,86,86,86,88,88,92,92,96,96,96,88,88,88,90,90,100,100,100,96,96,104,98,98,96,96,96,96,100,100,104,104,106,106,106,106,102,100,100,106,110,110,112,112,108,108,102,102,102,102,104,104,108,108,114,114,118,118,118,118,112,112,112,112,118,118,118,118,106,98,98,98,64,64,64,64,82,82,82,92,78,78,78,78,90,90,90,72,106,74,74,74,82,82,82,90,90,90,108,108,108,100,100,100,100,100,74,74,92,98,104,108,108,108,144,144,144,144,106,124,124,124,128,128,128,128,120,126,120,120,120,120,124,124,124,132,132,132,136,136,136,136,132,132,136,136,136,136,130,130,130,140,140,140,132,122,116,128,120,116,116,106,106,106,156,156,156,156,142,142,158,158,158,158,110,110,110,156,158,158,198,198,198,158,160,160,198,198,198,160,160,160,144,144,158,158,158,158,144,172,180,180,198,198,198,174,174,174,198,198,198,180,198,198,186,10,16,22,26,32,36,42,68,68,68,68,76,76,76,76,66,76,76,76,76,76,136,136,136,136,74,82,82,86,86,90,90,94,94,98,98,102,102,106,106,110,110,110,112,112,112,112,108,112,170,170,170,170,176,176,176,176,168,168,168,168,110,136,136,136,132,132,132,132,132,132,136,136,136,18,18,18,36,24,24,24,14,26,26,26,34,20,36,36,36,22,32,32,32,32,20,20,10,20,30,30,30,30,16,16,14,14,14,10,4,4,4,4,16,50,50,50,60,60,60,60,66,66,66,66,66,66,48,48,48,48,68,50,64,64,64,64,46,46,46,46,66,66,46,46,46,46,68,56,56,46,64,64,64,54,54,44,44,44,64,42,42,42,50,50,50,48,48,48,64,116,116,26,26,26,20,20,20,20,62,62,62,62,54,54,54,54,46,46,46,46,38,38,38,38,24,24,24,48,48,48,68,68,68,68,76,76,76,76,82,82,82,82,60,60,60,60,50,50,50,50,44,70,76,76,76,76,98,98,98,98,104,104,104,104,94,94,94,94,72,72,72,128,146,146,160,160,158,158,146,146,172,172,172,172,182,182,182,182,156,156,184,184,174,174,174,174,166,166,144,144,144,144,174,174,174,174,184,184,184,184,148,148,180,180,180,180,176,176,176,176,142,142,142,142,118,118,118,118,136,136,136,136,140,140,140,140,138,138,112,112,112,112,104,104,106,106,136,136,120,120,120,120,132,132,132,132,110,110,110,110,146,146,152,152,150,150,144,144,124,124,124,124,128,138,138,138,146,146,146,146,154,154,154,154,164,164,164,164,138,146,164,164,146,146,146,146,168,168,168,168,146,146,146,126,134,134,134,134,124,124,124,156,166,166,166,166,154,154,154,146,146,146,32,32,32,32,148,26,54,38,38,38,56,38,38,38,56,56,56,52,52,52,42,42,42,42,42,42,68,52,52,52,42,42,42,38,38,38,60,50,50,50,40,40,40,36,36,36,48,48,48,36,36,36,66,66,66,66,66,14,14,14,46,46,46,46,46,46,34,34,34,30,30,30,50,24,20,34,34,34,50,38,38,18,18,10,52,52,52,52,48,12,12,12,48,30,30,30,48,48,48,48,6,6,6,34,34,34,44,44,44,24,24,24,46,46,46,42,42,42,38,38,32,32,32,26,26,26,48,48,48,26,26,26,54,54,54,58,58,58,84,84,84,70,70,70,84,84,84,70,68,68,58,58,80,80,80,80,58,58,58,58,58,58,84,84,84,72,72,72,84,84,84,72,60,60,76,76,76,76,60,60,60,56,56,56,86,86,86,58,58,58,72,72,72,72,86,86,86,66,80,80,80,80,64,64,64,10,76,82,86,104,116,116,116,104,134,100,114,114,114,114,98,98,98,104,120,120,120,120,100,100,100,100,112,112,112,112,110,110,102,98,110,112,132,132,132,108,116,116,116,116,106,106,106,100,100,92,114,114,114,130,130,122,152,152,152,118,146,130,130,130,146,138,138,138,132,132,132,132,150,150,150,118,188,188,188,188,118,118,118,118,134,134,134,134,118,118,118,122,122,122,134,122,122,122,134,122,112,112,112,112,134,126,126,114,114,114,136,128,128,122,122,122,136,122,122,122,134,122,122,122,134,122,122,118,102,102,102,102,120,120,120,120,120,120,120,120,120,120,120,120,120,144,144,166,166,162,148,148,148,148,156,156,156,148,164,164,164,164,146,146,146,146,160,160,160,160,146,150,160,150,150,150,164,164,164,154,154,158,144,144,154,154,154,154,160,174,178,178,178,178,172,172,172,166,166,166,90,90,90,90,168,26,26,26,62,62,62,62,42,42,42,42,24,24,24,24,28,34,60,58,14,56,56,46,46,46,12,12,12,12,18,18,34,34,38,38,38,38,40,40,48,14,14,14,50,50,50,28,28,46,6,6,6,6,20,20,20,20,42,22,22,22,26,26,26,26,20,34,34,34,38,88,64,64,64,64,90,72,72,68,86,86,86,86,64,64,64,64,74,74,74,74,86,86,86,116,96,96,96,96,158,158,158,158,170,170,170,170,154,146,92,92,142,114,114,100,100,100,136,136,136,116,116,148,106,106,106,106,130,130,130,130,104,104,104,104,154,126,80,80,80,80,114,114,114,114,64,76,110,74,74,72,62,62,62,62,74,82,82,82,132,96,96,96,120,120,120,120,138,108,108,12,12,94,160,20,62,62,92,18,30,30,30,30,38,38,36,36,28,28,14,18,18,18,34,34,34,34,16,16,16,38,10,14,14,14,38,44,80,80,80,64,64,64,56,56,56,56,74,74,74,74,70,48,68,68,68,72,46,40,40,34,70,70,70,70,66,52,52,52,44,44,44,44,62,62,62,62,58,58,38,44,44,44,62,66,58,52,48,42,38,32,26,20,16,10,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,6,12,14,18,22,26,30,34,38,44,48,52,56,60,64,70,74,80,84,90,94,98,104,110,114,118,122,128,132,138,142,148,152,158,162,166,170,176,180,184,190,196,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,194,190,186,182,178,174,168,166,160,156,152,148,142,138,134,130,124,120,116,112,108,104,100,96,90,86,82,78,72,68,162,162,162,184,184,184,184,168,168,168,168,184,152,182,182,182,178,178,178,152,152,172,186,186,186,146,146,146,146,190,190,190,18,50,18,18,18,38,38,38,38,18,18,18,18,22,22,22,22,50,50,32,26,18,26,26,16,50,50,50,58,58,58,60,60,96,96,46,46,86,86,86,66,66,48,48,48,84,58,42,50,62,62,72,72,82,72,34,34,34,44,64,64,64,64,42,42,42,54,18,18,18,18,34,34,34,34,38,38,56,88,144,112,112,112,98,108,142,124,104,104,104,104,126,126,126,140,140,140,94,94,132,132,132,94,94,94,124,124,124,106,106,116,74,94,94,94,76,88,90,90,104,104,106,98,64,64,64,90,74,74,74,74,88,88,88,64,116,66,66,66,84,84,84,84,86,86,98,98,118,150,150,150,194,194,194,194,184,150,172,172,172,158,188,188,188,188,182,170,170,160,160,160,170,178,146,156,156,156,184,160,160,160,190,150,142,142,142,142,172,172,172,172,156,156,156,170,170,128,164,120,174,174,174,174,168,138,138,130,196,196,196,196,192,192,192,192,196,196,196,196,190,190,190,190,196,196,196,196,180,180,180,180,198,198,198,198,194,194,194,194,198,198,198,198,180,180,180,180,198,198,198,198,176,176,176,176,170,170,170,170,162,162,162,162,158,158,158,158,144,144,144,144,140,140,140,140,118,118,118,118,114,114,114,114,96,96,96,96,92,92,92,92,88,88,88,88,64,64,64,64,56,56,56,56,40,40,40,40,36,36,36,36,10,10,10,10,6,6,6,6,2,2,2,2,14,14,14,14,2,2,2,2,6,6,6,6,2,2,2,2,6,6,6,6,2,2,2,2,44,44,44,44,2,2,2,2,30,30,30,30,2,2,2,2,6,6,6,6,2,2,2,2,20,20,20,20,2,2,2,2,34,34,34,34,2,2,2,2,12,12,12,12,2,2,2,2,10,10,10,10,2,2,2,2,8,8,8,8,34,34,34,34,38,38,38,38,52,52,52,52,56,56,56,56,88,88,88,88,88,88,92,92,92,92,96,96,96,96,120,120,120,120,124,124,124,124,130,74,74,74,88,88,88,88,74,74,88,88,88,88,74,74,74,82,82,82,82,86,74,74,74,74,94,94,94,80,90,90,90,90,96,96,96,96,108,108,108,108,120,94,102,102,102,102,94,94,102,122,122,122,128,128,128,128,120,128,128,128,198,136,136,136,88,88,88,88,82,82,82,82,90,94,122,126,138,154,154,154,88,80,72,72,72,72,68,68,68,68,62,62,62,62,44,44,44,44,62,62,62,62,42,42,42,42,60,60,60,60,46,46,46,46,72,72,72,72,84,154,198,72,62,74,64,64,64,64,60,60,60,60,64,60,44,44,44,44,62,62,44,44,62,46,40,40,40,40,44,136,136,136,198,198,144,144,144,20,42,26,26,18,36,20,20,20,36,36,36,36,26,26,26,12,32,32,32,14,36,36,36,20,20,20,30,30,30,30,18,26,26,26,36,36,36,36,12,24,34,34,34,34,22,30,26,20,8,2,2,2,44,44,44,44,44,44,44,44,44,44,2,38,38,38,48,54,64,72,80,88,96,100,104,104,104,104,104,104,104,10,10,10,194,194,194,194,194,194,194,194,10,10,10,16,20,20,20,20,14,14,14,14,20,20,26,26,28,28,30,30,38,38,38,38,30,30,28,28,24,24,24,24,28,28,30,30,36,36,36,36,42,42,48,48,54,54,54,54,44,44,44,44,46,46,50,50,56,56,56,56,62,62,68,68,74,74,74,74,70,70,66,66,62,62,62,62,84,84,92,92,98,98,98,98,88,88,88,88,96,96,98,98,106,106,106,106,98,98,90,90,90,90,96,96,106,106,112,112,112,112,104,104,100,100,94,94,94,94,114,114,120,120,128,128,128,128,124,124,120,120,120,120,134,134,138,138,154,154,154,154,144,144,130,130,128,128,124,124,118,118,118,118,122,122,128,128,140,140,144,144,152,152,152,152,146,146,142,142,126,126,122,122,110,110,110,110,116,116,120,120,122,122,122,122,116,116,106,106,102,102,102,102,106,106,110,110,114,114,114,114,92,92,92,92,96,96,100,100,110,110,110,110,102,102,90,90,78,78,78,78,72,72,68,68,62,62,62,62,66,66,68,68,70,70,70,70,66,66,58,58,52,52,44,44,40,40,40,40,44,44,46,46,46,46,44,44,42,42,32,32,32,32,38,38,42,42,48,48,52,52,58,58,58,58,50,50,48,48,48,48,54,54,64,64,72,72,72,72,78,78,80,80,84,84,84,84,80,80,80,80,82,82,86,86,98,98,104,104,112,112,112,112,116,116,120,120,124,124,124,124,116,116,116,116,126,126,130,130,134,134,134,134,138,138,142,142,150,150,150,150,146,146,146,146,154,154,158,158,162,162,162,162,160,160,150,150,136,136,132,132,128,128,128,128,132,132,134,134,136,136,136,136,132,132,126,126,122,122,122,122,126,126,136,136,144,144,144,144,132,132,132,132,140,140,140,140,132,132,124,124,112,112,110,110,102,102,102,102,100,100,94,94,92,92,92,92,96,96,98,98,100,100,100,100,96,94,88,88,80,80,78,78,72,72,72,72,76,76,78,78,78,78,76,76,72,72,62,62,58,58,54,54,54,54,58,58,60,60,62,62,62,62,58,58,48,48,44,44,44,44,42,42,38,38,30,30,26,26,20,20,20,20,30,30,36,36,44,44,44,44,32,32,32,32,30,30,24,24,18,18,18,18,14,14,14,14,22,22,24,24,32,32,36,36,46,46,48,50,56,56,58,58,68,68,72,72,82,82,84,84,90,90,92,92,102,102,102,102,98,98,90,90,86,86,86,86,90,90,90,90,86,86,76,76,70,70,70,70,74,74,76,76,76,76,72,72,70,70,62,62,62,62,66,66,68,68,68,68,66,66,60,60,52,52,52,52,54,54,58,58,58,58,56,56,46,46,42,42,42,42,44,44,46,46,46,46,42,42,28,26,42,32,32,32,22,32,40,40,40,40,24,24,24,32,32,26,40,40,40,40,40,40,24,24,30,30,32,32,32,32,40,40,40,40,24,24,24,78,62,62,78,60,60,60,64,64,70,58,58,58,60,64,76,56,80,60,60,60,78,78,78,66,66,58,80,80,80,56,80,80,80,80,52,120,120,120,126,124,118,118,118,118,124,124,132,132,134,134,134,134,138,138,138,138,122,122,112,112,112,112,118,118,118,118,122,122,126,126,126,134,144,144,144,134,146,146,146,118,114,96,96,96,98,98,102,102,100,100,98,98,96,96,92,92,92,92,118,118,118,118,96,96,96,154,162,162,150,156,166,166,172,172,172,172,156,80,80,80,80,80,88,88,88,88,106,106,106,106,110,110,110,110,106,106,106,106,102,78,78,78,74,74,74,74,78,78,78,78,74,74,74,74,70,70,70,70,66,66,66,66,58,58,58,58,54,54,54,108,108,108,112,112,112,112,108,108,108,108,104,104,104,104,96,96,96,96,92,92,92,92,96,96,96,96,92,92,92,92,88,88,88,88,92,92,92,92,86,86,86,86,82,82,82,82,86,86,86,86,92,92,92,92,100,100,100,100,106,106,106,106,114,114,114,114,118,118,118,118,126,126,126,126,134,134,134,134,140,140,140,140,144,144,144,144,148,148,148,148,152,152,152,152,156,156,156,156,152,152,152,152,144,144,144,144,140,140,140,140,132,132,132,132,128,128,128,128,116,116,116,116,100,100,100,100,96,96,96,96,100,100,100,100,104,104,104,104,112,112,112,112,116,116,116,116,120,120,120,120,116,116,116,116,108,108,108,108,104,104,104,104,100,100,100,100,92,126,130,130,130,130,126,126,126,122,122,122,118,118,118,118,122,128,128,128,150,150,150,150,130,130,130,130,134,134,134,134,144,144,144,144,148,148,148,148,124,124,124,124,142,142,142,134,130,130,130,130,126,126,126,126,150,150,150,150,128,128,128,132,148,148,148,148,130,130,130,160,192,192,192,192,174,174,174,174,158,158,158,162,162,162,174,174,174,174,160,176,188,188,188,188,176,176,176,168,168,168,164,164,162,162,160,160,156,156,156,156,160,160,166,166,180,180,180,180,186,186,186,186,188,188,188,188,176,176,168,166,166,166,160,160,164,164,170,170,172,172,182,182,170,170,170,170,166,156,192,192,192,192,178,178,178,178,182,182,182,182,186,186,186,186,190,190,190,190,180,180,180,180,174,174,174,174,170,170,170,170,166,166,166,166,152,152,152,152,156,156,156,156,164,164,164,156,156,164,190,190,190,190,186,186,186,186,182,182,182,182,166,166,166,166,162,162,162,170,188,188,188,188,184,184,184,184,180,180,180,180,168,168,168,164,188,188,188,188,180,180,180,180,176,176,176,176,172,172,172,172,168,168,168,168,162,162,162,162,188,188,188,188,172,172,172,172,168,168,168,168,172,172,172,172,162,162,162,162,166,166,166,166,162,162,162,174,174,174,166,166,166,166,162,162,162,162,166,166,166,166,182,182,182,182,186,186,186,186,194,194,194,194,198,198,198,196,192,192,192,192,188,188,188,188,178,178,178,178,172,172,172,172,168,168,168,168,174,138,142,142,142,142,138,138,138,114,118,118,118,118,122,122,122,122,114,114,114,114,108,108,108,108,114,134,130,130,130,130,140,140,140,140,144,144,144,144,136,136,136,136,132,132,132,56,48,48,48,48,52,52,52,52,48,48,48,48,44,44,44,44,40,40,40,40,26,26,26,26,20,20,20,20,26,26,26,26,32,32,32,32,36,36,36,36,40,40,40,40,44,44,44,44,48,48,48,48,44,44,44,44,40,40,40,40,36,36,36,36,32,32,32,32,28,28,28,32,36,36,36,36,32,32,32,24,24,24,28,28,28,28,24,190,156,156,156,156,174,174,174,174,192,192,192,156,194,194,194,194,154,154,154,154,164,164,174,174,194,194,194,194,172,172,172,172,162,162,154,154,154,154,162,162,162,162,170,170,172,170,162,162,162,162,152,152,152,188,172,172,172,172,190,190,190,186,174,174,174,174,186,186,186,190,168,168,168,168,178,178,178,178,190,190,156,156,156,156,158,158,174,174,178,178,192,192,192,188,160,162,176,176,176,176,186,192,160,160,160,160,192,192,192,158,158,158,170,170,184,184,184,184,190,190,190,158,158,158,174,174,174,174,178,178,178,178,188,188,188,188,192,168,164,160,160,160,168,168,168,188,160,160,160,160,164,164,176,176,176,176,190,132,122,122,122,122,142,142,150,150,150,150,146,146,130,126,130,130,130,130,126,126,126,126,130,130,130,130,126,126,126,134,134,134,144,144,144,144,134,126,126,126,122,122,118,118,118,118,114,114,112,112,112,112,114,114,126,126,130,130,130,130,118,118,110,110,102,102,86,86,86,86,112,112,124,124,124,108,108,108,114,114,114,114,122,114,118,118,126,112,112,96,96,96,102,102,102,102,110,100,108,108,114,98,86,88,90,90,102,102,106,106,114,92,112,104,104,104,114,104,100,100,92,92,92,92,84,88,88,88,96,92,92,92,100,100,100,100,110,98,98,98,90,98,98,98,108,108,108,102,102,102,106,106,112,104,104,104,116,116,108,116,116,112,112,112,114,114,120,120,120,120,120,120,116,116,112,130,122,122,116,130,128,128,116,130,132,132,138,138,138,138,142,142,142,142,134,134,130,130,130,136,146,138,144,138,144,126,142,142,142,142,136,136,132,132,132,132,130,130,126,136,146,146,136,136,148,100,100,100,106,106,112,112,112,108,116,116,110,110,114,104,104,104,108,108,116,116,120,120,120,116,124,124,114,120,126,126,140,140,140,130,164,164,164,164,166,166,168,168,168,168,162,162,162,162,148,148,148,148,142,142,138,138,138,138,128,124,130,130,140,140,162,162,162,162,168,168,168,168,160,160,160,160,140,140,140,140,130,130,106,106,96,96,96,100,98,98,92,92,92,92,94,94,104,100,98,98,94,94,94,94,98,98,106,106,106,102,106,102,102,102,106,136,140,140,140,140,144,144,144,144,138,138,138,138,142,142,142,142,138,140,142,142,142,142,160,160,160,160,164,164,164,164,144,126,158,158,158,158,162,162,162,98,98,98,104,104,120,120,120,120,114,114,110,110,110,110,106,106,92,92,88,88,88,88,92,92,96,96,96,96,98,88,86,86,82,82,82,82,88,88,92,92,92,88,86,86,80,80,80,80,84,84,94,94,98,98,98,98,94,94,94,94,94,94,98,98,98,98,94,102,102,102,110,110,110,110,100,100,110,110,110,110,100,100,100,114,114,114,108,98,102,102,112,112,112,112,110,110,108,108,108,108,112,112,112,124,124,124,126,126,128,128,134,134,134,134,136,136,132,132,128,128,124,110,106,106,104,104,104,104,112,112,116,116,120,120,120,120,112,98,98,98,106,106,110,110,116,116,116,116,112,112,108,130,128,128,120,120,120,120,128,128,128,128,132,132,134,134,134,134,130,130,134,134,138,130,132,132,136,136,136,120,150,150,150,150,124,126,134,134,134,134,126,126,150,150,150,150,116,116,130,130,126,126,114,114,118,150,172,172,172,172,148,148,148,148,170,170,170,170,148,148,148,124,158,158,158,158,142,142,142,142,156,156,156,156,124,124,124,124,140,140,140,124,122,180,180,180,72,72,48,48,28,28,28,28,46,182,182,182,72,70,64,64,44,30,30,22,14,18,8,12,6,24,18,6,2,48,56,56,60,60,60,60,64,64,78,78,78,78,98,98,98,98,100,100,118,118,118,78,98,98,98,98,74,82,82,82,98,98,80,86,86,92,92,96,84,84,84,84,98,98,84,118,114,114,114,114,118,104,168,168,168,168,152,152,152,152,162,162,162,162,174,174,174,174,126,112,122,122,122,122,158,148,134,134,134,134,98,132,132,132,136,136,136,136,140,140,140,140,144,144,144,144,148,148,148,148,154,154,154,154,158,158,158,62,62,62,66,66,82,82,96,96,128,128,136,136,166,166,166,166,160,160,152,152,148,148,148,148,150,150,150,150,142,142,142,142,152,152,152,152,164,162,162,162,166,166,168,168,168,168,166,62,62,62,44,44,44,44,50,50,56,56,60,60,60,60,58,58,42,42,40,40,40,40,52,52,56,56,60,60,62,62,62,62,64,64,92,92,104,104,144,144,148,148,148,148,154,154,162,86,86,86,90,90,94,94,94,86,86,86,76,76,76,76,84,84,84,84,74,74,74,88,88,88,78,78,78,90,90,90,80,80,90,90,90,90,80,80,80,90,90,90,82,82,82,82,94,94,94,94,98,98,98,98,90,90,90,90,102,94,94,94,102,102,102,102,94,94,94,94,102,76,72,72,68,68,68,68,72,72,74,74,80,80,80,80,78,72,72,72,74,74,80,80,70,70,68,68,68,68,72,72,76,76,80,80,80,74,74,74,72,72,74,74,80,42,42,42,42,42,36,36,36,36,48,48,56,56,70,70,70,92,92,80,100,120,110,110,104,104,104,104,118,118,136,136,144,144,144,144,136,136,124,124,120,120,120,120,102,102,102,102,146,146,150,150,150,150,146,146,122,30,70,50,50,30,72,168,168,176,176,156,184,166,166,166,156,156,154,166,166,166,172,172,174,174,180,32,24,24,24,24,50,50,50,50,52,52,56,56,58,58,58,58,64,64,64,64,68,42,38,38,34,34,34,34,32,32,26,26,26,26,26,44,54,54,54,54,42,42,42,28,18,18,18,18,54,54,54,54,70,70,56,36,36,36,42,42,44,44,54,54,54,54,38,38,38,38,28,38,44,44,46,46,46,46,50,148,148,148,140,140,136,136,136,136,134,134,90,90,138,138,150,150,150,92,88,88,84,84,84,84,92,92,92,92,86,86,84,84,82,82,82,82,74,74,72,72,72,72,68,68,64,64,56,56,48,48,40,40,36,36,36,36,34,34,34,34,32,32,32,32,38,38,28,28,28,28,36,36,36,36,32,32,32,32,36,36,36,36,32,32,32,32,38,38,44,44,46,46,46,46,50,50,52,52,60,60,64,64,68,68,70,70,74,74,78,78,84,84,84,84,90,90,90,90,94,94,96,56,50,50,46,46,46,46,40,50,50,50,58,58,60,58,54,74,68,68,62,62,58,82,82,82,78,78,70,70,68,62,54,106,106,106,98,98,98,100,108,108,108,98,94,94,94,94,90,90,90,90,94,94,94,88,84,88,90,90,90,90,86,86,86,144,142,142,138,138,138,148,146,146,142,146,144,142,140,140,144,140,132,128,124,124,124,124,130,130,134,132,130,130,126,138,146,146,142,142,142,146,142,142,134,146,140,140,132,140,138,146,150,138,126,126,122,122,122,128,128,128,126,126,122,124,128,128,128,124,128,128,124,128,128,124,124,132,140,140,140,140,144,132,136,136,136,138,142,134,134,134,140,142,146,104,104,104,112,112,112,100,100,100,102,102,104,104,112,112,112,112,114,114,114,104,112,138,142,142,142,142,148,136,144,144,148,136,138,138,142,142,146,146,148,136,128,148,148,148,144,148,140,148,144,140,148,148,144,140,148,148,144,140,148,148,144,140,148,148,144,140,148,148,144,140,148,148,144,140,148,148,144,140,148,148,144,146,142,146,142,138,146,146,142,142,148,148,144,148,144,140,148,148,144,140,148,148,144,84,144,86,86,86,142,146,150,84,84,84,140,144,84,88,88,88,92,92,88,88,92,92,88,88,92,92,92,92,100,100,100,100,92,92,100,100,100,92,92,92,92,92,102,102,102,102,90,90,90,90,100,100,100,100,90,90,90,90,90,90,98,98,98,98,88,2,2,2,28,2,6,6,6,6,2,2,2,8,8,8,12,12,12,12,8,8,8,8,10,10,6,4,2,30,30,30,40,20,20,20,32,12,12,12,24,10,22,8,2,2,2,28,38,166,166,166,156,156,156,156,178,178,178,178,168,168,168,168,164,164,160,160,154,154,154,154,148,148,144,146,152,152,164,164,164,164,154,154,164,164,164,164,156,156,156,156,176,176,176,176,184,184,184,184,176,176,176,176,184,184,184,184,176,176,176,176,186,186,186,186,176,176,176,176,178,178,178,178,170,170,170,170,162,162,162,162,158,160,156,156,156,156,160,160,162,162,160,160,158,158,158,162,158,158,158,158,162,162,158,1,2,172,172,172,122,122,122,122,174,130,144,144,144,144,126,126,126,126,130,126,146,142,142,142,126,126,126,126,126,126,142,142,142,142,128,150,150,150,174,152,152,166,168,168,170,166,170,170,172,164,154,158,158,158,150,156,156,156,154,154,152,152,154,154,150,150,148,148,146,146,146,146,150,150,152,152,154,154,154,150,150,148,142,146,142,146,140,168,168,168,166,166,164,164,164,164,166,166,168,168,172,172,166,166,172,168,174,164,164,164,162,162,158,176,180,180,198,174,176,176,196,170,196,196,196,196,172,176,178,178,198,198,198,198,174,178,180,180,198,198,194,194,178,170,198,176,178,176,176,176,178,178,180,180,180,180,182,178,176,178,178,178,180,180,180,180,182,182,182,176,180,180,180,180,184,184,180,180,182,190,190,190,192,192,194,198,198,198,194,192,186,186,186,186,190,190,192,196,196,196,192,192,190,190,190,190,192,192,194,196,196,196,190,190,186,186,188,188,188,188,188,122,108,108,108,108,102,102,102,102,110,110,110,110,122,104,104,112,112,116,116,102,100,100,94,122,108,108,108,108,100,100,100,100,108,108,108,108,122,110,110,114,114,100,100,122,94,94,90,90,90,90,86,86,86,86,90,90,90,90,92,92,122,102,114,114,114,114,98,98,102,84,80,80,80,80,90,128,130,130,132,124,128,128,130,130,134,124,130,178,178,178,176,176,176,176,174,174,172,172,170,170,166,166,164,164,164,164,162,162,160,160,160,160,162,162,158,158,154,154,154,154,160,160,162,162,162,162,164,164,166,166,168,168,170,170,172,172,174,174,176,176,178,178,180,176,176,176,172,172,170,170,164,164,160,160,160,160,160,160,158,158,158,158,154,154,150,150,150,150,154,154,156,156,158,158,160,160,166,166,168,168,172,172,180,176,176,176,174,174,170,170,168,168,166,166,164,164,162,162,160,160,156,156,156,156,152,152,150,150,146,146,146,146,146,146,144,144,144,144,146,146,150,150,152,152,152,152,156,156,158,158,162,162,164,164,166,166,170,170,172,172,174,174,178,160,160,160,160,160,154,154,152,148,156,156,156,156,158,158,160,160,170,170,172,172,174,174,174,174,170,170,168,168,164,164,162,162,158,192,192,192,196,196,196,110,110,110,108,108,104,104,98,98,98,98,94,94,94,94,102,102,102,102,110,110,110,110,128,128,138,138,144,144,148,148,150,150,152,130,140,136,136,136,140,140,142,142,144,144,146,134,134,130,130,130,128,128,126,126,122,122,122,122,124,124,130,130,134,122,130,138,138,138,130,138,146,130,136,136,136,136,130,130,130,136,148,148,148,148,136,132,126,126,122,130,126,126,126,126,124,124,120,130,130,130,128,126,122,132,132,132,128,134,132,132,126,126,126,126,130,138,146,122,118,118,98,98,158,158,158,158,174,98,98,98,98,98,106,106,120,120,124,124,128,24,14,14,14,14,38,38,38,38,28,28,28,18,38,24,24,24,38,32,24,24,24,24,38,38,38,34,26,26,26,26,38,38,38,14,26,16,40,40,40,28,28,28,32,28,28,28,40,40,40,40,28,28,28,36,14,14,14,14,30,30,30,30,14,14,34,34,34,22,22,12,12,12,34,14,14,8,30,16,16,8,30,30,30,30,8,8,8,132,132,132,142,142,142,142,148,148,142,142,142,142,150,142,132,132,132,132,130,130,124,124,124,124,126,126,134,124,124,124,128,128,130,114,114,114,116,116,122,122,120,120,118,118,114,122,126,126,126,126,126,126,132,132,134,134,134,134,130,132,136,136,136,54,66,66,66,66,78,78,78,78,90,70,88,88,88,88,78,78,76,76,70,70,70,70,84,72,72,72,76,76,82,86,72,72,72,72,84,72,72,72,76,76,80,72,76,76,90,84,54,54,54,54,68,68,68,68,54,54,54,54,82,66,80,80,80,80,66,66,66,66,78,78,78,78,66,66,66,66,78,66,64,64,64,64,76,50,76,76,76,62,62,62,62,62,74,74,74,74,60,60,60,60,74,52,48,60,60,60,74,60,58,58,58,58,72,72,72,188,188,188,190,190,196,196,190,190,190,190,188,188,188,188,186,186,186,186,188,188,196,188,184,184,182,190,190,190,192,192,196,196,198,184,188,188,190,190,192,192,194,178,184,176,184,180,176,176,178,180,184,184,180,180,178,188,178,194,194,194,198,198,198,198,190,190,190,198,198,198,190,190,190,190,198,194,194,190,190,190,190,190,194,194,194,194,190,190,190,190,198,198,198,198,192,192,190,190,190,190,186,186,182,182,182,182,186,186,190,190,190,190,186,186,182,182,182,182,184,184,194,182,182,182,186,186,190,192,192,192,198,186,186,180,186,194,198,160,146,146,138,138,102,102,102,102,142,142,146,146,160,160,160,104,82,82,82,82,122,122,138,84,72,72,54,54,54,54,56,56,58,54,54,54,108,108,120,64,44,44,42,42,42,42,92,92,100,76,76,76,114,114,130,130,146,146,148,148,148,108,108,108,150,150,150,150,118,148,162,162,162,162,148,78,66,66,54,54,50,50,50,50,116,116,122,108,114,114,108,108,112,112,104,108,114,120,126,126,118,118,126,126,120,118,120,126,118,130,136,136,128,128,136,136,128,142,136,136,142,142,136,136,144,150,156,156,156,156,148,148,148,60,56,56,56,56,60,60,60,60,56,60,60,60,64,64,64,64,60,60,60,66,70,70,70,70,66,66,66,72,76,76,76,76,70,70,70,76,76,76,82,82,82,82,76,78,84,84,84,84,76,88,88,88,94,94,94,94,86,88,88,88,94,94,94,94,86,86,102,102,86,86,86,96,102,102,102,102,94,94,94,64,72,72,72,72,60,76,84,84,84,82,76,76,76,92,82,82,100,100,84,84,118,118,128,86,94,94,94,98,114,114,114,114,100,116,120,126,126,126,114,114,114,114,114,132,132,126,126,122,122,122,134,48,52,56,60,60,66,66,72,72,78,74,80,62,68,56,62,52,44,44,50,50,56,56,62,62,68,68,74,76,80,80,72,68,64,58,52,52,60,66,62,46,56,48,42,46,52,54,58,58,64,68,74,74,80,80,84,78,82,82,82,82,78,78,78,84,90,90,90,90,84,84,84,92,98,98,98,98,90,90,90,100,106,106,106,106,98,98,98,102,106,106,106,106,98,102,102,110,110,110,116,116,116,116,108,118,124,124,124,124,116,116,116,136,136,136,128,128,128,128,136,140,140,140,146,146,146,146,138,128,160,160,160,160,126,126,126,152,160,160,160,160,152,152,152,148,158,158,158,158,146,150,150,118,124,128,134,140,146,146,142,138,132,128,122,116,110,110,116,122,128,134,140,144,150,150,146,142,136,132,126,120,116,58,54,54,54,54,58,58,58,58,60,60,60,70,64,64,64,64,72,72,72,72,74,74,74,64,64,64,68,68,68,68,74,70,74,74,74,74,68,68,68,68,68,74,70,70,70,76,82,82,82,82,74,74,74,74,76,78,78,78,84,84,84,86,86,86,92,92,92,90,90,90,90,90,84,84,84,86,92,92,92,96,96,96,102,102,102,102,98,98,98,98,104,104,104,104,102,92,96,96,96,96,92,92,92,102,110,110,110,106,106,106,108,108,114,50,44,54,56,56,56,42,32,52,44,44,38,74,60,60,60,166,168,168,198,198,198,198,198,198,168,24,50,50,50,50,36,40,52,52,52,52,48,48,34,34,34,34,20,20,20,34,24,24,24,24,32,32,32,20,52,52,52,52,48,48,48,48,40,40,40,40,34,34,34,34,22,22,22,22,16,16,16,16,20,26,26,26,34,34,44,44,44,44,48,48,48,48,44,44,44,38,36,36,36,36,32,32,26,26,26,18,34,34,34,34,28,28,28,28,32,32,32,32,12,12,12,12,28,28,28,28,14,14,14,14,18,16,16,20,16,16,16,16,20,20,20,20,38,38,38,38,18,18,18,40,26,26,18,18,18,18,20,20,26,26,40,40,40,40,36,36,36,36,42,42,42,42,40,34,26,26,26,26,34,34,34,18,38,38,44,44,44,44,18,18,18,18,40,40,40,40,18,18,18,18,44,44,44,44,36,36,36,34,30,30,30,30,20,20,20,20,16,16,16,114,114,114,124,124,132,132,132,132,134,134,146,146,154,154,154,154,150,136,162,162,168,168,168,168,164,144,144,174,174,174,178,178,188,188,190,190,190,190,182,182,180,180,180,180,192,184,186,186,192,192,192,192,182,182,178,178,172,172,172,172,184,184,142,142,170,170,182,182,182,182,176,176,172,158,158,94,100,118,120,120,124,124,124,124,96,96,86,86,86,86,82,82,70,70,58,58,58,48,48,48,72,72,72,72,98,98,118,118,142,142,142,142,112,112,110,110,96,96,96,96,118,118,118,118,112,112,102,102,86,86,86,86,84,84,52,52,42,42,42,42,34,34,18,18,10,10,10,10,10,10,12,12,22,22,22,22,10,10,10,10,42,42,42,42,52,52,52,52,52,52,98,98,98,98,80,80,76,76,76,76,70,70,42,42,42,42,22,22,12,12,12,12,18,18,28,28,82,82,94,94,120,120,120,120,128,128,134,134,142,142,146,146,146,146,132,132,126,126,114,114,110,110,98,72,62,62,62,62,60,60,58,58,44,44,44,44,46,46,50,50,56,56,56,56,58,58,82,70,64,64,40,40,38,38,38,38,56,56,70,70,78,78,90,92,92,92,86,86,78,78,74,74,74,74,68,68,54,54,36,36,34,34,22,22,8,8,2,2,2,2,4,4,6,6,16,16,16,16,14,14,8,8,4,4,4,4,14,14,14,14,46,46,46,46,44,44,18,18,14,14,14,14,20,20,28,28,40,40,42,42,54,54,60,60,70,70,76,76,92,92,94,94,96,96,96,96,82,82,82,82,128,174,174,174,186,186,190,190,190,190,186,186,180,180,176,176,176,168,150,148,150,152,188,188,188,188,164,164,164,164,172,172,172,172,164,164,158,158,148,148,148,148,154,154,156,156,158,158,158,158,146,146,146,146,162,162,162,162,144,144,144,144,172,172,172,172,160,160,160,160,140,140,144,144,166,150,150,150,180,180,180,180,186,186,186,186,174,174,174,174,192,192,192,192,180,180,180,180,170,170,158,158,136,136,136,136,188,180,176,176,162,162,158,158,148,142,140,140,132,132,124,124,124,124,116,116,112,112,108,108,106,106,96,96,96,96,126,148,152,152,162,162,162,162,150,132,132,132,132,132,144,144,154,154,154,154,144,144,142,142,132,132,116,116,116,116,140,140,140,140,138,138,124,124,82,82,66,66,66,66,80,80,70,70,68,68,68,68,80,80,80,80,80,80,66,66,58,58,58,58,60,60,64,64,68,68,68,68,68,68,66,54,42,42,42,42,40,40,36,36,22,22,22,22,38,38,38,38,52,52,62,62,62,62,100,100,106,106,118,118,124,124,144,112,106,106,104,104,104,104,110,112,112,112,98,98,88,88,84,84,84,84,80,80,76,76,76,76,78,78,82,82,82,82,78,78,72,72,56,56,56,56,46,46,76,76,84,96,124,88,66,90,124,124,126,126,126,126,70,70,70,106,106,106,82,80,80,80,94,94,94,94,108,108,124,36,44,44,76,76,82,82,88,88,90,90,108,108,118,118,170,170,198,198,198,198,148,148,142,142,140,140,134,134,132,132,130,130,118,118,96,96,78,78,38,38,30,30,2,14,14,14,18,18,20,20,30,30,46,46,48,48,52,52,52,52,54,54,68,68,70,70,78,78,96,96,106,106,110,110,116,116,120,120,134,134,136,136,150,162,162,172,178,178,180,186,186,186,188,188,198,198,198,198,198,198,192,192,190,190,180,180,176,176,172,172,170,170,168,168,166,166,164,164,162,162,160,160,158,158,156,156,154,154,150,150,148,148,144,144,142,142,138,138,136,136,132,132,130,130,128,128,128,128,124,124,124,124,122,122,120,120,118,118,116,116,114,114,110,108,104,104,102,102,100,100,98,98,96,96,94,94,90,90,88,82,80,80,80,80,76,76,74,74,72,72,70,70,68,68,66,66,64,64,62,62,60,60,58,58,56,56,54,54,52,52,50,50,48,48,44,44,42,42,40,38,36,36,34,34,28,28,26,26,16,16,14,14,12,10,6,6,4,4,2,4,26,26,30,30,34,34,34,34,38,38,64,66,72,72,74,74,108,118,118,118,132,132,144,148,152,162,166,166,168,168,168,168,174,174,178,178,180,180,180,180,190,190,190,190,194,194,166,166,164,164,158,158,154,154,146,146,142,142,140,140,138,138,138,138,136,136,134,134,130,130,128,126,122,122,120,120,118,118,116,116,112,112,110,110,108,108,106,106,100,100,98,98,96,96,94,94,92,92,90,90,90,90,78,54,48,48,48,48,46,46,44,44,42,42,40,40,38,38,36,36,34,34,32,32,26,20,16,16,12,12,8,8,4,4,2,2,2,2,2,64,66,66,70,70,70,70,106,106,106,106,108,108,108,108,110,106,114,114,114,114,128,128,132,132,132,132,128,128,128,128,118,118,118,118,104,104,102,102,88,66,64,64,58,58,20,20,20,20,24,24,28,28,30,30,30,30,28,28,26,26,26,26,20,20,64,64,64,64,70,70,70,70,74,74,74,74,72,72,66,66,66,66,62,62,62,62,68,68,68,72,72,72,88,88,88,88,70,84,92,92,92,92,86,64,64,64,60,60,28,28,28,28,26,26,26,26,22,22,22,22,24,24,28,28,32,32,34,34,44,44,44,44,48,48,42,48,50,50,50,50,46,46,46,48,70,70,76,76,78,78,82,82,90,90,92,92,112,112,112,112,120,120,120,132,112,112,112,112,108,108,108,108,130,134,116,116,108,108,108,108,116,116,116,116,120,128,144,144,144,144,148,148,148,148,144,148,148,148,144,148,148,148,144,148,148,148,144,144,140,140,136,136,124,130,136,136,136,136,140,140,146,146,146,146,142,148,148,148,144,148,148,148,142,148,148,148,144,144,138,138,138,138,134,134,134,134,130,40,40,40,44,44,44,44,40,40,48,38,38,38,42,42,42,42,36,36,36,38,38,38,46,54,54,54,50,50,50,50,54,54,54,54,58,58,58,58,56,56,54,54,52,56,58,58,58,58,56,28,24,32,28,82,90,90,92,92,104,104,104,104,106,106,110,110,110,110,106,106,106,106,98,98,98,98,94,94,94,94,90,90,90,90,86,86,86,102,102,32,28,28,32,32,28,38,38,38,42,56,56,50,50,52,52,48,48,126,128,132,128,62,96,96,96,96,82,62,62,62,76,76,66,66,66,66,62,62,74,74,86,86,90,90,96,62,94,94,94,94,82,82,82,82,78,78,78,78,74,74,74,74,70,70,70,70,64,64,64,64,96,96,96,96,84,84,84,84,80,80,80,80,76,76,76,76,72,72,72,72,64,64,64,64,96,96,96,96,78,78,78,78,64,64,64,72,72,106,106,106,124,124,124,124,104,104,104,104,112,112,112,112,122,122,122,122,110,110,110,106,104,104,104,136,184,184,184,184,168,168,168,168,172,172,172,172,178,178,178,178,182,182,182,182,170,170,170,170,166,166,166,166,162,162,162,136,136,136,160,160,160,146,146,136,184,184,184,184,168,168,168,168,162,162,162,162,156,156,156,156,152,152,152,152,132,132,132,132,184,184,184,184,176,184,186,186,186,186,130,130,130,130,186,186,186,186,166,166,166,166,130,130,130,144,144,112,112,112,118,118,118,118,112,112,112,112,116,116,116,116,110,42,48,42,46,2,2,2,198,198,198,198,194,194,2,2,2,2,2,2,198,198,198,198,194,194,2,2,2,84,2,104,110,110,122,122,198,198,198,198,132,132,106,98,98,94,94,2,2,6,6,6,10,10,10,14,14,14,18,18,18,22,22,22,26,26,26,132,132,132,56,56,56,56,104,104,104,104,110,110,122,122,126,126,138,42,42,42,46,46,54,54,70,70,70,70,74,74,82,82,90,90,96,96,96,96,100,100,104,104,112,112,112,112,114,114,122,122,136,136,138,138,152,152,156,156,168,168,170,170,176,176,176,176,186,186,198,198,198,198,186,186,180,180,180,180,196,196,196,196,180,180,198,198,180,180,180,180,198,198,180,180,180,180,198,198,198,198,178,178,192,192,194,192,178,178,156,156,134,134,132,132,106,106,80,80,56,56,54,54,30,30,28,28,2,2,2,2,26,26,26,26,2,2,2,2,28,28,2,2,2,2,28,28,10,10,8,10,34,34,34,34,70,70,70,70,42,42,68,68,72,72,96,96,96,96,66,102,114,114,116,116,116,116,122,122,138,138,140,140,140,140,154,154,158,158,174,174,174,174,150,150,122,122,122,122,154,154,156,156,190,190,140,140,138,138,92,92,92,92,168,168,168,168,136,136,134,134,48,48,46,46,24,24,24,24,62,62,68,68,104,104,104,104,28,28,26,26,2,2,2,2,96,96,102,102,172,172,172,172,88,88,86,86,22,22,22,22,96,96,98,98,170,170,154,122,110,110,96,76,76,76,120,120,162,192,192,192,184,184,184,184,176,176,176,176,176,176,184,184,184,184,198,198,198,142,142,142,130,130,130,130,144,128,88,88,88,88,132,132,86,86,86,86,92,92,132,132,86,86,86,86,132,132,88,88,88,88,132,84,84,84,78,78,78,78,86,108,108,108,116,116,116,116,108,94,94,94,100,100,100,100,92,80,72,72,72,72,80,80,72,72,72,72,80,80,60,60,60,60,82,78,72,72,72,72,80,80,74,74,74,74,80,124,124,124,118,118,118,118,124,40,22,40,68,68,90,90,90,90,116,116,116,116,88,88,88,88,100,96,66,66,66,66,20,20,20,20,30,30,30,30,46,46,74,74,18,18,18,18,72,72,72,72,86,86,86,94,118,118,118,22,22,22,26,26,26,26,32,32,32,32,42,42,42,42,60,60,60,60,64,64,64,64,68,68,68,68,74,74,74,74,78,78,80,80,80,80,84,84,84,84,86,86,102,102,102,102,112,112,114,114,116,116,118,118,124,124,130,130,130,130,134,134,138,138,140,140,140,140,144,144,150,150,154,154,158,158,158,158,162,162,162,162,170,170,170,170,174,174,174,174,178,178,178,178,182,182,182,182,186,186,186,186,182,182,182,182,178,178,178,178,174,174,174,174,170,170,164,164,156,156,152,152,144,144,140,140,140,140,136,136,130,130,128,128,120,120,120,120,106,106,92,92,92,92,72,72,70,70,60,60,56,56,48,48,44,44,38,22,26,26,26,26,38,22,26,26,26,26,28,28,36,36,36,36,44,44,48,48,52,52,52,52,56,56,56,56,60,60,60,60,72,72,72,72,90,90,90,90,98,98,98,98,102,102,102,102,104,104,108,108,108,108,116,116,116,116,120,120,120,120,122,122,122,122,130,130,132,132,132,132,136,136,138,138,150,150,150,150,154,154,154,154,158,158,158,158,162,162,162,162,158,158,158,36,40,40,40,40,46,46,46,46,50,50,50,50,54,54,54,54,62,62,62,62,70,70,70,70,74,74,74,74,84,84,84,84,96,96,96,96,100,100,100,100,104,104,104,104,108,108,108,108,112,112,112,112,114,114,114,114,116,116,118,118,120,120,120,120,122,122,124,124,130,130,130,130,132,132,132,132,134,134,136,136,136,136,140,140,140,140,144,144,144,144,138,138,134,134,128,128,126,126,122,122,120,120,120,120,108,108,108,108,104,104,104,104,78,78,74,74,74,74,64,64,64,64,60,60,60,60,56,56,56,56,52,52,52,52,50,50,46,46,44,44,44,44,40,40,40,64,68,68,68,68,64,64,64,60,64,64,64,68,72,72,72,72,68,68,68,74,78,78,78,78,74,74,74,84,88,88,88,88,84,84,84,80,80,80,76,76,76,78,80,80,84,84,84,84,80,80,80,76,80,80,80,80,76,76,76,88,92,92,92,92,88,88,88,84,88,88,88,88,84,84,84,92,96,96,96,96,92,92,92,88,92,92,92,92,88,88,88,84,84,84,80,80,80,80,84,96,100,100,100,100,96,96,96,104,108,108,108,108,104,104,104,100,100,100,96,96,96,96,100,106,106,106,110,110,110,110,106,106,102,102,102,102,106,106,106,110,110,110,114,114,114,114,110,110,114,114,114,114,110,110,110,116,116,116,120,120,120,120,116,122,122,122,126,126,126,126,122,122,126,126,126,126,122,122,122,118,118,118,114,114,114,114,118,118,122,122,122,122,118,118,118,124,124,124,128,128,128,128,124,128,132,132,132,132,128,128,128,132,132,132,134,134,134,134,130,130,130,134,138,138,138,136,132,136,136,136,140,140,140,140,136,22,22,22,18,18,18,18,26,26,26,30,30,30,26,22,46,46,46,46,38,32,34,34,42,42,44,44,48,48,48,48,22,22,22,22,40,24,24,24,36,26,26,26,22,22,22,22,26,26,26,26,44,44,44,44,48,48,48,48,44,44,44,44,24,28,44,44,44,44,26,26,26,22,42,42,42,42,46,46,46,46,42,42,42,42,22,22,22,22,42,42,42,42,22,22,22,22,46,46,46,46,30,30,30,30,28,28,28,28,32,32,32,32,24,24,24,24,20,20,20,20,28,28,28,28,22,22,22,32,44,44,44,44,32,32,32,52,52,52,56,56,56,56,52,60,60,60,64,64,64,64,60,66,66,66,70,70,70,70,66,74,122,122,76,76,72,72,72,80,80,80,110,12,12,12,24,24,34,34,40,40,40,40,32,32,14,14,12,28,28,14,38,38,38,12,36,36,36,16,34,20,20,20,34,26,26,18,32,32,32,32,14,14,14,14,28,12,12,12,22,22,22,22,34,34,34,14,34,34,34,22,22,12,12,50,68,50,48,48,48,48,50,50,54,54,54,54,46,46,42,42,62,62,62,62,42,42,42,42,58,58,58,42,64,42,42,42,44,44,54,54,54,54,42,42,42,42,60,42,62,62,62,62,38,38,38,38,62,36,36,36,46,46,46,46,36,36,36,36,60,60,60,60,36,36,36,46,46,34,58,34,32,32,32,32,42,42,42,42,56,56,56,56,34,34,56,34,28,28,28,28,28,28,38,38,38,38,52,52,52,72,82,82,82,82,92,92,92,92,84,84,84,84,94,94,94,94,84,84,84,84,70,70,90,90,90,90,90,72,72,72,88,88,88,72,88,88,88,78,92,78,72,72,72,72,80,80,80,80,84,84,84,84,92,92,92,96,96,96,94,94,72,72,72,82,82,82,82,72,94,94,94,94,94,94,68,68,68,68,94,66,66,66,80,80,80,80,92,92,92,92,66,66,66,66,66,106,122,106,104,104,104,104,104,122,122,122,122,124,106,106,106,106,116,116,116,116,122,122,122,122,102,102,122,108,108,108,124,104,104,122,122,122,122,106,106,104,104,104,108,112,112,104,124,104,102,102,102,102,112,112,112,112,104,104,102,102,102,102,118,122,122,122,100,100,100,112,112,132,146,146,146,150,150,150,150,150,132,132,132,132,132,132,150,138,138,138,136,136,130,138,144,144,144,144,152,132,150,150,150,150,138,140,144,144,144,138,138,132,130,130,128,128,128,128,152,130,130,130,130,130,130,130,150,150,150,140,140,130,130,130,144,144,144,144,150,144,140,140,140,140,128,140,140,140,150,128,148,128,128,128,140,140,140,140,148,148,148,128,128,128,140,140,140,140,150,150,150,160,180,180,180,180,180,180,180,160,156,156,156,156,156,156,178,158,158,158,168,168,168,168,176,176,176,176,152,152,170,152,148,148,148,148,148,148,166,158,158,158,174,164,164,164,180,180,180,172,172,172,176,164,164,170,172,158,166,166,166,166,180,170,170,170,156,156,156,156,156,156,172,172,172,172,178,154,176,176,176,176,176,176,152,152,152,152,152,152,176,152,152,152,166,166,166,166,176,176,176,176,152,120,136,136,136,136,92,90,90,90,90,90,80,80,68,68,46,46,46,46,42,42,32,32,28,28,28,28,40,40,44,44,44,44,60,60,74,74,90,90,90,90,90,46,36,36,36,36,26,26,26,26,16,16,16,16,26,26,26,26,30,30,34,34,34,34,36,36,40,40,52,52,52,52,64,64,64,64,72,72,72,72,68,72,78,78,78,78,68,66,74,74,74,74,70,70,56,56,56,56,56,56,54,44,42,42,42,102,128,128,128,128,98,98,98,98,118,118,118,118,98,98,98,98,126,134,126,132,132,132,98,98,98,98,100,100,112,112,112,112,96,96,96,96,128,128,128,128,124,124,112,112,130,130,130,130,100,98,98,98,126,126,126,126,104,104,104,104,134,134,134,134,128,114,114,114,120,120,120,120,112,112,92,92,92,92,108,92,90,104,94,94,98,98,106,106,92,92,92,92,124,124,124,124,116,116,116,116,130,130,130,130,124,18,18,50,60,60,72,72,122,122,122,122,72,72,72,72,60,60,50,50,42,42,40,40,48,48,48,48,52,100,100,112,112,110,100,100,104,106,106,96,100,100,106,106,98,106,110,110,110,110,102,108,98,98,98,108,108,108,94,102,102,102,94,106,108,108,114,138,138,138,158,158,164,164,164,164,174,174,176,176,196,196,196,196,138,138,138,138,150,150,152,152,168,168,168,196,188,188,172,172,158,158,150,150,128,148,156,156,174,174,184,184,196,164,164,164,174,174,174,142,142,148,152,148,144,148,148,148,138,138,146,146,156,156,156,156,144,144,142,142,142,132,136,136,136,136,192,192,192,192,134,134,134,134,132,132,126,126,114,114,114,114,118,118,130,130,132,132,132,132,138,158,190,142,138,136,136,136,144,144,146,146,146,146,138,138,132,158,158,158,190,190,190,60,56,56,32,32,32,32,36,36,38,38,46,46,46,46,42,42,46,46,46,46,44,44,44,44,48,48,52,52,52,52,52,52,54,54,54,54,58,58,60,60,60,60,60,60,66,66,66,66,64,64,58,60,60,60,58,58,58,58,56,56,54,54,48,48,48,48,50,50,50,50,48,48,44,44,44,46,46,46,44,44,44,44,48,44,34,34,34,34,36,36,36,36,38,40,40,40,42,42,42,42,44,48,54,54,54,54,58,58,64,64,64,64,54,54,64,64,64,64,62,62,58,58,50,60,102,102,102,102,56,90,90,90,90,90,90,90,90,96,96,96,96,96,96,96,96,88,88,82,82,82,82,82,82,82,82,2,2,2,16,16,16,2,16,8,8,8,16,16,2,2,2,2,6,6,6,6,10,10,10,10,20,20,20,12,16,16,26,10,18,2,2,2,10,10,10,10,2,2,2,2,2,2,16,16,16,16,18,18,24,24,34,34,34,198,182,182,180,180,172,172,170,170,164,164,156,156,144,144,140,140,124,124,120,120,110,110,104,104,92,92,90,90,76,76,72,72,62,62,60,60,60,60,50,50,48,48,38,38,34,34,28,28,22,22,14,14,10,10,2,2,2,198,184,184,184,184,172,172,168,168,158,158,156,156,156,156,148,148,138,138,128,128,120,120,106,106,98,98,80,80,72,72,56,56,50,50,36,36,30,30,14,14,10,10,2,2,2,2,2,198,184,184,180,180,166,166,166,166,160,160,144,144,136,136,128,128,120,120,116,116,104,104,92,92,86,86,84,84,72,72,68,68,62,62,58,58,46,46,26,26,24,24,24,24,8,8,2,2,22,22,22,22,14,14,10,10,8,8,8,8,14,14,44,44,52,52,64,64,72,72,86,86,88,88,90,90,94,94,106,106,114,114,130,130,134,134,144,144,152,152,160,160,168,168,198,198,180,180,172,172,160,160,154,154,142,142,136,136,124,124,118,118,112,112,110,110,108,108,100,100,84,84,74,74,42,42,38,38,20,20,14,14,14,14,18,18,32,32,32,32,18,18,16,16,16,16,24,24,28,28,34,34,36,36,50,50,60,60,68,68,74,74,80,80,82,82,90,90,108,108,116,116,124,124,132,132,136,136,146,146,152,152,154,154,156,156,166,166,176,176,190,190,198,24,24,24,20,20,20,20,30,30,30,30,36,36,36,36,40,40,40,40,32,32,32,32,24,24,24,38,34,34,34,34,28,28,28,28,24,24,22,22,22,22,16,16,16,16,24,24,24,28,40,40,40,40,38,34,32,70,70,70,66,66,60,60,56,56,52,52,52,52,62,62,62,62,64,64,70,70,74,74,66,74,76,76,76,76,68,62,62,62,66,66,76,76,82,82,82,82,56,56,54,54,54,54,58,58,62,66,74,66,56,56,56,56,56,56,62,62,66,66,66,198,194,194,176,176,166,166,158,158,128,128,124,124,120,120,108,108,94,94,90,90,90,90,100,100,100,100,94,94,88,88,86,86,86,86,94,94,94,94,86,86,86,86,94,94,98,98,104,104,108,108,118,118,122,122,130,130,146,146,152,152,166,166,174,174,198,198,190,190,190,190,190,190,186,186,174,174,170,170,154,154,146,146,142,142,134,134,130,130,116,116,116,116,112,112,102,102,102,102,108,108,108,108,102,102,100,100,100,100,104,104,104,104,102,102,102,102,124,124,130,130,132,132,146,146,156,156,170,170,178,178,194,194,198,198,198,198,184,184,178,178,170,170,164,164,162,162,154,154,148,148,144,142,140,140,132,132,118,118,110,110,110,110,126,126,142,142,152,152,154,154,154,154,158,158,168,168,172,172,180,180,184,184,194,194,198,174,174,174,160,160,158,158,158,158,148,148,142,142,126,126,124,124,124,124,150,150,162,162,170,170,172,172,172,172,174,198,194,194,178,178,178,178,168,168,168,168,170,174,184,184,194,194,198,198,184,184,182,182,182,182,192,192,192,192,198,198,198,198,194,194,194,194,190,190,190,190,184,184,184,198,198,198,198,198,198,64,58,58,36,36,28,28,22,22,22,22,28,28,36,36,36,36,20,20,20,20,32,32,34,34,60,36,46,46,36,36,36,36,36,36,40,40,40,36,48,26,38,38,24,28,28,32,32,2,198,198,2,2,180,168,2,2,160,156,2,2,142,118,2,106,2,102,2,2,76,60,54,60,72,54,2,2,62,34,2,2,28,22,2,2,12,2,14,14,14,14,2,2,12,2,30,30,22,30,34,22,2,2,52,54,2,54,72,76,38,22,2,2,20,44,98,102,46,16,2,16,22,22,2,52,120,124,54,24,2,2,34,64,124,136,124,138,58,26,2,2,22,58,140,142,140,144,52,18,2,2,16,50,156,144,148,168,44,12,2,12,18,18,2,18,20,44,182,198,36,20,2,20,24,2,30,38,198,198,2,22,12,12,22,30,30,30,38,38,38,38,20,28,28,28,38,38,38,38,32,32,32,38,38,38,28,28,28,28,22,22,22,20,24,24,24,24,20,20,20,26,36,40,40,40,34,34,34,34,28,28,28,22,22,16,38,38,38,14,38,28,28,28,38,26,24,24,18,30,30,30,26,26,26,26,34,34,34,22,22,22,18,18,18,18,24,28,38,34,26,26,26,26,36,32,32,32,26,26,26,26,38,38,38,38,12,28,28,28,40,26,24,24,16,26,36,36,36,36,24,36,22,22,22,22,36,40,40,40,30,30,30,30,24,24,24,24,24,18,42,42,42,62,62,62,68,68,68,68,50,58,58,58,68,68,68,68,62,62,62,66,66,66,58,58,58,58,52,52,52,52,52,52,46,46,46,46,54,58,70,64,64,64,60,60,60,60,56,56,56,50,50,46,70,70,70,56,56,56,66,66,66,66,60,60,60,56,68,68,68,68,56,56,56,56,66,66,66,66,48,58,58,58,68,68,44,58,58,58,70,56,48,62,62,62,56,56,56,56,70,70,70,70,58,50,46,46,46,46,50,50,50,70,56,56,56,56,70,70,70,70,54,54,54,54,64,64,64,50,50,50,44,44,44,44,72,72,72,74,44,44,44,40,40,40,72,48,48,42,42,42,38,38,38,38,42,48,70,64,46,46,46,46,70,70,70,70,54,54,54,44,72,52,66,66,66,66,48,68,48,48,48,48,66,50,50,50,64,64,64,64,50,62,70,70,70,70,80,80,80,80,66,66,66,78,92,92,92,92,82,82,82,78,78,78,94,94,94,94,82,82,82,78,78,78,82,82,82,82,94,94,94,94,76,70,70,70,66,66,66,66,74,80,80,80,86,86,86,86,94,94,94,80,80,74,96,96,96,96,90,84,84,84,78,78,78,78,94,94,94,94,80,70,74,74,74,74,70,70,70,94,78,78,78,78,94,100,70,70,70,70,76,76,76,80,80,80,94,94,94,94,84,84,84,78,78,78,92,92,92,92,72,82,82,82,92,92,80,80,80,80,92,80,92,92,92,92,78,92,78,78,78,116,130,130,130,130,108,108,108,108,144,144,144,144,172,172,172,172,148,148,148,148,180,180,180,180,138,138,138,138,114,114,114,114,114,114,108,108,108,108,180,180,180,180,172,172,172,172,114,108,182,182,182,182,142,142,142,142,182,182,182,182,108,108,108,108,138,138,138,138,108,108,108,108,108,108,178,178,178,178,172,172,172,172,144,144,144,144,138,138,138,138,114,114,114,114,106,106,106,106,180,180,180,180,108,108,108,116,130,130,130,130,104,104,104,104,144,144,144,144,176,176,176,176,146,146,146,146,184,184,184,184,138,138,138,138,110,110,110,110,116,112,112,112,126,126,126,126,104,104,104,104,144,144,144,144,172,172,172,172,148,148,148,148,182,182,182,182,136,136,136,136,110,110,110,110,104,104,104,104,182,182,182,182,174,174,174,174,140,140,140,140,134,134,134,134,110,106,170,170,170,170,104,104,104,174,174,174,182,182,182,182,174,160,184,184,160,192,192,192,186,186,186,186,198,198,186,186,186,192,192,186,198,198,198,186,198,198,198,198,186,186,186,186,198,198,198,198,190,190,190,184,198,198,198,198,190,190,190,190,184,184,184,184,184,184,198,198,198,190,190,184,196,186,196,196,196,36,68,68,110,110,110,110,72,72,36,36,36,36,54,54,66,66,66,66,50,50,20,20,16,16,16,16,22,22,64,64,66,66,66,66,30,30,30,30,56,56,56,56,34,34,34,34,52,52,52,52,36,36,34,34,34,34,36,36,148,148,148,148,136,136,132,132,130,130,130,130,136,136,174,174,182,182,182,182,190,190,192,192,198,198,198,198,194,194,122,122,116,116,116,116,120,120,146,146,170,170,170,170,164,164,160,160,154,154,128,128,118,118,62,62,62,62,66,66,118,118,118,118,108,108,96,96,96,96,108,108,108,108,112,112,116,116,118,118,118,118,110,110,110,110,104,104,66,66,64,64,64,64,86,86,104,104,110,110,176,176,176,176,174,174,164,164,118,118,112,112,112,112,104,104,104,104,128,128,128,128,162,162,162,162,178,178,178,178,186,186,186,186,194,194,194,194,176,176,176,176,186,186,186,186,152,152,152,152,122,122,122,122,84,84,84,84,84,84,118,118,118,118,80,80,80,80,20,20,20,20,32,32,32,32,32,32,12,12,12,12,86,86,86,86,66,66,66,66,54,54,54,54,26,26,26,26,44,44,44,44,34,34,34,34,40,40,40,40,28,28,28,28,18,18,18,18,8,8,8,8,20,20,20,20,18,18,2,2,2,2,6,6,6,6,10,10,10,10,4,4,4,4,6,6,6,6,8,8,8,8,2,2,2,2,10,10,10,10,2,2,2,2,10,10,10,10,2,2,2,2,10,10,10,10,2,2,2,2,4,4,28,28,28,28,88,88,88,88,80,14,14,14,24,24,44,44,56,56,56,56,48,48,46,46,46,46,54,54,54,54,52,52,48,48,44,44,28,28,24,24,24,24,22,22,14,14,12,12,12,50,40,40,28,28,16,16,12,12,12,12,16,16,36,36,46,46,50,50,54,54,54,54,44,44,40,40,40,40,54,54,54,54,52,52,50,22,22,22,24,24,36,36,36,36,32,32,24,24,24,16,16,16,26,26,32,32,38,38,38,38,40,40,54,54,56,56,56,56,18,18,18,18,14,24,32,32,32,32,28,28,22,22,22,18,46,46,56,56,56,56,50,50,18,18,18,18,48,48,48,48,42,42,28,28,24,24,16,16,14,14,14,14,18,48,48,48,50,50,52,52,58,58,58,58,56,56,52,52,48,48,50,36,56,56,56,56,54,54,28,28,48,48,12,12,12,12,32,32,32,30,22,22,16,16,16,16,36,36,36,20,46,46,46,46,38,38,38,38,30,30,30,30,28,28,26,26,22,22,22,22,26,26,18,18,16,16,16,16,14,14,8,8,8,8,22,18,18,18,24,24,24,24,34,34,34,34,22,22,22,22,18,70,74,74,78,78,78,78,80,80,82,82,86,86,96,96,96,96,94,94,88,88,88,88,88,88,86,86,80,80,80,80,84,84,86,86,88,88,86,86,84,84,84,84,88,88,92,92,84,92,92,92,100,100,102,102,102,102,106,106,110,110,104,104,100,100,100,100,102,100,92,92,98,98,98,98,90,90,90,90,94,94,94,94,96,96,102,102,102,102,100,100,100,100,96,96,96,88,88,88,108,108,126,126,138,138,138,138,124,124,108,108,88,124,108,108,108,126,134,134,138,138,128,128,144,144,144,144,134,134,128,126,124,140,154,154,166,166,166,166,152,152,136,134,142,142,166,166,166,166,162,148,148,148,156,156,150,150,146,146,146,146,152,152,154,154,154,38,40,40,44,44,50,50,50,50,56,56,56,56,36,44,48,48,48,48,44,44,44,106,106,106,110,110,110,110,114,114,122,114,110,110,110,110,114,114,114,112,104,104,102,102,102,108,108,108,110,120,114,114,110,110,110,110,126,126,126,126,122,122,122,122,130,130,130,130,126,126,118,118,116,116,116,116,120,120,126,114,106,106,106,106,110,110,110,110,110,112,114,118,118,118,114,114,114,114,110,110,110,110,116,122,122,122,128,128,130,130,130,130,124,124,124,124,132,132,132,132,134,128,136,136,136,136,130,130,130,132,140,158,154,154,142,142,142,142,162,162,176,176,192,192,192,192,172,172,168,168,168,168,182,182,174,174,168,168,150,150,150,150,166,166,166,166,164,164,160,160,156,160,160,160,166,166,168,190,190,190,184,184,174,174,172,172,172,172,178,178,182,182,184,184,184,184,178,178,170,170,168,168,168,168,178,178,178,178,172,172,170,170,168,168,168,168,180,180,180,180,174,174,172,174,182,182,186,186,186,186,184,184,178,178,174,158,158,158,124,124,124,124,124,124,98,98,98,98,132,132,136,136,160,122,108,108,96,96,96,96,100,100,106,106,110,96,96,96,118,118,122,122,126,128,138,138,138,138,142,138,142,142,142,122,126,192,198,198,198,174,184,184,190,198,198,198,192,192,190,190,186,186,186,186,188,188,190,190,198,198,194,194,198,194,184,184,184,184,188,188,192,192,196,196,196,196,190,190,188,188,198,198,198,198,188,188,188,188,196,196,196,196,188,188,188,188,198,198,198,198,188,188,188,188,198,198,198,198,192,192,180,180,176,176,176,172,180,180,186,186,186,186,194,194,194,194,182,182,182,182,190,190,190,190,188,132,132,132,144,130,140,140,140,140,144,144,148,54,56,56,94,94,104,104,118,118,118,118,108,108,106,108,116,18,18,18,18,18,36,36,42,42,42,42,48,48,64,64,70,70,70,70,64,64,60,60,60,60,58,58,56,56,42,42,30,30,16,16,16,16,28,28,32,32,36,36,36,36,40,40,52,52,56,56,56,56,50,50,42,42,32,32,32,32,32,32,38,38,44,44,60,60,60,60,52,52,38,38,38,38,24,24,22,22,22,22,24,24,50,50,52,52,52,52,56,56,64,70,78,78,80,80,80,80,104,104,122,122,122,122,118,118,116,116,116,116,112,112,108,108,108,108,102,102,90,90,88,88,88,88,90,90,92,92,98,98,98,98,94,90,90,90,92,92,98,98,116,116,116,116,108,108,108,108,104,104,96,96,94,94,82,82,82,82,68,68,66,66,66,66,76,76,76,76,72,72,72,72,76,76,82,82,84,84,86,86,88,88,88,88,82,82,78,78,76,76,76,76,86,86,84,84,80,80,80,80,88,88,98,98,104,104,108,108,114,114,114,114,110,110,106,106,96,92,86,86,82,82,72,72,56,56,34,34,20,20,10,10,10,10,20,20,20,20,10,10,10,10,28,28,198,198,198,198,196,196,196,196,192,192,192,192,198,198,198,198,196,196,192,192,192,192,190,190,190,190,192,196,198,198,198,198,196,196,194,194,186,186,186,186,198,198,198,198,192,192,190,190,186,186,190,190,194,194,194,194,192,192,190,190,190,190,198,198,198,198,194,194,186,186,184,184,184,184,188,188,192,194,198,198,198,198,194,194,190,190,190,190,190,190,188,188,182,182,180,180,180,180,182,182,188,188,192,192,192,192,188,188,186,186,182,182,180,180,180,180,184,184,186,186,196,196,198,198,198,198,198,198,196,196,196,196,194,194,192,192,190,190,190,190,194,194,196,196,198,198,196,196,188,188,188,188,186,186,184,184,184,184,190,190,188,188,184,184,184,184,188,186,186,186,184,184,182,182,174,174,174,174,172,172,170,170,170,170,166,166,160,160,158,158,154,154,154,154,160,160,168,168,170,170,170,170,168,168,166,166,166,166,176,176,176,176,170,170,164,164,162,162,162,162,166,166,174,174,176,176,176,176,164,164,162,162,162,162,164,164,172,172,178,178,178,178,172,172,170,170,154,154,154,154,164,164,164,164,152,152,152,152,156,156,156,156,146,146,146,146,140,140,138,138,134,134,134,134,136,136,140,140,146,146,146,146,138,138,138,138,140,140,142,142,142,142,154,154,154,154,152,152,148,148,142,142,142,142,156,156,156,156,150,150,144,144,134,134,132,132,122,122,118,118,106,106,102,102,92,92,88,88,80,80,80,80,82,82,112,112,120,120,122,122,130,130,136,136,136,136,132,132,122,122,120,120,120,120,130,130,130,130,134,134,132,132,122,122,122,122,134,134,134,134,132,132,126,126,110,110,110,110,118,118,104,104,110,110,110,110,104,102,100,100,92,92,82,82,68,68,64,64,54,54,36,36,34,34,32,32,32,32,56,56,56,56,60,60,82,82,88,88,88,88,96,96,96,96,94,94,94,94,108,108,108,108,92,92,82,82,68,68,66,66,54,54,50,50,50,50,44,44,38,38,36,36,34,34,34,34,32,32,20,20,16,16,8,8,8,8,10,10,12,12,22,22,24,24,30,30,32,32,40,40,40,40,38,38,28,28,26,26,18,18,18,18,22,22,30,30,32,32,32,32,28,28,20,20,12,12,12,12,18,18,28,30,32,32,40,40,40,40,14,14,14,14,26,26,44,44,56,56,74,74,76,76,92,92,84,84,82,82,80,80,80,80,74,74,68,68,58,58,58,58,68,68,66,66,64,64,46,46,46,46,50,50,52,52,50,50,48,48,48,48,50,52,52,52,50,50,50,50,52,52,54,54,52,52,54,54,54,52,54,54,58,58,60,60,56,56,52,52,52,52,58,58,62,62,66,66,66,66,60,58,52,52,52,52,54,54,58,58,64,64,64,64,62,62,60,60,60,60,66,66,68,74,100,100,122,122,122,122,112,112,112,112,122,122,128,128,130,130,130,130,134,134,142,142,142,142,136,136,136,136,154,154,166,166,168,168,186,186,188,188,188,188,184,184,174,174,172,172,172,172,186,182,182,182,166,166,166,166,176,176,178,176,170,170,166,166,166,166,184,184,184,184,168,164,160,160,160,160,164,164,172,172,172,172,164,164,160,160,160,160,164,164,176,176,176,176,176,176,180,180,182,182,196,196,196,196,192,192,188,188,178,178,178,180,186,186,188,188,198,198,198,198,196,196,194,194,190,190,190,190,198,198,196,196,192,192,192,192,198,198,198,198,196,196,186,186,186,186,196,196,196,196,192,192,190,190,190,190,184,184,176,176,170,170,170,170,180,180,180,178,176,176,160,160,172,172,172,172,170,170,162,162,152,152,152,152,172,172,172,172,170,170,166,156,148,148,148,148,150,150,152,152,158,158,158,158,156,156,134,134,132,132,132,132,136,136,156,156,156,156,152,152,140,140,134,134,134,134,138,138,150,150,154,154,154,142,98,98,96,88,88,88,88,60,58,58,58,58,52,52,48,40,36,36,30,30,16,16,12,12,12,118,98,122,124,122,118,98,2,2,2,2,20,28,48,62,134,134,140,140,148,148,158,158,178,178,194,196,198,198,198,198,198,198,198,198,198,194,190,190,186,186,178,178,154,154,152,152,132,132,130,130,108,108,108,108,96,96,94,94,94,94,104,104,120,120,140,140,170,170,176,176,192,58,62,62,68,68,68,68,78,82,88,96,100,100,100,100,88,88,88,92,110,112,124,124,104,104,104,104,96,96,96,96,100,100,102,102,112,112,116,116,120,120,124,130,138,138,138,138,138,138,146,146,152,154,162,162,170,170,170,170,164,164,162,162,152,152,134,134,146,146,140,140,138,138,130,116,112,112,112,112,124,124,124,124,118,118,106,106,104,104,104,104,106,106,110,106,104,104,110,110,112,112,118,118,122,122,146,146,146,146,140,140,136,136,130,130,128,128,132,132,144,144,146,146,152,40]; +var cP = [0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0]; +var fN = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8,8,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,28,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,39,39,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,40,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,43,43,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,52,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,57,57,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,117,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,119,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,121,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,138,138,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,147,147,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,148,148,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,149,149,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,151,151,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,154,154,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,155,155,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,156,156,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,157,157,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,158,158,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,159,160,160,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,194,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,36]; diff --git a/extensions/fablabchemnitz/webp_import/meta.json b/extensions/fablabchemnitz/webp_import/meta.json new file mode 100644 index 0000000..1f2c012 --- /dev/null +++ b/extensions/fablabchemnitz/webp_import/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "WebP Import", + "id": "fablabchemnitz.de.webp_import", + "path": "webp_import", + "dependent_extensions": null, + "original_name": "WebP Import", + "original_id": "fablabchemnitz.de.webp_import", + "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/webp_import", + "fork_url": null, + "documentation_url": "https://stadtfabrikanten.org/display/IFM/WebP+Import", + "inkscape_gallery_url": "https://inkscape.org/de/~MarioVoigt/%E2%98%85webp-import", + "main_authors": [ + "inkscape.org/mono", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/webp_import/webp_import.inx b/extensions/fablabchemnitz/webp_import/webp_import.inx new file mode 100644 index 0000000..2f83bc2 --- /dev/null +++ b/extensions/fablabchemnitz/webp_import/webp_import.inx @@ -0,0 +1,14 @@ + + + WebP Import + fablabchemnitz.de.webp_import + + + diff --git a/extensions/fablabchemnitz/webp_import/webp_import.py b/extensions/fablabchemnitz/webp_import/webp_import.py new file mode 100644 index 0000000..a3be68d --- /dev/null +++ b/extensions/fablabchemnitz/webp_import/webp_import.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 + +import sys +import os +import argparse +import inkex +import shutil +from inkex import Rectangle +from PIL import Image +import base64 +from io import BytesIO, StringIO +import subprocess +from lxml import etree + +class WebpImport(inkex.InputExtension): + + def add_arguments(self, pars): + pars.add_argument('inputfile') + + def effect(self): + if os.name == 'nt': + tmp = os.getenv('TEMP') + '\\' + else: + tmp = '/tmp/' + convertfile = os.path.join(tmp, "webp.png") + if shutil.which('magick'): + command = "magick \"%s\" \"%s\" " % (self.options.inputfile, convertfile) + elif shutil.which('convert'): + command = "convert \"%s\" \"%s\" " % (self.options.inputfile, convertfile) + else: + inkex.errormsg('ImageMagick does not appear to be installed.') + exit() + p = subprocess.Popen(command, shell=True) + return_code = p.wait() + #inkex.utils.debug("command:" + command) + #inkex.utils.debug("Errorcode:" + str(return_code)) + + try: + img = Image.open(convertfile) + except Image.DecompressionBombError as e: #we could also increse PIL.Image.MAX_IMAGE_PIXELS = some large int + self.msg("Error. Image is too large. Reduce DPI and try again!") + exit(1) + output_buffer = BytesIO() + img.save(output_buffer, format='PNG') + width, height = img.size + byte_data = output_buffer.getvalue() + base64_str = base64.b64encode(byte_data).decode('UTF-8') + webp = etree.SubElement(Rectangle(), '{http://www.w3.org/2000/svg}image') + webp.attrib['x'] = str(0) + webp.attrib['y'] = str(0) + webp.attrib['width'] = str(width) + webp.attrib['height'] = str(height) + webp.attrib['{http://www.w3.org/1999/xlink}href'] = "data:image/png;base64,{}".format(base64_str) + base = ('' + ).format(width, height, 0, 0, width, height) + output = StringIO(base) + tree = etree.parse(output) + output.close() + tree.getroot().append(webp) + svgfile = os.path.join(tmp, "webp.svg") + with open(svgfile, 'w') as file: + tree.write(svgfile) + with open(svgfile, 'r') as newfile: + sys.stdout.write(newfile.read()) + + def load(self, stream): + return str(stream.read()) + +if __name__ == '__main__': + WebpImport().run() + \ No newline at end of file diff --git a/extensions/fablabchemnitz/x_agram/meta.json b/extensions/fablabchemnitz/x_agram/meta.json new file mode 100644 index 0000000..a4d0fbf --- /dev/null +++ b/extensions/fablabchemnitz/x_agram/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "X-Agram", + "id": "fablabchemnitz.de.x_agram", + "path": "x_agram", + "dependent_extensions": null, + "original_name": "X-agram", + "original_id": "com.kacmarcik.pathmonkey.x-agram", + "license": "MIT License", + "license_url": "https://github.com/garykac/pathmonkey/blob/master/LICENSE", + "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/x_agram", + "fork_url": "https://github.com/garykac/pathmonkey", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/X-Agram", + "inkscape_gallery_url": null, + "main_authors": [ + "github.com/garykac", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/x_agram/x_agram.inx b/extensions/fablabchemnitz/x_agram/x_agram.inx new file mode 100644 index 0000000..a09b267 --- /dev/null +++ b/extensions/fablabchemnitz/x_agram/x_agram.inx @@ -0,0 +1,34 @@ + + + X-Agram + fablabchemnitz.de.x_agram + + + 5 + 2 + 0 + false + true + 50 + + + + + + + path + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/x_agram/x_agram.py b/extensions/fablabchemnitz/x_agram/x_agram.py new file mode 100644 index 0000000..e692428 --- /dev/null +++ b/extensions/fablabchemnitz/x_agram/x_agram.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python3 +""" +X-agram +Create n-pointed star polygons (pentagram, hexagram, etc) +""" +import inkex +from math import * +from lxml import etree + +def addPathCommand(a, cmd): + for x in cmd: + a.append(str(x)) + +class XAgram(inkex.EffectExtension): + + def add_arguments(self, pars): + pars.add_argument('--tab') + pars.add_argument('--points', type=int, default=5, help='Number of points (or sides)') + pars.add_argument('--skip', type=int, default=2, help='Vertex increment when connecting points') + pars.add_argument('--rotate', type=float, default=0, help='Rotation angle (clockwise, in degrees)') + pars.add_argument('--inner_circle', type=inkex.Boolean, default=False, help='Connect points via inner circle') + pars.add_argument('--show_inner_circle', type=inkex.Boolean, default=True, help='Show inner circle') + pars.add_argument('--inner_ratio', type=int, default=50, help='Inner radius percentage (inner radius as a percentage of the outer radius)') + + def effect(self): + layer = self.svg.get_current_layer(); + + if len(self.svg.selected) == 0: + inkex.errormsg('Please select a circle or ellipse.') + exit() + + numValid = 0 + for id, obj in self.svg.selected.items(): + cx,cy, rx,ry = 0,0, 0,0 + style = '' + isValid = False + if obj.tag == inkex.addNS('circle','svg'): + isValid = True + cx = float(obj.get('cx')) + cy = float(obj.get('cy')) + rx = float(obj.get('r')) + ry = rx + elif obj.tag == inkex.addNS('ellipse', 'svg'): + isValid = True + cx = float(obj.get('cx')) + cy = float(obj.get('cy')) + rx = float(obj.get('rx')) + ry = float(obj.get('ry')) + elif obj.tag == inkex.addNS('path', 'svg'): + if obj.get(inkex.addNS('type', 'sodipodi')) == 'arc': + isValid = True + cx = float(obj.get(inkex.addNS('cx', 'sodipodi'))) + cy = float(obj.get(inkex.addNS('cy', 'sodipodi'))) + rx = float(obj.get(inkex.addNS('rx', 'sodipodi'))) + ry = float(obj.get(inkex.addNS('ry', 'sodipodi'))) + + if not isValid: + continue; + + numValid += 1 + style = obj.get('style') + transform = obj.get('transform') + isEllipse = False + if rx != ry: + isEllipse = True + + sides = self.options.points + skip = self.options.skip + rotate = self.options.rotate + useInnerCircle = self.options.inner_circle + showInnerCircle = self.options.show_inner_circle + innerRatio = float(self.options.inner_ratio) / 100.0 + + if useInnerCircle and showInnerCircle: + if not isEllipse: + cin = etree.SubElement(layer, inkex.addNS('circle','svg')) + cin.set('r', str(rx * innerRatio)) + else: + cin = etree.SubElement(layer, inkex.addNS('ellipse','svg')) + cin.set('rx', str(rx * innerRatio)) + cin.set('ry', str(ry * innerRatio)) + cin.set('cx', str(cx)) + cin.set('cy', str(cy)) + cin.set('style', style) + if transform: + cin.set('transform', transform) + + tau = 2*pi + origin = -(tau / 4) + (rotate * pi / 180) + out_pts = [] + in_pts = [] + for i in range(sides): + # Outer points (on outer circle) + theta = (i * (tau / sides)) + px = cx + rx * cos(origin + theta) + py = cy + ry * sin(origin + theta) + out_pts.append([px, py]) + + # Inner points (on inner circle) + theta = ((i + (skip / 2.0)) * (tau / sides)) + px = cx + rx * innerRatio * cos(origin + theta) + py = cy + ry * innerRatio * sin(origin + theta) + in_pts.append([px, py]) + + pts = [] + pt_done = {} + for i in range(sides): + if i in pt_done: + continue; + + p1 = out_pts[i] + addPathCommand(pts, ['M', p1[0], p1[1]]) + + pt_done[i] = True + start_index = i + curr = start_index + next = (curr + skip) % sides + while next != start_index: + p = out_pts[next] + pt_done[next] = True + + if useInnerCircle: + addPathCommand(pts, ['L', in_pts[curr][0], in_pts[curr][1]]) + + addPathCommand(pts, ['L', p[0], p[1]]) + + curr = next + next = (curr + skip) % sides + if useInnerCircle: + addPathCommand(pts, ['L', in_pts[curr][0], in_pts[curr][1]]) + addPathCommand(pts, ['z']) + + # Create star polygon as a single path. + l1 = etree.SubElement(layer, inkex.addNS('path','svg')) + l1.set('style', style) + if transform: + l1.set('transform', transform) + l1.set('d', ' '.join(pts)) + + if numValid == 0: + inkex.errormsg('Selection must contain a circle or ellipse.') + +if __name__ == '__main__': + XAgram().run() diff --git a/extensions/fablabchemnitz/zigzag_nodes/meta.json b/extensions/fablabchemnitz/zigzag_nodes/meta.json new file mode 100644 index 0000000..e1de5a8 --- /dev/null +++ b/extensions/fablabchemnitz/zigzag_nodes/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Zigzag Nodes", + "id": "fablabchemnitz.de.zigzag_nodes", + "path": "zigzag_nodes", + "dependent_extensions": null, + "original_name": "", + "original_id": "", + "license": "GNU GPL v2", + "license_url": "http://dp48069596.lolipop.jp/sd/scripts/script_inkscape/zigzag.zip", + "comment": "ported to Inkscape v1 by Mario Voigt", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/zigzag_nodes", + "fork_url": "http://dp48069596.lolipop.jp/sd/scripts/script_inkscape/zigzag.zip", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Zigzag+Nodes", + "inkscape_gallery_url": null, + "main_authors": [ + "Sunabe Kazumichi", + "github.com/eridur-de" + ] + } +] diff --git a/extensions/fablabchemnitz/zigzag_nodes/zigzag_nodes.inx b/extensions/fablabchemnitz/zigzag_nodes/zigzag_nodes.inx new file mode 100644 index 0000000..ad97548 --- /dev/null +++ b/extensions/fablabchemnitz/zigzag_nodes/zigzag_nodes.inx @@ -0,0 +1,18 @@ + + + Zigzag Nodes + fablabchemnitz.de.zigzag_nodes + + 3.0 + + path + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/zigzag_nodes/zigzag_nodes.py b/extensions/fablabchemnitz/zigzag_nodes/zigzag_nodes.py new file mode 100644 index 0000000..2ffd8ac --- /dev/null +++ b/extensions/fablabchemnitz/zigzag_nodes/zigzag_nodes.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +''' +zigzag.py +Sunabe kazumichi 2009/9/29 +http://dp48069596.lolipop.jp/ + + +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 program shifts the nodes zigzags. +''' +import random +import math +import inkex +from inkex import CubicSuperPath + +def pointdistance(x1, y1, x2, y2): + dist=math.sqrt(((x2 - x1) ** 2) + ((y2 - y1) ** 2)) + if dist==0: + dx=0 + dy=0 + else: + dx=(x2 - x1)/dist + dy=(y2 - y1)/dist + return dx,dy + +def randomize(x,y, r): + if y == 0: + a = math.pi + elif x == 0 and y != 0: + a = math.pi/2 + else: + a = math.atan2(y,x) + x = math.cos(a-math.pi/2)*r + y = math.sin(a-math.pi/2)*r + return [x, y] + +class ZigzagNodes(inkex.EffectExtension): + + def add_arguments(self, pars): + pars.add_argument("--title") + pars.add_argument("--radius", type=float, default=10.0, help="Randomly move control and end points in this radius") + + def effect(self): + for id, node in self.svg.selected.items(): + if node.tag == inkex.addNS('path','svg'): + d = node.get('d') + p = CubicSuperPath(d) + for subpath in p: + for i, csp in enumerate(subpath): + if i % 2 != 0: + dx,dy=pointdistance(csp[0][0], csp[0][1], csp[1][0], csp[1][1]) + delta=randomize(dx, dy, self.options.radius) + csp[0][0]+=delta[0] + csp[0][1]+=delta[1] + csp[1][0]+=delta[0] + csp[1][1]+=delta[1] + csp[2][0]+=delta[0] + csp[2][1]+=delta[1] + else: + dx,dy=pointdistance(csp[0][0], csp[0][1], csp[1][0], csp[1][1]) + delta=randomize(dx, dy, self.options.radius) + csp[0][0]-=delta[0] + csp[0][1]-=delta[1] + csp[1][0]-=delta[0] + csp[1][1]-=delta[1] + csp[2][0]-=delta[0] + csp[2][1]-=delta[1] + + node.set('d', CubicSuperPath(p)) + +if __name__ == '__main__': + ZigzagNodes().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/zoetrope/meta.json b/extensions/fablabchemnitz/zoetrope/meta.json new file mode 100644 index 0000000..9a5eaad --- /dev/null +++ b/extensions/fablabchemnitz/zoetrope/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Zoetrope", + "id": "fablabchemnitz.de.zoetrope", + "path": "zoetrope", + "dependent_extensions": null, + "original_name": "Zoetrope", + "original_id": "neon22.zoetrope", + "license": "MIT License", + "license_url": "https://github.com/Neon22/inkscape-Zoetrope/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/zoetrope", + "fork_url": "https://github.com/Neon22/inkscape-Zoetrope", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Zoetrope", + "inkscape_gallery_url": null, + "main_authors": [ + "github.com/Neon22", + "github.com/eridur-de" + ] + } +] diff --git a/extensions/fablabchemnitz/zoetrope/zoetrope.inx b/extensions/fablabchemnitz/zoetrope/zoetrope.inx new file mode 100644 index 0000000..d283049 --- /dev/null +++ b/extensions/fablabchemnitz/zoetrope/zoetrope.inx @@ -0,0 +1,58 @@ + + + Zoetrope + fablabchemnitz.de.zoetrope + + + + + + + + + + 12 + 24 + 3 + 0.1 + + + true + 3 + 3 + 0.1 + 0.1 + + + + true + 150 + + + + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/zoetrope/zoetrope.py b/extensions/fablabchemnitz/zoetrope/zoetrope.py new file mode 100644 index 0000000..1cf9e32 --- /dev/null +++ b/extensions/fablabchemnitz/zoetrope/zoetrope.py @@ -0,0 +1,232 @@ +#!/usr/bin/env python3 +''' +Zoetrope maker. +- prints disk of given diameter and number of images around the outside. +Also includes a pulse trigger ring to trigger a strobe. +- Width and phase of the pulse can be defined. +Prints a distorted and undistorted image reference sizes +- for use in a paint program to distort the source inages to fit onto the Disk. + +Neon22 - github 2016 +MIT license +''' + +import inkex +from lxml import etree +from math import cos, sin, radians, pi + +__version__ = '0.2' + +def point_on_circle(radius, angle): + " return xy coord of the point at distance radius from origin at angle " + x = radius * cos(angle) + y = radius * sin(angle) + return (x, y) + +def draw_SVG_circle(parent, r, cx, cy, name, style): + " structre an SVG circle entity under parent " + circ_attribs = {'style': str(inkex.Style(style)), + 'cx': str(cx), 'cy': str(cy), + 'r': str(r), + inkex.addNS('label','inkscape'): name} + circle = etree.SubElement(parent, inkex.addNS('circle','svg'), circ_attribs ) + +Black = '#000000' + +class Zoetrope(inkex.EffectExtension): + + def add_arguments(self, pars): + pars.add_argument("-u", "--units", default='mm', help="Units this dialog is using") + pars.add_argument("-d", "--diameter", type=float, default=1.0, help="Diameter of disk") + pars.add_argument("-n", "--divisions", type=int, default=24, help="Number of divisions") + pars.add_argument("-i", "--height", type=float, default=1.0, help="Image height") + pars.add_argument("-t", "--trigger", type=inkex.Boolean, default=False, help="Trigger") + pars.add_argument("-q", "--triggerradius", type=float, default=1.0, help="Height of trigger line") + pars.add_argument("-e", "--thick", type=float, default=1.0, help="Thickness of trigger line") + pars.add_argument("-r", "--ratio", type=float, default=0.5, help="Ratio of trigger pulse") + pars.add_argument("-p", "--phase", type=float, default=0, help="Delay of trigger pulse") + pars.add_argument("-w", "--stroke_width", type=float, default=0.1, help="Line thickness") + pars.add_argument("-m", "--template", type=inkex.Boolean, default=False, help="Show Image Distortion template") + pars.add_argument("-k", "--dpi", type=int, default=300, help="To calculate useful image size") + pars.add_argument("--active-tab", default='', help="Active tab. Not used now.") + + def calc_unit_factor(self): + """ return the scale factor for all dimension conversions. + - Everything in inkscape is expected to be in 90dpi pixel units + """ + unit_factor = self.svg.unittouu(str(1.0) + self.options.units) + return unit_factor + + def polar_to_cartesian(self, cx, cy, radius, angle): + " So we can make arcs in the 'A' svg syntax. " + angle_radians = radians(angle) + return (cx + (radius * cos(angle_radians)), + cy + (radius * sin(angle_radians))) + + def build_arc(self, x,y, start_angle, end_angle, radius, reverse=True): + " Make a filled arc " + # Not using internal arc rep - instead construct path A in svg style directly + # so we can append lines to make single path + start = self.polar_to_cartesian(x, y, radius, end_angle) + end = self.polar_to_cartesian(x, y, radius, start_angle) + arc_flag = 0 if reverse else 1 + sweep = 0 if (end_angle-start_angle) <=180 else 1 + path = 'M %s,%s' % (start[0], start[1]) + path += " A %s,%s 0 %d %d %s %s" % (radius, radius, sweep, arc_flag, end[0], end[1]) + return path + + def build_trigger_arc(self, angle, radius1, radius2): + """ return path + - using -ve angles to get pulse on CCW side of division line + """ + path = self.build_arc(0,0, -angle, 0, radius1) + # shorten and reverse second arc to connect + path += " L"+self.build_arc(0,0, 0, -angle, radius2, False)[1:] + path += " Z" # close + return path + + + +### ------------------------------------------------------------------- +### This is the main function and is called when the extension is run. + + def effect(self): + """ Calculate Zoetrope from inputs. + - Make gropups for each drawn entity type. + - add explanatory text + - Show trigger pulse ring, distortion and image templates + """ + # convert import options + unit_factor = self.calc_unit_factor() + path_stroke_width = self.options.stroke_width * unit_factor + diameter = self.options.diameter * unit_factor + divisions = self.options.divisions + image_height = self.options.height * unit_factor + triggerradius = self.options.triggerradius * unit_factor + thick = self.options.thick * unit_factor + cross = diameter/50 + + # 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'): 'Zoetrope', + 'transform': t, + 'info':'N: '+str(divisions)+';' } + # add the group to the document's current layer + topgroup = etree.SubElement(self.svg.get_current_layer(), 'g', g_attribs ) + # Group for pulse triggers + g_attr = { inkex.addNS('label','inkscape'): 'Pulse track'} + pulsegroup = etree.SubElement(topgroup, 'g', g_attr ) + # Group for Labels + t = 'translate(%s,%s)' % (0, diameter/1.9 ) + g_attr = { inkex.addNS('label','inkscape'): 'Label', 'transform': t } + labelgroup = etree.SubElement(topgroup, 'g', g_attr ) + + # Center cross + line_style = { 'stroke': Black, 'fill': 'none', 'stroke-width': path_stroke_width } + fill_style = { 'stroke': 'none', 'fill': Black, 'stroke-width': 'none' } + d = 'M {0},0 L {1},0 M 0,{0} L 0,{1}'.format(-cross,cross) + cross_attribs = { inkex.addNS('label','inkscape'): 'Center cross', + 'style': str(inkex.Style(line_style)), 'd': d } + cross_path = etree.SubElement(topgroup, inkex.addNS('path','svg'), cross_attribs ) + + # Main Disk + draw_SVG_circle(topgroup, diameter/2, 0, 0, 'outer_ring', line_style) + draw_SVG_circle(topgroup, diameter/2-image_height, 0, 0, 'image_ring', line_style) + # radials + trigger_angle = (360.0/divisions) * self.options.ratio + angle = 360.0/divisions + angle_radians = radians(angle) + arc_path = self.build_trigger_arc(trigger_angle, triggerradius, triggerradius + thick) + for i in range(divisions): + startpt = point_on_circle(cross*2, angle_radians*i) + if self.options.trigger: + endpt = point_on_circle(triggerradius, angle_radians*i) + else: + endpt = point_on_circle(diameter/2, angle_radians*i) + path = "M%s,%s L%s,%s"%(startpt[0], startpt[1], endpt[0], endpt[1]) + radial_attr = {inkex.addNS('label','inkscape'): 'radial', + 'style': str(inkex.Style(line_style)), 'd': path } + etree.SubElement(topgroup, inkex.addNS('path','svg'), radial_attr ) + # second part of radial line (and trigger ring) if needed + if self.options.trigger: + # radial lines + startpt = point_on_circle(triggerradius + thick, angle_radians*i) + endpt = point_on_circle(diameter/2, angle_radians*i) + path = "M%s,%s L%s,%s"%(startpt[0], startpt[1], endpt[0], endpt[1]) + radial_attr = {inkex.addNS('label','inkscape'): 'radial', + 'style': str(inkex.Style(line_style)), 'd': path } + etree.SubElement(topgroup, inkex.addNS('path','svg'), radial_attr ) + # add the arcs # CCW rotation + arc_offset = angle*i - (angle-trigger_angle)*self.options.phase + t = 'rotate(%s)' % (arc_offset) + attribs = { inkex.addNS('label','inkscape'): 'trigger', + 'style': str(inkex.Style(fill_style)), 'd': arc_path , 'transform': t,} + etree.SubElement(pulsegroup, inkex.addNS('path','svg'), attribs ) + # Add animation of bouncing ball + # Add pale grid on each image so can draw directly on template + + # + if self.options.trigger: + draw_SVG_circle(pulsegroup, triggerradius, 0, 0, 'trigger_ring', line_style) + draw_SVG_circle(pulsegroup, triggerradius + thick, 0, 0, 'trigger_ring', line_style) + + # text Label + font_height = min(32, max( 8, int(diameter/50.0))) + text_style = { 'font-size': str(font_height), + 'font-family': 'sans-serif', + 'text-anchor': 'middle', + 'text-align': 'center', + 'fill': Black } + text_atts = {'style':str(inkex.Style(text_style)), + 'x': '0', 'y': '0' } + text = etree.SubElement(labelgroup, 'text', text_atts) + text.text = "Zoetrope" + text_atts = {'style':str(inkex.Style(text_style)), + 'x': '0', 'y': str(font_height*1.2) } + text = etree.SubElement(labelgroup, 'text', text_atts) + text.text = "Diameter = %4.2f%s. Divisions = %d" % (self.options.diameter, self.options.units, divisions) + text_atts = {'style':str(inkex.Style(text_style)), + 'x': '0', 'y': str(font_height*2.4) } + if self.options.trigger: + text = etree.SubElement(labelgroup, 'text', text_atts) + text.text = "Pulse Duty = %4.2f, Phase = %4.2f" % (self.options.ratio, self.options.phase) + + # Distortion pattern + if self.options.template: + # Group for Labels + t = 'translate(%s,%s)' % (0, -image_height-font_height*5 ) + g_attr = { inkex.addNS('label','inkscape'): 'Template', 'transform': t } + templategroup = etree.SubElement(topgroup, 'g', g_attr ) + # Draw template + arc_path = self.build_trigger_arc(angle, diameter/2, diameter/2-image_height) + t = 'rotate(%s)' % (-90+angle/2) + attribs = { inkex.addNS('label','inkscape'): 'distorted image', + 'style': str(inkex.Style(line_style)), 'd': arc_path , 'transform': t} + image = etree.SubElement(templategroup, inkex.addNS('path','svg'), attribs ) + # Draw Image info + image_width = pi*diameter/divisions + ystart = -diameter/2.0 + image_height + image_ratio = image_width / image_height + text_atts = {'style':str(inkex.Style(text_style)), + 'x': '0', 'y': str(ystart + font_height*2) } + text = etree.SubElement(templategroup, 'text', text_atts) + text.text = "Aspect ratio=1:%4.2f" % (image_ratio) + # template rect + attr = {'x':str(-image_width*1.8), 'y':str(-diameter/2), + 'width':str(image_width), + 'height':str(image_height), + 'style':str(inkex.Style(line_style))} + template_sq = etree.SubElement(templategroup, 'rect', attr) + # suggested sizes + # image_height is in 90dpi pixels + dpi_factor = self.svg.unittouu('1in')/float(self.options.dpi) + h = int(image_height / float(dpi_factor)) + w = int(h*image_ratio) + text_atts = {'style':str(inkex.Style(text_style)), + 'x': '0', 'y': str(ystart + font_height*3.2) } + text = etree.SubElement(templategroup, 'text', text_atts) + text.text = "At %d dpi. Image = %d x %d pixels" % (self.options.dpi, w, h) + +if __name__ == '__main__': + Zoetrope().run() \ No newline at end of file