Merge branch 'master' of lastycah.fablabchemnitz.de:MarioVoigt/mightyscape-1.X
This commit is contained in:
commit
ef06056348
@ -1,4 +0,0 @@
|
||||
#!/bin/sh
|
||||
KABEJA_HOME=`dirname $0`
|
||||
java -jar $KABEJA_HOME/kabeja-dxf2svg.jar "$1"
|
||||
|
Binary file not shown.
@ -1,3 +0,0 @@
|
||||
@echo off
|
||||
set KABEJA_HOME=%0\..
|
||||
java -jar %KABEJA_HOME%\kabeja-dxf2svg.jar %1
|
@ -1,22 +0,0 @@
|
||||
<inkscape-extension>
|
||||
<name>Kabeja DXF Input</name>
|
||||
<id>org.kabeja.inkscape.import.dxf</id>
|
||||
<dependency type="extension">org.inkscape.input.svg</dependency>
|
||||
<param name="description" type="description">Choose the layout for the import.</param>
|
||||
<param name="bounds-rule" gui-text="Layout" type="enum">
|
||||
<item>Modelspace</item>
|
||||
<item>Modelspace-Limits</item>
|
||||
<item>Paperspace</item>
|
||||
<item>Paperspace-Limits</item>
|
||||
</param>
|
||||
<input>
|
||||
<extension>.dxf</extension>
|
||||
<mimetype>image/vnd.dxf</mimetype>
|
||||
<filetypename>Kabeja-AutoCAD DXF (*.dxf)</filetypename>
|
||||
<filetypetooltip>Import AutoCAD's Document Exchange Format</filetypetooltip>
|
||||
<output_extension>org.inkscape.output.svg</output_extension>
|
||||
</input>
|
||||
<script>
|
||||
<command reldir="extensions" >kabeja</command>
|
||||
</script>
|
||||
</inkscape-extension>
|
@ -1,4 +0,0 @@
|
||||
#!/bin/sh
|
||||
KABEJA_HOME=`dirname $0`
|
||||
java -jar $KABEJA_HOME/kabeja-dxf2svg.jar "$1"
|
||||
|
Binary file not shown.
@ -1,3 +0,0 @@
|
||||
@echo off
|
||||
set KABEJA_HOME=%0\..
|
||||
java -jar %KABEJA_HOME%\kabeja-dxf2svg.jar %1
|
@ -1,22 +0,0 @@
|
||||
<inkscape-extension>
|
||||
<name>Kabeja DXF Input</name>
|
||||
<id>org.kabeja.inkscape.import.dxf</id>
|
||||
<dependency type="extension">org.inkscape.input.svg</dependency>
|
||||
<param name="description" type="description">Choose the layout for the import.</param>
|
||||
<param name="bounds-rule" gui-text="Layout" type="enum">
|
||||
<item>Modelspace</item>
|
||||
<item>Modelspace-Limits</item>
|
||||
<item>Paperspace</item>
|
||||
<item>Paperspace-Limits</item>
|
||||
</param>
|
||||
<input>
|
||||
<extension>.dxf</extension>
|
||||
<mimetype>image/vnd.dxf</mimetype>
|
||||
<filetypename>Kabeja-AutoCAD DXF (*.dxf)</filetypename>
|
||||
<filetypetooltip>Import AutoCAD's Document Exchange Format</filetypetooltip>
|
||||
<output_extension>org.inkscape.output.svg</output_extension>
|
||||
</input>
|
||||
<script>
|
||||
<command reldir="extensions" >kabeja</command>
|
||||
</script>
|
||||
</inkscape-extension>
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<inkscape-extension xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape">
|
||||
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
|
||||
<name>Ids To Text</name>
|
||||
<id>org.inkscape.render.ids_to_text</id>
|
||||
<id>fablabchemnitz.de.ids_to_text</id>
|
||||
<param name="fontsize" type="int" min="1" max="1000" gui-text="Font size (px):">10</param>
|
||||
<param name="color" type="color" appearance="colorbutton" gui-text="Color (in hex)">255</param>
|
||||
<param name="font" type="string" gui-text="Font">Roboto</param>
|
||||
|
80
extensions/fablabchemnitz/ifs_fractals.inx
Normal file
80
extensions/fablabchemnitz/ifs_fractals.inx
Normal file
@ -0,0 +1,80 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
|
||||
<name>IFS Fractals</name>
|
||||
<id>fablabchemnitz.de.ifs_fractals</id>
|
||||
<param name="tab" type="notebook">
|
||||
<page name="Options" gui-text="Options">
|
||||
<param name="iter" type="int" min="0" max="100" gui-text="Number of iterations:">3</param>
|
||||
<label appearance="header">Transform Matrices</label>
|
||||
<param name="tab" type="notebook">
|
||||
<page name="0" gui-text="1">
|
||||
<param name="xform0" type="bool" gui-text="Enabled:">true</param>
|
||||
<param name="A0" type="float" min="-10000" max="10000" precision="3" gui-text="A:">0.5</param>
|
||||
<param name="B0" type="float" min="-10000" max="10000" precision="3" gui-text="B:">0</param>
|
||||
<param name="C0" type="float" min="-10000" max="10000" precision="3" gui-text="C:">0</param>
|
||||
<param name="D0" type="float" min="-10000" max="10000" precision="3" gui-text="D:">0.5</param>
|
||||
<param name="E0" type="float" min="-10000" max="10000" precision="3" gui-text="E:">0</param>
|
||||
<param name="F0" type="float" min="-10000" max="10000" precision="3" gui-text="F:">0</param>
|
||||
</page>
|
||||
<page name="1" gui-text="2">
|
||||
<param name="xform1" type="bool" gui-text="Enabled:">false</param>
|
||||
<param name="A1" type="float" min="-10000" max="10000" precision="3" gui-text="A:">0.5</param>
|
||||
<param name="B1" type="float" min="-10000" max="10000" precision="3" gui-text="B:">0</param>
|
||||
<param name="C1" type="float" min="-10000" max="10000" precision="3" gui-text="C:">0</param>
|
||||
<param name="D1" type="float" min="-10000" max="10000" precision="3" gui-text="D:">0.5</param>
|
||||
<param name="E1" type="float" min="-10000" max="10000" precision="3" gui-text="E:">1</param>
|
||||
<param name="F1" type="float" min="-10000" max="10000" precision="3" gui-text="F:">0</param>
|
||||
</page>
|
||||
<page name="2" gui-text="3">
|
||||
<param name="xform2" type="bool" gui-text="Enabled:">false</param>
|
||||
<param name="A2" type="float" min="-10000" max="10000" precision="3" gui-text="A:">0.5</param>
|
||||
<param name="B2" type="float" min="-10000" max="10000" precision="3" gui-text="B:">0</param>
|
||||
<param name="C2" type="float" min="-10000" max="10000" precision="3" gui-text="C:">0</param>
|
||||
<param name="D2" type="float" min="-10000" max="10000" precision="3" gui-text="D:">0.5</param>
|
||||
<param name="E2" type="float" min="-10000" max="10000" precision="3" gui-text="E:">0.5</param>
|
||||
<param name="F2" type="float" min="-10000" max="10000" precision="3" gui-text="F:">1</param>
|
||||
</page>
|
||||
<page name="3" gui-text="4">
|
||||
<param name="xform3" type="bool" gui-text="Enabled:">false</param>
|
||||
<param name="A3" type="float" min="-10000" max="10000" precision="3" gui-text="A:">0.5</param>
|
||||
<param name="B3" type="float" min="-10000" max="10000" precision="3" gui-text="B:">0</param>
|
||||
<param name="C3" type="float" min="-10000" max="10000" precision="3" gui-text="C:">0</param>
|
||||
<param name="D3" type="float" min="-10000" max="10000" precision="3" gui-text="D:">0.5</param>
|
||||
<param name="E3" type="float" min="-10000" max="10000" precision="3" gui-text="E:">0</param>
|
||||
<param name="F3" type="float" min="-10000" max="10000" precision="3" gui-text="F:">0</param>
|
||||
</page>
|
||||
<page name="4" gui-text="5">
|
||||
<param name="xform4" type="bool" gui-text="Enabled:">false</param>
|
||||
<param name="A4" type="float" min="-10000" max="10000" precision="3" gui-text="A:">0.5</param>
|
||||
<param name="B4" type="float" min="-10000" max="10000" precision="3" gui-text="B:">0</param>
|
||||
<param name="C4" type="float" min="-10000" max="10000" precision="3" gui-text="C:">0</param>
|
||||
<param name="D4" type="float" min="-10000" max="10000" precision="3" gui-text="D:">0.5</param>
|
||||
<param name="E4" type="float" min="-10000" max="10000" precision="3" gui-text="E:">0</param>
|
||||
<param name="F4" type="float" min="-10000" max="10000" precision="3" gui-text="F:">0</param>
|
||||
</page>
|
||||
</param>
|
||||
</page>
|
||||
<page name="Help" gui-text="Help">
|
||||
<label>
|
||||
This performs an Iterated Function System by repeating one or more duplicate-and-transform operations on the selected objects.
|
||||
</label>
|
||||
<label>
|
||||
The transformations are specified using matrices, in the same form as the transformation dialog, each of which should be contractive (i.e., shrinking).
|
||||
</label>
|
||||
<label>
|
||||
For example, if you set N transforms, it will make N duplicates and transform each in the first iteration, and then N^2 duplicates of those, and so on, for a total of (N^(I+1)-1)/(N-1) duplicates.
|
||||
</label>
|
||||
</page>
|
||||
</param>
|
||||
<effect>
|
||||
<object-type>all</object-type>
|
||||
<effects-menu>
|
||||
<submenu name="FabLab Chemnitz">
|
||||
<submenu name="Shape/Pattern from Generator"/>
|
||||
</submenu>
|
||||
</effects-menu>
|
||||
</effect>
|
||||
<script>
|
||||
<command location="inx" interpreter="python">ifs_fractals.py</command>
|
||||
</script>
|
||||
</inkscape-extension>
|
72
extensions/fablabchemnitz/ifs_fractals.py
Normal file
72
extensions/fablabchemnitz/ifs_fractals.py
Normal file
@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env python3
|
||||
# coding=utf-8
|
||||
#
|
||||
# Copyright (C) 2020 Dylan Simon, dylan@dylex.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
"""
|
||||
Perform fixed-depth IFS repeated duplicate-and-transform.
|
||||
"""
|
||||
|
||||
import inkex
|
||||
|
||||
class IFS(inkex.EffectExtension):
|
||||
NXFORM = 5
|
||||
XFORM_PARAMS = list("ABCDEF")
|
||||
|
||||
def add_arguments(self, pars):
|
||||
pars.add_argument("--tab")
|
||||
pars.add_argument("--iter", type=int, default=3, help="number of iterations")
|
||||
for i in range(self.NXFORM):
|
||||
pars.add_argument("--xform%d"%i, type=inkex.Boolean, default=False, help="enable transformation %d"%i)
|
||||
for p in self.XFORM_PARAMS:
|
||||
pars.add_argument("--%s%d"%(p,i), type=float, help="transformation matrix %d %s"%(i,p))
|
||||
|
||||
def effect(self):
|
||||
xforms = []
|
||||
for i in range(self.NXFORM):
|
||||
if getattr(self.options,'xform%d'%i):
|
||||
t = [getattr(self.options,"%s%d"%(p,i)) for p in self.XFORM_PARAMS]
|
||||
xforms.append(inkex.Transform(t))
|
||||
|
||||
if not xforms:
|
||||
inkex.errormsg(_('There are no transforms to apply'))
|
||||
return False
|
||||
|
||||
if not self.svg.selected:
|
||||
inkex.errormsg(_('There is no selection to duplicate'))
|
||||
return False
|
||||
|
||||
nodes = self.svg.selected.values()
|
||||
grp = inkex.Group('IFS')
|
||||
layer = self.svg.get_current_layer().add(grp)
|
||||
|
||||
for i in range(self.options.iter):
|
||||
n = []
|
||||
for node in nodes:
|
||||
for x in xforms:
|
||||
d = node.copy()
|
||||
d.transform = x * d.transform
|
||||
n.append(d)
|
||||
g = inkex.Group('IFS iter %d'%i)
|
||||
g.add(*n)
|
||||
grp.add(g)
|
||||
nodes = n
|
||||
|
||||
return True
|
||||
|
||||
if __name__ == '__main__':
|
||||
IFS().run()
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<inkscape-extension>
|
||||
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
|
||||
<name>InkPACKING</name>
|
||||
<id>fablabchemnitz.de.inkpacking</id>
|
||||
<param name="pages" type="notebook">
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<inkscape-extension>
|
||||
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
|
||||
<name>Longest Continuous Path</name>
|
||||
<id>fablabchemnitz.de.optimize_paths</id>
|
||||
<param name="tolerance" type="float" min="0.0" max="100.0" gui-text="Merge Tolerance:">0.10</param>
|
||||
|
27
extensions/fablabchemnitz/maze/laby.inx
Normal file
27
extensions/fablabchemnitz/maze/laby.inx
Normal file
@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
|
||||
<name>Maze</name>
|
||||
<id>fablabchemnitz.de.maze</id>
|
||||
<param name="verti" type="int" min="2" max="1000" gui-text="Height">20</param>
|
||||
<param name="horiz" type="int" min="2" max="1000" gui-text="Length">20</param>
|
||||
<param name="size" type="float" max="100.0" gui-text="Cell Size">10.0</param>
|
||||
<param name="width" type="float" gui-text="Line Width">1.0</param>
|
||||
<param name="algo" type="optiongroup" appearance="minimal" gui-text="Algorithm">
|
||||
<option value="kruskal">Kruskal</option>
|
||||
<option value="recursive_backtrack">Recursive Backtrack</option>
|
||||
<option value="empty">Empty</option>
|
||||
<option value="full">Full</option>
|
||||
</param>
|
||||
<param name="use2" type="description" xml:space="preserve">This script will generate a maze according to a certain algorithm.</param>
|
||||
<effect>
|
||||
<object-type>path</object-type>
|
||||
<effects-menu>
|
||||
<submenu name="FabLab Chemnitz">
|
||||
<submenu name="Shape/Pattern from Generator" />
|
||||
</submenu>
|
||||
</effects-menu>
|
||||
</effect>
|
||||
<script>
|
||||
<command reldir="inx" interpreter="python">laby.py</command>
|
||||
</script>
|
||||
</inkscape-extension>
|
70
extensions/fablabchemnitz/maze/laby.py
Normal file
70
extensions/fablabchemnitz/maze/laby.py
Normal file
@ -0,0 +1,70 @@
|
||||
#! /usr/bin/env python3
|
||||
|
||||
# this extension is under licence CC-by-sa @ Tiemen DUVILLARD 2020
|
||||
# for all questions, comments, bugs: duvillard.tiemen@gmail.com
|
||||
|
||||
import inkex
|
||||
from lxml import etree
|
||||
|
||||
# my maze module
|
||||
from maze 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 Recursive(inkex.Effect):
|
||||
|
||||
def __init__(self):
|
||||
" define how the options are mapped from the inx file "
|
||||
inkex.Effect.__init__(self) # initialize the super class
|
||||
|
||||
self.arg_parser.add_argument("--verti", type=int, default=20, help="Height")
|
||||
self.arg_parser.add_argument("--horiz", type=int, default=20, help="Length")
|
||||
self.arg_parser.add_argument("--size", type=float, default=10.0, help="Cell Size")
|
||||
self.arg_parser.add_argument("--algo", default=1, help="Algorithm")
|
||||
self.arg_parser.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.options.size
|
||||
X = self.options.verti
|
||||
Y = self.options.horiz
|
||||
|
||||
L = Maze(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__':
|
||||
Recursive().run()
|
537
extensions/fablabchemnitz/maze/maze.py
Normal file
537
extensions/fablabchemnitz/maze/maze.py
Normal file
@ -0,0 +1,537 @@
|
||||
#! /usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# this module is under licence CC-by-sa @ 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
|
||||
|
||||
# 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 Maze:
|
||||
"""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
|
||||
|
||||
|
||||
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
|
||||
|
||||
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 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 Pile[-1] != (xb,yb) and len(Pile) != 0:
|
||||
pos = Pile[-1]
|
||||
x,y = pos[0],pos[1]
|
||||
if laby[y][x] == 'new':
|
||||
possibilite = []
|
||||
for d in ['down',"up","left","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)
|
||||
|
||||
if len(possibilite) == 0:
|
||||
laby[y][x] = "old"
|
||||
else :
|
||||
laby[y][x] = possibilite
|
||||
elif laby[y][x] == "old":
|
||||
del Pile[-1]
|
||||
del Sol[-1]
|
||||
elif type(laby[y][x]) == list:
|
||||
if len(laby[y][x]) == 0:
|
||||
laby[y][x] = "old"
|
||||
else:
|
||||
ichoix = 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 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
|
@ -35,7 +35,7 @@
|
||||
<param name="xz_mirror" type="bool" gui-text="XZ-Mirror">false</param>
|
||||
<param name="scale" type="float" min="0.0001" max="10000.0" precision="4" gui-text="Scale">1.0</param>
|
||||
</page>
|
||||
<page name="meshfixing" gui-text="About">
|
||||
<page name="about" gui-text="About">
|
||||
<label>Papercraft Unfold - by Mario Voigt / Stadtfabrikanten e.V. (2020)</label>
|
||||
<label appearance="url">https://fablabchemnitz.de</label>
|
||||
<label>License: GNU GPL v3</label>
|
||||
|
22
extensions/fablabchemnitz/removeDuplicateNodes.inx
Normal file
22
extensions/fablabchemnitz/removeDuplicateNodes.inx
Normal file
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
|
||||
<name>Purge Duplicate Path Nodes</name>
|
||||
<id>fablabchemnitz.de.purge_duplicate_path_nodes</id>
|
||||
<param name="help" type="description">Remove duplicate nodes from selected paths.</param>
|
||||
<param name="minUse" type="boolean" gui-text="Also remove segments with length less than specified length">false</param>
|
||||
<param name="minlength" indent="6" type="float" precision="1" min="0" max="9999" gui-text="Minimum segment length">0.01</param>
|
||||
<param name="joinEnd" type="boolean" gui-text="Join end nodes of each subpath if distance less or equal to">false</param>
|
||||
<param name="maxdist" indent="6" type="float" precision="1" min="0" max="9999" gui-text="Max opening">0.01</param>
|
||||
<param name="unitinfo" type="description">Unit as defined in document (File->Document Properties).</param>
|
||||
<effect>
|
||||
<object-type>path</object-type>
|
||||
<effects-menu>
|
||||
<submenu name="FabLab Chemnitz">
|
||||
<submenu name="Nesting/Cut Optimization"/>
|
||||
</submenu>
|
||||
</effects-menu>
|
||||
</effect>
|
||||
<script>
|
||||
<command location="inx" interpreter="python">removeDuplicateNodes.py</command>
|
||||
</script>
|
||||
</inkscape-extension>
|
101
extensions/fablabchemnitz/removeDuplicateNodes.py
Normal file
101
extensions/fablabchemnitz/removeDuplicateNodes.py
Normal file
@ -0,0 +1,101 @@
|
||||
#!/usr/bin/env python
|
||||
# coding=utf-8
|
||||
#
|
||||
# Copyright (C) 2020 Ellen Wasboe, ellen@wasbo.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
"""
|
||||
Remove duplicate nodes or join nodes with distance less than specified.
|
||||
Optionally join start node with end node of each subpath if distance less than specified.
|
||||
"""
|
||||
|
||||
import inkex
|
||||
from inkex import bezier, PathElement, CubicSuperPath
|
||||
|
||||
class removeDuplicateNodes(inkex.EffectExtension):
|
||||
|
||||
def add_arguments(self, pars):
|
||||
pars.add_argument("--minlength", default="0")
|
||||
pars.add_argument("--minUse", type=inkex.Boolean, default=False)
|
||||
pars.add_argument("--maxdist", default="0")
|
||||
pars.add_argument("--joinEnd", type=inkex.Boolean, default=False)
|
||||
|
||||
"""Remove duplicate nodes"""
|
||||
def effect(self):
|
||||
for id, elem in self.svg.selected.items():
|
||||
minlength=float(self.options.minlength)
|
||||
maxdist=float(self.options.maxdist)
|
||||
if self.options.minUse == False:
|
||||
minlength=0
|
||||
if self.options.joinEnd == False:
|
||||
maxdist=-1
|
||||
|
||||
#register which subpaths are closed
|
||||
dList=str(elem.path).upper().split(' M')
|
||||
closed=[""]
|
||||
l=0
|
||||
for sub in dList:
|
||||
if dList[l].find("Z") > -1:
|
||||
closed.append(" Z ")
|
||||
else:
|
||||
closed.append("")
|
||||
l+=1
|
||||
closed.pop(0)
|
||||
|
||||
new = []
|
||||
s=0
|
||||
for sub in elem.path.to_superpath():
|
||||
new.append([sub[0]])
|
||||
i = 1
|
||||
while i <= len(sub) - 1:
|
||||
length = bezier.cspseglength(new[-1][-1], sub[i]) #curve length
|
||||
if length >= minlength:
|
||||
new[-1].append(sub[i])
|
||||
else:
|
||||
#average last node xy with this node xy and set this further node to last
|
||||
new[-1][-1][1][0]= 0.5*(new[-1][-1][1][0]+sub[i][1][0])
|
||||
new[-1][-1][1][1]= 0.5*(new[-1][-1][1][1]+sub[i][1][1])
|
||||
new[-1][-1][-1]=sub[i][-1]
|
||||
i += 1
|
||||
|
||||
if maxdist > -1:
|
||||
#calculate distance between first and last node
|
||||
#if <= maxdist set closed[i] to "Z "
|
||||
last=new[-1][-1]
|
||||
length = bezier.cspseglength(new[-1][-1], sub[0])
|
||||
if length <= maxdist:
|
||||
newStartEnd=[0.5*(new[-1][-1][-1][0]+new[0][0][0][0]),0.5*(new[-1][-1][-1][1]+new[0][0][0][1])]
|
||||
new[0][0][0]=newStartEnd
|
||||
new[0][0][1]=newStartEnd
|
||||
new[-1][-1][1]=newStartEnd
|
||||
new[-1][-1][2]=newStartEnd
|
||||
closed[s]=" Z "
|
||||
s+=1
|
||||
|
||||
elem.path = CubicSuperPath(new).to_path(curves_only=True)
|
||||
|
||||
#reset z to the originally closed paths (z lost in cubicsuperpath)
|
||||
temppath=str(elem.path.to_absolute()).split('M ')
|
||||
temppath.pop(0)
|
||||
newPath=''
|
||||
l=0
|
||||
for sub in temppath:
|
||||
newPath=newPath+'M '+temppath[l]+closed[l]
|
||||
l+=1
|
||||
elem.path=newPath
|
||||
|
||||
if __name__ == '__main__':
|
||||
removeDuplicateNodes().run()
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<inkscape-extension ns="http://www.inkscape.org/namespace/inkscape/extension">
|
||||
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
|
||||
<name>Vertical / Horizontal Scale</name>
|
||||
<id>fablabchemnitz.de.render_scale</id>
|
||||
<param name="tab" type="notebook">
|
||||
|
382
extensions/fablabchemnitz/shapes/arakne_xy.py
Normal file
382
extensions/fablabchemnitz/shapes/arakne_xy.py
Normal file
@ -0,0 +1,382 @@
|
||||
#!/usr/bin/env python3
|
||||
'''
|
||||
Copyright (C) 2015 Paco Garcia, www.arakne.es
|
||||
|
||||
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 os, sys, tempfile, webbrowser, math, inkex, simplestyle, simpletransform
|
||||
from lxml import etree
|
||||
|
||||
def info(s, newLine="\n"):
|
||||
sys.stderr.write(s)
|
||||
#sys.stderr.write(s.encode("UTF-8"))
|
||||
sys.stderr.write(newLine)
|
||||
|
||||
def tern(condition,val1,val2):
|
||||
return val1 if condition else val2
|
||||
|
||||
def _rads(n):
|
||||
return math.radians(n)
|
||||
|
||||
def pow2(n):
|
||||
return math.pow(n, 2)
|
||||
|
||||
# calcula la hipotenusa dados los catetos
|
||||
def triHipo(catA, catB):
|
||||
return math.sqrt(pow2(catA) + pow2(catB))
|
||||
|
||||
# calcula el cateto dada la hipotenusa y el otro cateto
|
||||
def triCat(Hipo, catA):
|
||||
return math.sqrt(pow2(Hipo) - pow2(catA))
|
||||
|
||||
class XY:
|
||||
"""A class for work with 2d points"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.co=[0.0,0.0]
|
||||
lArgs=len(args)
|
||||
if lArgs>0:
|
||||
if lArgs==1:
|
||||
if type(args[0])==XY:
|
||||
self.co=args[0].co
|
||||
else:
|
||||
self.co=[args[0],args[0]]
|
||||
if lArgs>1:
|
||||
self.co=[args[0],args[1]]
|
||||
def __add__(self,xy):
|
||||
self.co = [self.co[0] + xy.co[0],self.co[1] + xy.co[1]]
|
||||
return self
|
||||
def __sub__(self,xy):
|
||||
self.co=[self.co[0] - xy.co[0], self.co[1] - xy.co[1]]
|
||||
return self
|
||||
def __eq__(self, xy):
|
||||
return (self.co[0] == xy.x and self.co[1] == xy.y)
|
||||
|
||||
def sub(self,xy):
|
||||
self.__sub__(xy)
|
||||
return self
|
||||
def mul(self,xy):
|
||||
if type(xy)==XY:
|
||||
co=[self.co[0] * xy.co[0],self.co[1] * xy.co[1]]
|
||||
else:
|
||||
co=[self.co[0] * xy,self.co[1] * xy]
|
||||
self.co = co
|
||||
return self
|
||||
|
||||
def div(self,xy):
|
||||
if type(xy)==XY:
|
||||
co=[self.co[0] / xy.co[0], self.co[1] / xy.co[1]]
|
||||
else:
|
||||
co=[self.co[0] / xy, self.co[1] / xy]
|
||||
self.co = co
|
||||
return self
|
||||
|
||||
def vlength(self):
|
||||
#return math.sqrt((self.co[0]*self.co[0])+(self.co[1]*self.co[1]))
|
||||
return triHipo(self.co[0], self.co[1])
|
||||
|
||||
def rot(self,ang):
|
||||
x,y,sa,ca= (self.co[0], self.co[1], math.sin(ang), math.cos(ang))
|
||||
self.co=[ca * x - sa * y, sa * x + ca * y]
|
||||
return self
|
||||
|
||||
def Rot(self,p,r):
|
||||
self.co=[math.cos(r)*p, math.sin(r)*p]
|
||||
return self
|
||||
|
||||
def rotate(self,rot,cX=0.0,cY=0.0):
|
||||
cosRot = math.cos(rot)
|
||||
px = cX + (self.x-cX) * cosRot - (self.y-cY) * math.sin(rot)
|
||||
py = cY + (self.x-cX) * math.sin(rot) + (self.y-cY) * cosRot
|
||||
self.co = [px,py]
|
||||
return self
|
||||
|
||||
def rotateD(self,rot,cX=0.0,cY=0.0):
|
||||
self.rotate(_rads(rot),cX,cY)
|
||||
return self
|
||||
|
||||
def VDist(self,V2):
|
||||
tmp = XY(self.co[0],self.co[1])
|
||||
tmp = tmp.sub(V2)
|
||||
return tmp.vlength()
|
||||
|
||||
def st(self):
|
||||
return str(self.co[0])+','+str(self.co[1])
|
||||
|
||||
@property
|
||||
def x(self):
|
||||
return self.co[0]
|
||||
|
||||
@property
|
||||
def sx(self):
|
||||
return str(self.co[0])
|
||||
|
||||
@property
|
||||
def y(self):
|
||||
return self.co[1]
|
||||
|
||||
@property
|
||||
def sy(self):
|
||||
return str(self.co[1])
|
||||
|
||||
def hipo(self,xy):
|
||||
#return math.sqrt(math.pow(self.x-xy.x,2) + math.pow(self.y-xy.y,2) )
|
||||
return triHipo(self.x-xy.x, self.y-xy.y)
|
||||
def angBetween2Lines(self,p1,p2): # pC punto comun
|
||||
return math.atan2(self.y - p1.y, self.x - p1.x) - math.atan2(self.y - p2.y, self.x - p2.x)
|
||||
def getAngle(self,b):
|
||||
return math.atan2(b.y - self.y, b.x - self.x)
|
||||
def getAngleD(self,b):
|
||||
return math.degrees(math.atan2(b.y - self.y, b.x - self.x))
|
||||
# translada un punto hacia otro
|
||||
def atPercent(self, p2, percent):
|
||||
self.co = [(p2.x - self.x) * percent + self.x,(p2.y-self.y) * percent + self.y]
|
||||
return self
|
||||
|
||||
def atMid(self, p2):
|
||||
return self.atPercent(p2,0.5)
|
||||
|
||||
# ________________________________________________________________
|
||||
# ________________________________________________________________
|
||||
# ________________________________________________________________
|
||||
class bezpnt(object):
|
||||
def __init__(self,pfixed=None,pprev=None,pnext=None):
|
||||
if isinstance(pfixed, list):
|
||||
self.fixed = XY(pfixed[0],pfixed[1])
|
||||
else:
|
||||
self.fixed = pfixed
|
||||
if isinstance(pprev, list):
|
||||
self.prev = XY(pprev[0],pprev[1])
|
||||
else:
|
||||
self.prev = pprev
|
||||
if isinstance(pnext, list):
|
||||
self.next = XY(pnext[0],pnext[1])
|
||||
else:
|
||||
self.next = pnext
|
||||
return
|
||||
|
||||
def translate(self,x,y):
|
||||
self.fixed + XY(x,y)
|
||||
if self.prev!=None:self.prev + XY(x,y)
|
||||
if self.next!=None:self.next + XY(x,y)
|
||||
return self
|
||||
|
||||
def scale(self,x=1.0,y=1.0):
|
||||
self.fixed.scale(x,y)
|
||||
if self.prev!=None:self.prev.scale(x,y)
|
||||
if self.next!=None:self.next.scale(x,y)
|
||||
return self
|
||||
|
||||
def rotate(self,rot,cX=0.0,cY=0.0):
|
||||
self.fixed.rotate(rot,cX,cY)
|
||||
if self.prev!=None:self.prev.rotate(rot,cX,cY)
|
||||
if self.next!=None:self.next.rotate(rot,cX,cY)
|
||||
return sel
|
||||
|
||||
def skew(self,rotx,roty,cX=0.0,cY=0.0):
|
||||
self.fixed.skew(rotx,roty,cX,cY)
|
||||
if self.prev!=None:self.prev.skew(rotx,roty,cX,cY)
|
||||
if self.next!=None:self.next.skew(rotx,roty,cX,cY)
|
||||
return self
|
||||
def copy(self,bez2):
|
||||
try:
|
||||
self.fixed=XY().copy(bez2.fixed)
|
||||
self.prev = None if bez2.prev == None else XY().copy(bez2.prev)
|
||||
self.next = None if bez2.next == None else XY().copy(bez2.next)
|
||||
except Exception:
|
||||
gimp.message(str(Exception))
|
||||
return self
|
||||
def arrXY(self):
|
||||
pts=[]
|
||||
if self.prev == None:
|
||||
pts+=self.fixed.arrXY(1)
|
||||
else:
|
||||
pts+=self.prev.arrXY(1)
|
||||
pts+=self.fixed.arrXY(1)
|
||||
if self.next==None:
|
||||
pts+=self.fixed.arrXY(1)
|
||||
else:
|
||||
pts+=self.next.arrXY(1)
|
||||
return pts
|
||||
def Prev(self):
|
||||
p = self.prev
|
||||
if p==None: p=self.fixed
|
||||
return p
|
||||
def Next(self):
|
||||
p = self.next
|
||||
if p==None: p=self.fixed
|
||||
return p
|
||||
def Fixed(self):
|
||||
return self.fixed
|
||||
def flip(self):
|
||||
p=self.prev
|
||||
n=self.next
|
||||
self.prev=n
|
||||
self.next=p
|
||||
|
||||
def createSmallArcBez(r, a1, a2,rot):
|
||||
a = (a2 - a1) * 0.5
|
||||
p4 = XY(r * math.cos(a), r * math.sin(a))
|
||||
p1 = XY(p4.x, -p4.y)
|
||||
k = 0.5522847498
|
||||
f = k * math.tan(a)
|
||||
p2 = XY(p1.x + f * p4.y, p1.y + f * p4.x)
|
||||
p3 = XY(p2.x,-p2.y)
|
||||
ar = a + a1
|
||||
P1 = XY(r * math.cos(a1), r * math.sin(a1)).rotate(rot)
|
||||
P2 = XY(p2.x, p2.y).rotate(ar).rotate(rot)
|
||||
P3 = XY(p3.x, p3.y).rotate(ar).rotate(rot)
|
||||
P4 = XY(r * math.cos(a2),r * math.sin(a2)).rotate(rot)
|
||||
B1=bezpnt(P1,None,P2)
|
||||
B2=bezpnt(P4,P3)
|
||||
return [B1,B2]
|
||||
|
||||
def createArcBez(rad, sAng, eAng):
|
||||
EPSILON = 0.0000000001
|
||||
bezs =[]
|
||||
if eAng < sAng:
|
||||
eAng += 360.0
|
||||
sAng = _rads(sAng)
|
||||
eAng = _rads(eAng)
|
||||
rot = sAng
|
||||
sAng = _rads(0)
|
||||
eAng = eAng - rot
|
||||
pi2 = math.pi * 2
|
||||
sAng, eAng = (sAng % pi2, eAng % pi2)
|
||||
pi_2 = math.pi * 0.5
|
||||
sign = 1 if (sAng < eAng) else -1
|
||||
a1 = sAng
|
||||
totAng = min(math.pi * 2, abs(eAng - sAng))
|
||||
while (totAng > EPSILON):
|
||||
a2 = a1 + sign * min(totAng, pi_2)
|
||||
bezs.extend(createSmallArcBez(rad, a1, a2,rot))
|
||||
totAng = totAng - abs(a2 - a1)
|
||||
a1 = a2
|
||||
return bezs
|
||||
|
||||
def bezs2XYList(arc1, transform = None):
|
||||
pnts=[]
|
||||
bezs=[]
|
||||
for aa in arc1:
|
||||
if aa.prev is not None:
|
||||
bezs.append(XY(aa.prev))
|
||||
bezs.append(XY(aa.fixed))
|
||||
|
||||
if aa.next is not None:
|
||||
bezs.append(XY(aa.next))
|
||||
|
||||
for i in range(len(bezs)):
|
||||
v = bezs[i]
|
||||
if transform:
|
||||
v = v + transform
|
||||
if i == 0:
|
||||
pnts.append(v)
|
||||
else:
|
||||
v2=pnts[-1]
|
||||
if (v2.x != v.x or v2.y != v.y):
|
||||
pnts.append(XY(v))
|
||||
a=len(pnts)
|
||||
return pnts
|
||||
|
||||
def XYList(lst, rot = 0.0, add = None):
|
||||
verts=[]
|
||||
for nn in range(len(lst)):
|
||||
v = lst[nn]
|
||||
if rot != 0.0: v = v.rotate(rot)
|
||||
if add: v = v + add
|
||||
verts.append([v.x,v.y])
|
||||
return verts
|
||||
|
||||
def XYListSt(lst, rot = 0.0, add = None):
|
||||
D2 = ""
|
||||
for nn in range(len(lst)):
|
||||
v = lst[nn]
|
||||
if rot != 0.0: v = v.rotate(rot)
|
||||
if add: v = v + add
|
||||
D2 += "%s%s " % (tern(nn==1,"C",""), v.st())
|
||||
return D2
|
||||
|
||||
# circle by quadrants, A: 0>90, B: 90>180, C: 180>270, D: 270>360
|
||||
def circQ(p,r,abcd="ABCD",inverse=0,xtra=None):
|
||||
aa = r * 0.551915024494
|
||||
parts={
|
||||
'A':[XY(0,-r),XY(aa,-r), XY(r, -aa),XY(r,0)],
|
||||
'B':[XY(r,0), XY(r, aa), XY(aa, r),XY(0,r)],
|
||||
'C':[XY(0,r), XY(-aa,r), XY(-r, aa),XY(-r,0)],
|
||||
'D':[XY(-r,0),XY(-r,-aa),XY(-aa,-r),XY(0,-r)]}
|
||||
pA = [XY(p)+N for N in parts[abcd[0]]]
|
||||
for aa in abcd[1:]:
|
||||
pA = pA + [XY(p)+N for N in parts[aa][1:]]
|
||||
if inverse==1: pA.reverse()
|
||||
listA = XYList(pA)
|
||||
if xtra:
|
||||
for n in xtra:
|
||||
listA[n].extend(xtra[n])
|
||||
return listA
|
||||
|
||||
def circleInCircle(c1,r1,c2,r2):
|
||||
return tern((r1 > (c1.hipo(c2) + r2)),True,False)
|
||||
|
||||
def polar2cartesian(cX, cY, rad, ang):
|
||||
return XY(cX + (rad * math.cos(ang)), cY + (rad * math.sin(ang)))
|
||||
|
||||
def setArc(x, y, rad, ang1, ang2, first):
|
||||
start = polar2cartesian(x, y, rad, ang2)
|
||||
end = polar2cartesian(x, y, rad, ang1)
|
||||
arcSweep = "0" if (ang2 - ang1 <= 180) else "1"
|
||||
d = " A%f,%f 0 %s 0 %s" % (rad, rad, arcSweep, end.st())
|
||||
if first == 1:
|
||||
d = "M" + start.st() + " " + d
|
||||
return d
|
||||
|
||||
def setArcs(x, y, rad, ang1, ang2, first):
|
||||
if ang2 < ang1:
|
||||
m = setArc(x, y, rad, 0, _rads(ang2), first)
|
||||
m = m +" "+ setArc(x, y, rad, _rads(ang1), _rads(360), 0)
|
||||
else:
|
||||
m = setArc(x, y, rad, _rads(ang1), _rads(ang2), first)
|
||||
return m
|
||||
|
||||
def addChild(padre, type, props):
|
||||
hijo = etree.SubElement(padre, inkex.addNS(type,'svg'))
|
||||
for n in props:
|
||||
hijo.set(n,props[n])
|
||||
return hijo
|
||||
|
||||
def svgCircle(padre, r, cx, cy):
|
||||
return addChild(padre,'circle' ,{'r':str(r), 'cx': str(cx), 'cy': str(cy)})
|
||||
|
||||
def circleInscribedInTri(p1,p2,p3):
|
||||
d1, d2, d3 = [n.vlength() for n in [XY(p3)-p2, XY(p1)-p3, XY(p2)-p1]]
|
||||
p = d1 + d2 + d3
|
||||
centro = XY( (p1.x*d1 + p2.x*d2 + p3.x*d3) / p, (p1.y*d1 + p2.y*d2 + p3.y*d3) / p)
|
||||
p = p / 2.0
|
||||
radius = math.sqrt(p * (p - d1) * (p - d2) * (p - d3))/p
|
||||
return (radius, centro.x, centro.y)
|
||||
|
||||
def TriInscribedInCircle(p1,p2,p3):
|
||||
x12, x13, x31, x21 = (p1.x - p2.x, p1.x - p3.x, p3.x - p1.x, p2.x - p1.x)
|
||||
y12, y13, y31, y21 = (p1.y - p2.y, p1.y - p3.y, p3.y - p1.y, p2.y - p1.y)
|
||||
sx13 = pow2(p1.x) - pow2(p3.x)
|
||||
sy13 = pow2(p1.y) - pow2(p3.y)
|
||||
sx21 = pow2(p2.x) - pow2(p1.x)
|
||||
sy21 = pow2(p2.y) - pow2(p1.y)
|
||||
f = (sx13*x12 + sy13*x12 + sx21*x13 + sy21*x13) / (2 * (y31*x12 - y21*x13))
|
||||
g = (sx13*y12 + sy13*y12 + sx21*y13 + sy21*y13) / (2 * (x31*y12 - x21*y13))
|
||||
c = - pow2(p1.x) - pow2(p1.y) - 2*g*p1.x - 2*f*p1.y
|
||||
r = math.sqrt(pow2(-g) + pow2(-f) - c)
|
||||
return (r, -g, -f)
|
||||
|
||||
# 243
|
197
extensions/fablabchemnitz/shapes/shapes.inx
Normal file
197
extensions/fablabchemnitz/shapes/shapes.inx
Normal file
@ -0,0 +1,197 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
|
||||
<name>Shapes</name>
|
||||
<id>fablabchemnitz.de.shapes</id>
|
||||
<param name="help-ren" type="description" xml:space="preserve">Create shapes using the bounding box or the node position of the selected objects</param>
|
||||
<param name="tab" type="notebook">
|
||||
<page name="bbox" gui-text="From bounding box">
|
||||
<param type="notebook" name="tab_from_bb">
|
||||
<page name="chamfer" gui-text="From corners">
|
||||
<!--image>shapes-squared-plugin.svg</image-->
|
||||
<hbox>
|
||||
<param type="optiongroup" name="chamfertype" gui-text="Type:">
|
||||
<item value="chamfer">Chamfer</item>
|
||||
<item value="rect">Rect inside</item>
|
||||
<item value="round">Round inside</item>
|
||||
<item value="starcorners">Star</item>
|
||||
<item value="crosscornersquad">Crossed corners quads</item>
|
||||
<item value="crosscornerstri">Crossed corners tris</item>
|
||||
<item value="crosscornersround">Crossed corners round</item>
|
||||
<item value="pillowrect">Pillow</item>
|
||||
<item value="spiralrect">Rect spiral</item>
|
||||
</param>
|
||||
<spacer />
|
||||
<spacer />
|
||||
<spacer />
|
||||
<vbox>
|
||||
<param type="boolean" name="fromCornersInv" gui-text="Inverse (for chamfer and round)">false</param>
|
||||
<param type="float" name="size" min="0.0" max="1000.0" gui-text="Size:">20</param>
|
||||
</vbox>
|
||||
</hbox>
|
||||
</page>
|
||||
<page name="mid" gui-text="From the middle">
|
||||
<!--image>shapes-squared-plugin.svg</image-->
|
||||
<hbox>
|
||||
<param type="optiongroup" name="midtype" gui-text="Type:">
|
||||
<item value="rombus">Rombus</item>
|
||||
<item value="cross">Cross</item>
|
||||
<item value="starcenter">Star</item>
|
||||
<item value="pillowrombus">Pillow</item>
|
||||
</param>
|
||||
<spacer />
|
||||
<spacer />
|
||||
<spacer />
|
||||
<vbox>
|
||||
<param type="float" name="midsize" min="0.0" max="1000.0" gui-text="Size:">20</param>
|
||||
</vbox>
|
||||
</hbox>
|
||||
</page>
|
||||
<page name="spikes" gui-text="Spikes">
|
||||
<hbox>
|
||||
<vbox>
|
||||
<param name="spikestype" type="enum" appearance="minimal" gui-text="Shape:">
|
||||
<item value="tri">Triangle</item>
|
||||
<item value="trirect">Rectangle</item>
|
||||
<item value="squ">Square</item>
|
||||
<item value="rnd">Rounded</item>
|
||||
<item value="wav">Wave</item>
|
||||
</param>
|
||||
<param name="spikesize" type="float" min="0.1" max="1000.0" gui-text="Size:">2.0</param>
|
||||
<param name="spikesep" type="float" min="-1000.0" max="1000.0" gui-text="Distance:">0.0</param>
|
||||
<param name="spikeheight" type="float" min="-1000.0" max="1000.0" gui-text="Height:">0.0</param>
|
||||
</vbox>
|
||||
<vbox>
|
||||
<param name="spikesdir" type="enum" appearance="minimal" gui-text="Direction:" indent="1">
|
||||
<item value="out">Outside</item>
|
||||
<item value="ins">Inside</item>
|
||||
<item value="alt">Alternate</item>
|
||||
</param>
|
||||
<param name="spikesdirt" type="enum" appearance="minimal" gui-text="Top:" indent="1">
|
||||
<item value="pre">Predefined</item>
|
||||
<item value="non">None</item>
|
||||
<item value="out">Outside</item>
|
||||
<item value="ins">Inside</item>
|
||||
<item value="alt">Alternate</item>
|
||||
</param>
|
||||
<param name="spikesdirr" type="enum" appearance="minimal" gui-text="Right:" indent="1">
|
||||
<item value="pre">Predefined</item>
|
||||
<item value="non">None</item>
|
||||
<item value="out">Outside</item>
|
||||
<item value="ins">Inside</item>
|
||||
<item value="alt">Alternate</item>
|
||||
</param>
|
||||
<param name="spikesdirb" type="enum" appearance="minimal" gui-text="Bottom:" indent="1">
|
||||
<item value="pre">Predefined</item>
|
||||
<item value="non">None</item>
|
||||
<item value="out">Outside</item>
|
||||
<item value="ins">Inside</item>
|
||||
<item value="alt">Alternate</item>
|
||||
</param>
|
||||
<param name="spikesdirl" type="enum" appearance="minimal" gui-text="Left:" indent="1">
|
||||
<item value="pre">Predefined</item>
|
||||
<item value="non">None</item>
|
||||
<item value="out">Outside</item>
|
||||
<item value="ins">Inside</item>
|
||||
<item value="alt">Alternate</item>
|
||||
</param>
|
||||
</vbox>
|
||||
</hbox>
|
||||
</page>
|
||||
<page name="triangles" gui-text="Triangles">
|
||||
<param name="tritype" type="optiongroup" gui-text="Triangle type:">
|
||||
<item value="isosceles">Isosceles</item>
|
||||
<item value="equi">Equilateral</item>
|
||||
<item value="rect">Rectangle</item>
|
||||
<item value="trii">From 3 nodes: Inscribed triangle</item>
|
||||
<item value="circi">From 3 nodes: Inscribed circle</item>
|
||||
<item value="circe">From 3 nodes: Bounding circle</item>
|
||||
</param>
|
||||
<param name="trihside" type="boolean" gui-text="Right side aligned">false</param>
|
||||
<param name="trivside" type="boolean" gui-text="Top side aligned">false</param>
|
||||
</page>
|
||||
<page name="arrow" gui-text="Arrows">
|
||||
<param name="arrowtype" type="optiongroup" gui-text="Arrow type:">
|
||||
<item value="arrowfilled">Filled</item>
|
||||
<item value="arrowstick">Stick</item>
|
||||
</param>
|
||||
<param name="headWidth" type="float" min="0.1" max="1000.0" gui-text="Head width:">20.0</param>
|
||||
<param name="headHeight" type="float" min="0.1" max="1000.0" gui-text="Head height:">40.0</param>
|
||||
<param name="arrowWidth" type="float" min="0.1" max="1000.0" gui-text="Tail width:">10.0</param>
|
||||
</page>
|
||||
</param>
|
||||
</page>
|
||||
<page name="extra" gui-text="Join circles">
|
||||
<param name="joincirctype" type="enum" gui-text="Function:">
|
||||
<item value="trapecio">Rect</item>
|
||||
<item value="blob">Blob</item>
|
||||
<item value="oval">Oval</item>
|
||||
</param>
|
||||
<param name="joinradius" type="float" min="0.0" max="1000.0" gui-text="Join radius:">0.0</param>
|
||||
</page>
|
||||
<page name="nodes" gui-text="From nodes">
|
||||
<hbox>
|
||||
<param name="obj" type="enum" gui-text="Add:">
|
||||
<item value="s">Square</item>
|
||||
<item value="c">Circle</item>
|
||||
<item value="number">Numerate</item>
|
||||
<item value="coords">Nodes coordinates</item>
|
||||
<item value="obj">Object</item>
|
||||
</param>
|
||||
<spacer />
|
||||
<spacer />
|
||||
<spacer />
|
||||
<param name="objsize" type="float" min="0.01" max="1000.0" gui-text="Diameter/Side size:">3</param>
|
||||
</hbox>
|
||||
<param name="objid" type="string" gui-text="Object ID"></param>
|
||||
<hbox>
|
||||
<label appearance="header">Position - </label>
|
||||
<param name="posh" type="enum" gui-text="Horizontal:">
|
||||
<item value="0">Center</item>
|
||||
<item value="1">Left</item>
|
||||
<item value="-1">Right</item>
|
||||
</param>
|
||||
<param name="posv" type="enum" gui-text="Vertical:">
|
||||
<item value="0">Center</item>
|
||||
<item value="-1">Bottom</item>
|
||||
<item value="1">Top</item>
|
||||
</param>
|
||||
</hbox>
|
||||
<hbox>
|
||||
<param name="reducey" type="float" min="0.00" max="1000.0" gui-text="Reduce size in Y axis (%):">0.00</param>
|
||||
<param name="ordery" type="boolean" gui-text="Z-index by Y position">false</param>
|
||||
</hbox>
|
||||
<param name="fntsize" type="float" min="0.1" max="100.0" gui-text="Font size (for numerate/coordinates):">10</param>
|
||||
<param name="maxdecimals" type="int" min="1" max="6" gui-text="Number of decimals (for coordinates):">6</param>
|
||||
</page>
|
||||
</param>
|
||||
<hbox>
|
||||
<vbox>
|
||||
<param name="incdec" type="float" min="-1000.0" max="1000.0" gui-text="Increase/decrease size:">0.0</param>
|
||||
</vbox>
|
||||
<vbox>
|
||||
<param name="unit" gui-text="Unit for values:" type="enum" appearance="minimal">
|
||||
<item value="px">px</item>
|
||||
<item value="pt">pt</item>
|
||||
<item value="in">in</item>
|
||||
<item value="cm">cm</item>
|
||||
<item value="mm">mm</item>
|
||||
</param>
|
||||
</vbox>
|
||||
</hbox>
|
||||
<hbox>
|
||||
<param name="squareselection" type="boolean" gui-text="Make the result object square">false</param>
|
||||
<param name="copyfill" type="boolean" gui-text="Copy fill from selected">false</param>
|
||||
</hbox>
|
||||
<param name="deleteorigin" type="boolean" gui-text="Delete origin object">false</param>
|
||||
<effect needs-live-preview="true">
|
||||
<object-type>all</object-type>
|
||||
<effects-menu>
|
||||
<submenu name="FabLab Chemnitz">
|
||||
<submenu name="Shape/Pattern from existing Path(s)" />
|
||||
</submenu>
|
||||
</effects-menu>
|
||||
</effect>
|
||||
<script>
|
||||
<command reldir="inx" interpreter="python">shapes.py</command>
|
||||
</script>
|
||||
</inkscape-extension>
|
766
extensions/fablabchemnitz/shapes/shapes.py
Normal file
766
extensions/fablabchemnitz/shapes/shapes.py
Normal file
@ -0,0 +1,766 @@
|
||||
#!/usr/bin/env python
|
||||
'''
|
||||
shapes_1.py
|
||||
|
||||
Copyright (C) 2015 - 2020 Paco Garcia, www.arakne.es
|
||||
|
||||
2017_07_30: added crossed corners
|
||||
copy class of original object if exists
|
||||
2017_08_09: rombus moved to From corners tab
|
||||
2017_08_17: join circles not need boolen operations now
|
||||
join circles added Oval
|
||||
2017_08_25: fixed error in objects without style
|
||||
|
||||
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 locale, os, sys, tempfile, webbrowser, math, simplepath
|
||||
from lxml import etree
|
||||
|
||||
try:
|
||||
from subprocess import Popen, PIPE
|
||||
bsubprocess = True
|
||||
except:
|
||||
bsubprocess = False
|
||||
|
||||
import inkex
|
||||
from inkex.transforms import BoundingBox
|
||||
|
||||
from arakne_xy import *
|
||||
|
||||
defStyle = [['stroke-width','0.5'],['fill','#f0ff00'],['stroke','#ff0000']]
|
||||
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
|
||||
# ####################################################3
|
||||
|
||||
def calcCircle(pt1, pt2, pt3):
|
||||
D_a = XY(pt2)-pt1
|
||||
D_b = XY(pt3)-pt2
|
||||
m_C = XY()
|
||||
Min = 0.000000001
|
||||
|
||||
m_dRadius= 0
|
||||
|
||||
if (abs(D_a.x) <= Min and abs(D_b.y) <= Min):
|
||||
m_C= XY(0.5*(pt2.x + pt3.x), 0.5*(pt1.y + pt2.y))
|
||||
m_dRadius= vlength(m_C,pt1) # calc. radius
|
||||
|
||||
aSlope = D_a.y / D_a.x
|
||||
|
||||
if D_b.x == 0:
|
||||
bSlope = D_b.y
|
||||
else:
|
||||
bSlope = D_b.y / D_b.x
|
||||
if (abs(aSlope-bSlope) <= Min): # checking if given points are colinear.
|
||||
return [-1,-1,-1]
|
||||
# calc center
|
||||
m_Cx= (aSlope * bSlope * (pt1.y - pt3.y) + bSlope * ( pt1.x + pt2.x ) - aSlope * ( pt2.x + pt3.x ) )/( 2 * ( bSlope - aSlope) )
|
||||
m_Cy = -1*(m_Cx - (pt1.x + pt2.x) / 2 ) / aSlope + (pt1.y + pt2.y)/2
|
||||
v1 = XY(m_Cx,m_Cy).VDist(pt2)
|
||||
return {'c':XY(m_Cx,m_Cy),'r':v1}
|
||||
|
||||
# ####################################################3
|
||||
|
||||
class Shapes(inkex.Effect):
|
||||
def addOpt(self, name, Type=str, Default=""):
|
||||
self.arg_parser.add_argument("--" + name, action="store", type=Type, dest=name, default=Default, help="")
|
||||
|
||||
def __init__(self):
|
||||
inkex.Effect.__init__(self)
|
||||
#sOP = self.OptionParser
|
||||
sOP = self.arg_parser
|
||||
for n in ["tab","chamfertype","midtype","objid","tab_from_bb"] : self.addOpt(n)
|
||||
for n in ["size","midsize","incdec","spikesep","spikeheight","joinradius","objsize","reducey"] : self.addOpt(n, float, 0.0)
|
||||
for n in ["arrowWidth","fntsize"] : self.addOpt(n, float, 10.0)
|
||||
for n in ["tritype", "spikestype","spikesdir","spikesdirt","spikesdirr","spikesdirb","spikesdirl","unit"] : self.addOpt(n)
|
||||
self.addOpt("spikesize", float, "2.0")
|
||||
self.addOpt("arrowtype")
|
||||
self.addOpt("headWidth",float,"20.0")
|
||||
self.addOpt("headHeight",float,"40.0")
|
||||
for n in ["squareselection", "trihside","trivside","copyfill", "fromCornersInv", "deleteorigin"] : self.addOpt(n, inkex.Boolean, "false")
|
||||
sOP.add_argument("--joincirctype", action="store", type=str, dest="joincirctype", default="", help="" )
|
||||
|
||||
# para from nodes
|
||||
for n in ["obj","posh","posv"] : self.addOpt(n)
|
||||
sOP.add_argument("--maxdecimals", action="store", type=int, dest="maxdecimals", default="6", help="" )
|
||||
|
||||
for n in ["ordery", "rotpath"] : self.addOpt(n, inkex.Boolean, "false")
|
||||
|
||||
def getU(self, val):
|
||||
return self.svg.unittouu(str(val)+self.options.unit)
|
||||
|
||||
def addEle(self, ele, parent, props):
|
||||
#elem = inkex.etree.SubElement(parent, ele)
|
||||
elem = etree.SubElement(parent, ele)
|
||||
for n in props: elem.set(n,props[n])
|
||||
return elem
|
||||
|
||||
def chStyles(self,node,sty):
|
||||
style = dict(inkex.Style.parse_str(node.get('style')))
|
||||
for m in list(style):
|
||||
sys.stderr.write(m)
|
||||
for n in sty:
|
||||
if n[0] in style: style.pop(n[0], None)
|
||||
if n[1]!="": style[n[0]]=n[1]
|
||||
node.set('style',inkex.Style(style))
|
||||
|
||||
# def unit2uu(self, val):
|
||||
# if hasattr(self,"unittouu") is True:
|
||||
# return self.svg.unittouu(val)
|
||||
# else:
|
||||
# return inkex.unittouu(val)
|
||||
|
||||
# def u2uu(self,value):
|
||||
# if hasattr(inkex, 'unittouu'):
|
||||
# v = inkex.unittouu(value)
|
||||
# else:
|
||||
# v = self.unittouu(value)
|
||||
# return v
|
||||
|
||||
def limits(self, node):
|
||||
s = node.bounding_box()
|
||||
incdec = self.getU(self.options.incdec)
|
||||
l,r,t,b,an,al = (s.left-incdec, s.right+incdec, s.top-incdec, s.bottom+incdec, s.width+incdec*2, s.height+incdec*2)
|
||||
return (l,r,t,b,an,al)
|
||||
|
||||
def copyProp(self, orig, dest, prop):
|
||||
if orig.get(prop):
|
||||
dest.set(prop, orig.get(prop))
|
||||
|
||||
def estilo(self, nn, orig, style=defStyle):
|
||||
if self.options.copyfill:
|
||||
self.copyProp(orig, nn, 'style')
|
||||
self.copyProp(orig, nn, 'class')
|
||||
else:
|
||||
self.chStyles(nn,style)
|
||||
|
||||
def circleABCD(self,p,r,abcd="ABCD",inverse=False,xtra=None):
|
||||
aa = r * 0.551915024494
|
||||
parts={
|
||||
'A':[XY(0,-r),XY(aa,-r), XY(r, -aa),XY(r,0)],
|
||||
'B':[XY(r,0), XY(r, aa), XY(aa, r),XY(0,r)],
|
||||
'C':[XY(0,r), XY(-aa,r), XY(-r, aa),XY(-r,0)],
|
||||
'D':[XY(-r,0),XY(-r,-aa),XY(-aa,-r),XY(0,-r)]}
|
||||
pA = [XY(p)+N for N in parts[abcd[0]]]
|
||||
for aa in abcd[1:]:
|
||||
pA = pA + [XY(p)+N for N in parts[aa][1:]]
|
||||
if inverse==True: pA.reverse()
|
||||
listA = XYList(pA)
|
||||
if xtra:
|
||||
for n in xtra:
|
||||
listA[n].extend(xtra[n])
|
||||
return listA
|
||||
|
||||
# def getMed(self, id):
|
||||
# #query inkscape about the bounding box of obj
|
||||
# q = {'width':0,'height':0}
|
||||
# file = self.args[-1]
|
||||
# scale = self.unittouu('1px') # convert to document units
|
||||
# for query in q.keys():
|
||||
# ss = 'inkscape --query-%s --query-id=%s "%s"' % (query,id,file)
|
||||
# if bsubprocess:
|
||||
# info('bsubprocess')
|
||||
# p = Popen(ss, shell=True, stdout=PIPE, stderr=PIPE)
|
||||
# rc = p.wait()
|
||||
# aaa = p.stdout.read()
|
||||
# q[query] = aaa
|
||||
# err = p.stderr.read()
|
||||
# else:
|
||||
# f,err = os.popen3(ss)[1:]
|
||||
# q[query] = scale * float(f.read())
|
||||
# f.close()
|
||||
# err.close()
|
||||
# return q
|
||||
|
||||
def pillows(self, type, a, node, l, t, r, b, cX, cY):
|
||||
pts = []
|
||||
if type=="pillowrect": cnrs=[XY(l,t), XY(r,t), XY(r,b), XY(l,b)]
|
||||
if type=="pillowrombus": cnrs=[XY(l,t+cY), XY(l+cX,t), XY(r,t+cY), XY(r-cX,b)]
|
||||
aa = a
|
||||
for n in range(0,len(cnrs)-1):
|
||||
pts.append(cnrs[n])
|
||||
pts.append(XY(cnrs[n]).atMid(cnrs[n+1]) + XY(0,aa).rot(cnrs[n].getAngle(cnrs[n+1])))
|
||||
n=len(cnrs)-1
|
||||
pts.append(cnrs[n])
|
||||
|
||||
pM = XY(cnrs[n]).atMid(cnrs[0]) + XY(0,aa).rot(cnrs[n].getAngle(cnrs[0]))
|
||||
pts.append(pM)
|
||||
|
||||
s = ''
|
||||
|
||||
for n in range(0,int(len(pts)/2)-1):
|
||||
nnA = calcCircle(pts[n*2], pts[n*2+1], pts[n*2 + 2])
|
||||
s = s + ' ' + setArc(nnA['c'].x, nnA['c'].y, nnA['r'], nnA['c'].getAngle(pts[n*2+2]), nnA['c'].getAngle(pts[n*2]), 1 if n==0 else 0)
|
||||
n = len(pts)
|
||||
nnA = calcCircle(pts[n-2], pts[n-1], pts[0])
|
||||
s = s + ' ' + setArc(nnA['c'].x, nnA['c'].y, nnA['r'], nnA['c'].getAngle(pts[0]), nnA['c'].getAngle(pts[n-2]), 0)
|
||||
shp = addChild(node.getparent(), 'path',{'d': s+" Z"})
|
||||
self.estilo(shp,node)
|
||||
|
||||
def draw(self, node, sh='rombus'):
|
||||
if (node.tag == inkex.addNS('text','svg')):
|
||||
return
|
||||
sO = self.options
|
||||
l, r, t, b, an, al = self.limits(node)
|
||||
sqSel = sO.squareselection
|
||||
tInv = sO.fromCornersInv
|
||||
copyfill = sO.copyfill
|
||||
deleteorigin = sO.deleteorigin
|
||||
|
||||
side = min(al,an)
|
||||
if sqSel:
|
||||
incx=(an-side)/2.0
|
||||
l,r,an =(l+incx,r-incx,side)
|
||||
incy=(al-side)/2.0
|
||||
t +=incy
|
||||
b -=incy
|
||||
al = side
|
||||
cX, cY = (an/2.0,al/2.0)
|
||||
sub_bb = sO.tab_from_bb
|
||||
pp = node.getparent()
|
||||
varBez = 0.551915024494
|
||||
|
||||
a = self.getU(sO.size) if sub_bb=="chamfer" else self.getU(sO.midsize)
|
||||
|
||||
a_2, a2 = (a / 2.0,a * 2.0)
|
||||
dS = "m %sz"
|
||||
pnts = [[l+cX,t],[cX,cY],[-cX,cY],[-cX,-cY]]
|
||||
aa = a * varBez
|
||||
chtype = sO.chamfertype
|
||||
midtype = sO.midtype
|
||||
|
||||
an2, al2 = ((an-a)/2.0,(al-a)/2.0)
|
||||
tritype = sO.tritype
|
||||
if sh == 'bbox':
|
||||
if midtype=="rombus" and a>0: pnts=[[l+cX - a_2,t],[a,0],[an2,al2],[0,a],[-an2,al2],[-a,0],[-an2,-al2],[0,-a]]
|
||||
if (sub_bb=='mid'):
|
||||
if midtype=="chamfer":
|
||||
if tInv==False:
|
||||
pnts=[[l+a,t],[an - a2,0],[a,a],[0,al-a2],[-a,a],[-(an - a2),0],[-a,-a],[0,-(al-a2)]]
|
||||
else:
|
||||
pnts=[[l,t],[a,0],[-a,a],[an-a,0," z m"],[a,0],[0,a],[a,al," z m"],[0,-a],[-a,a],[-an+a,0," z m"],[-a,-a],[0,a]]
|
||||
if midtype=="cross":
|
||||
pnts=[[l+an2,t],[a,0],[0,al2],[an2,0],[0,a],[-an2,0],[0,al2],[-a,0],[0,-al2],[-an2,0],[0,-a],[an2,0]]
|
||||
if midtype=="starcenter":
|
||||
pnts=[[l+cX,t],[a_2,al2], [an2,a_2], [-an2,a_2],[-a_2,al2],[-a_2,-al2],[-an2,-a_2],[an2,-a_2]]
|
||||
|
||||
if midtype=="pillowrombus":
|
||||
self.pillows(midtype, a, node, l, t, r, b, cX, cY)
|
||||
pnts = []
|
||||
if deleteorigin: node.getparent().remove(node)
|
||||
|
||||
if (sub_bb=='chamfer'):
|
||||
if chtype=="chamfer":
|
||||
if tInv==False:
|
||||
pnts=[[l+a,t],[an - a2,0],[a,a],[0,al-a2],[-a,a],[-(an - a2),0],[-a,-a],[0,-(al-a2)]]
|
||||
else:
|
||||
pnts=[[l,t],[a,0],[-a,a],[an-a,0," z m"],[a,0],[0,a],[a,al," z m"],[0,-a],[-a,a],[-an+a,0," z m"],[-a,-a],[0,a]]
|
||||
if chtype=="round":
|
||||
if tInv==False:
|
||||
pnts = circQ(XY(l,t),a,"B",0,{1:"C"}) + circQ(XY(l,b),a,"A",0,{0:"L",1:"C"}) + circQ(XY(r,b),a,"D",0,{0:"L",1:"C"}) + circQ(XY(r,t),a,"C",0,{0:"L",1:"C"})
|
||||
else:
|
||||
pnts=[[l,t],[a,0],[0,aa,"c "],[-aa,a],[-a,a],[an-a,0,"z m "],[a,0],[0,a],[-aa,0," c"],[-a,-aa],[-a,-a],
|
||||
[a,al-a,"z m "],[0,a],[-a,0],[0,-aa,"c "],[aa,-a],[a,-a],[-an,0,"z m "],[0,a],[a,0],[0,-aa,"c "],[-aa,-a],[-a,-a]]
|
||||
if chtype=="roundinv":
|
||||
pnts=[[l,t],[a,0],[0,aa,"c "],[-aa,a],[-a,a],[an-a,0,"z m "],[a,0],[0,a],[-aa,0," c"],[-a,-aa],[-a,-a],
|
||||
[a,al-a,"z m "],[0,a],[-a,0],[0,-aa,"c "],[aa,-a],[a,-a],[-an,0,"z m "],[0,a],[a,0],[0,-aa,"c "],[-aa,-a],[-a,-a]]
|
||||
if chtype=="rect":
|
||||
pnts=[[l+a,t],[an - a2,0],[0,a],[a,0],[0,al-a2],[-a,0],[0,a],[-(an-a2),0],[0,-a],[-a,0],[0,-(al-a2)],[a,0]]
|
||||
if chtype=="starcorners":
|
||||
pnts=[[l,t],[cX,al2],[cX,-al2],[-an2,cY],[an2,cY],[-cX,-al2],[-cX,al2],[an2,-cY]]
|
||||
if chtype=="crosscornersquad":
|
||||
pnts=[[l-a,t],[0,-a],[a,0],[0,al+a*2],[-a,0],[0,-a],[an+a*2,0],[0,a],[-a,0],[0,-al-a*2],[a,0],[0,a]]
|
||||
if chtype=="crosscornerstri":
|
||||
pnts=[[l-a,t], [a,-a], [0,al+a*2], [-a,-a], [an+a*2,0], [-a,a], [0,-al-a*2],[a,a]]
|
||||
if chtype=="crosscornersround":
|
||||
dS = "M %sZ"
|
||||
aa2 = a_2 * varBez
|
||||
p1 = circQ(XY(r + a_2, t - a_2),a_2,"DAB",1)
|
||||
p2 = circQ(XY(r + a_2, b + a_2),a_2,"ABC",1)
|
||||
p3 = circQ(XY(l - a_2, b + a_2),a_2,"BCD",1)
|
||||
p4 = circQ(XY(l - a_2, t - a_2),a_2,"CDA",1)
|
||||
pnts = p1 + [[r,t],[r,b+a_2-aa2]] + p2 + [[r+a_2-aa2,b],[l-a_2+aa2,b]] + p3 + [[l,b+a_2-aa],[l,t-a_2+aa]] + p4
|
||||
pnts[1].append(" C")
|
||||
|
||||
if chtype=="pillowrect":
|
||||
pts = []
|
||||
self.pillows(chtype, a, node, l, t, r, b, cX, cY)
|
||||
pnts = []
|
||||
dS = "M %sZ"
|
||||
if deleteorigin: node.getparent().remove(node)
|
||||
if chtype == "spiralrect":
|
||||
An, Al = (an, al)
|
||||
pnts = [[l,t], [An,0], [0,Al], [-An,0], [0,-Al+a]]
|
||||
An = An - a
|
||||
Al = Al - a*2
|
||||
tot = min(An//a,Al//a) // 2 + 1
|
||||
|
||||
for n in range(0,int(tot)):
|
||||
pnts.append([An,0])
|
||||
An = An-a
|
||||
if Al>a:
|
||||
pnts.append([0,Al])
|
||||
Al=Al-a
|
||||
else:
|
||||
break
|
||||
if An>a:
|
||||
pnts.extend([[-An,0]])
|
||||
An = An-a
|
||||
else:
|
||||
break
|
||||
if Al>0:
|
||||
pnts.extend([[0, -Al]])
|
||||
Al=Al-a
|
||||
else:
|
||||
break
|
||||
|
||||
# ________________
|
||||
# ______________ |
|
||||
# | __________ | |
|
||||
# | |____________| |
|
||||
# |________________|
|
||||
|
||||
defStyle = [['stroke-width','2.5'],['fill','none'],['stroke','#ff0000']]
|
||||
dS = "m %s"
|
||||
|
||||
if (sub_bb=='spikes'): pnts = self.triSpikes(sO, an, al, l, t)
|
||||
|
||||
if sub_bb=='arrow':
|
||||
pnts = self.drawArrow(sO, an, al, l, t)
|
||||
if sO.arrowtype=="arrowstick":
|
||||
dS = "m %s"
|
||||
|
||||
if sub_bb=='triangles':
|
||||
trihside, trivside=(sO.trihside, sO.trivside)
|
||||
if tritype=="isosceles": pnts=[[l+cX,t],[cX,al],[-an,0]]
|
||||
if tritype=="equi":
|
||||
sqrt3 = 1.7320508075
|
||||
height = sqrt3/2 * side
|
||||
tcx, tcy = ((an - side)/2.0, (al - height)/2.0)
|
||||
pnts=[[cX+l,t+tcy],[an/2.0-tcx,height],[-side,0]]
|
||||
if tritype=="rect":
|
||||
x1 = l + tern(not trivside and trihside,an,0)
|
||||
x2 = tern(not trivside and trihside,0,an)
|
||||
x3 = tern(trivside and trihside,0,-an)
|
||||
pnts=[[x1,t], [x2,tern(not trivside,al,0)], [x3,tern(not trivside,0,al)]]
|
||||
# #######################################
|
||||
if tritype=="circi" or tritype=="circe" or tritype=="trii":
|
||||
# get verts
|
||||
pnts = []
|
||||
if node.get('d'):
|
||||
p = node.path.to_superpath().to_path().to_arrays()
|
||||
vs=[]
|
||||
for cmd, params in p:
|
||||
if cmd != 'Z' and cmd != 'z':
|
||||
vs.append(XY(params[-2],params[-1]))
|
||||
if len(vs)>2:
|
||||
if tritype == "trii":
|
||||
p1 = XY(vs[0]) + (XY(vs[1])-vs[0]).div(2)
|
||||
p2 = XY(vs[1]) + (XY(vs[2])-vs[1]).div(2)
|
||||
p3 = XY(vs[2]) + (XY(vs[0])-vs[2]).div(2)
|
||||
pnts = [p3.co,(p1-p3).co,((p2-p1)-p3).co]
|
||||
if tritype == "circi" or tritype == "circe":
|
||||
if tritype == "circi":
|
||||
rad, px, py = circleInscribedInTri(vs[0], vs[1], vs[2])
|
||||
if tritype == "circe":
|
||||
rad, px, py = TriInscribedInCircle(vs[0], vs[1], vs[2])
|
||||
nn = svgCircle(node.getparent(), rad, px, py)
|
||||
self.estilo(nn,node)
|
||||
pnts=[]
|
||||
if deleteorigin: node.getparent().remove(node)
|
||||
if sh=='nodes':
|
||||
# get verts
|
||||
obj, posh, posv, objS, oY =(sO.obj, int(sO.posh), int(sO.posv), sO.objsize, sO.ordery)
|
||||
reducey = sO.reducey
|
||||
o2 = objS/2
|
||||
pnts = []
|
||||
orderY = []
|
||||
if node.get('d'):
|
||||
p = node.path.to_arrays()
|
||||
vs = []
|
||||
minY, maxY, prevX, prevY = (100000.0, -100000.0, 0, 0)
|
||||
for cmd, params in p:
|
||||
if cmd != 'Z' and cmd != 'z':
|
||||
posY = prevY
|
||||
posX = prevX
|
||||
posY = params[-1]
|
||||
if cmd in ['h','H']:
|
||||
posY = prevY
|
||||
posX = params[-1]
|
||||
elif cmd not in ['v','V','h','H']:
|
||||
posX = params[-2]
|
||||
vs.append(XY(posX, posY))
|
||||
prevX, prevY, minY, maxY = (posX, posY, min(posY, minY), max(posY, maxY))
|
||||
objs = []
|
||||
dist = maxY - minY
|
||||
grp = addChild(node.getparent(), 'g',{})
|
||||
self.copyProp(node, grp, 'transform')
|
||||
self.estilo(grp,node)
|
||||
|
||||
if obj == "obj":
|
||||
oi = sO.objid
|
||||
este = self.svg.getElementById('%s' % oi)
|
||||
if este == None:
|
||||
obj='c'
|
||||
else:
|
||||
l1, r1, t1, b1, an1, al1 = self.limits(este)
|
||||
w2, h2 = (an1/2 , al1/2)
|
||||
for n in range(0,len(vs)):
|
||||
if obj == "number":
|
||||
nn = self.addTxt(grp, str(vs[n].x), str(vs[n].y), str(n))
|
||||
if obj=="obj":
|
||||
if este != None:
|
||||
reduce = (100 - (reducey * ((maxY - vs[n].y) / dist)))/100
|
||||
px = str(vs[n].x / reduce - l1 - w2 + w2 * posh)
|
||||
py = str(vs[n].y / reduce - t1 - h2 + h2 * posv)
|
||||
nn = addChild(grp,'use',{inkex.addNS('href',"xlink"):"#"+oi,'x':px,'y':py, "transform":"scale(%f)" % (reduce)})
|
||||
else:
|
||||
obj='c'
|
||||
reduce = (o2 / 100 * reducey) * (maxY - vs[n].y) / dist
|
||||
O2 = o2 - reduce
|
||||
if obj=="c":
|
||||
pxy = vs[n] + XY(posh, posv).mul(O2)
|
||||
nn = svgCircle(grp, O2, pxy.x, pxy.y)
|
||||
if obj=="s":
|
||||
pxy = vs[n] - XY(O2) + XY(O2 * posh, O2 * posv)
|
||||
nn = addChild(grp,'rect',{'height':str(O2*2), 'width':str(O2*2), 'x':str(pxy.x), 'y':str(pxy.y)})
|
||||
if obj=="number":
|
||||
nn = self.addTxt(grp, str(vs[n].x), str(vs[n].y), str(n))
|
||||
if obj=="coords":
|
||||
maxDec=sO.maxdecimals
|
||||
nn = self.addTxt(grp, str(vs[n].x), str(vs[n].y), str(round(vs[n].x, maxDec)) + "," + str(round(vs[n].y, maxDec)))
|
||||
orderY.append([vs[n].y,nn])
|
||||
#self.estilo(nn,node)
|
||||
if sO.ordery:
|
||||
def myFunc(e):
|
||||
return e[0]
|
||||
orderY.sort(key=myFunc)
|
||||
for item in orderY:
|
||||
grp.append( item[1])
|
||||
if deleteorigin: node.getparent().remove(node)
|
||||
# ##############################3
|
||||
|
||||
d = ""
|
||||
if len(pnts)>0:
|
||||
for n in pnts:
|
||||
ss = "" if len(n)<3 else n[2]
|
||||
d += "%s%s,%s " % (ss, str(n[0]),str(n[1]))
|
||||
nn = self.addEle('path',pp, {'d':dS % (d)})
|
||||
self.estilo(nn,node)
|
||||
if deleteorigin: node.getparent().remove(node)
|
||||
|
||||
def makeRel(self,arr):
|
||||
b = arr[:]
|
||||
for n in range(1,len(arr)):
|
||||
s = b[n]
|
||||
for i in range(0,n):
|
||||
s = s - arr[i]
|
||||
b[n] = s
|
||||
return b
|
||||
|
||||
def circle(self,p,r):
|
||||
varBez = 0.551915024494
|
||||
dS = "m %s"
|
||||
aa = r * varBez
|
||||
d=""
|
||||
pnts=[[p.x - r,p.y],[0,aa,"c "],[r - aa,r],[r,r],[aa,0,"c "],[r,-r+aa],[r,-r],[0,-aa,"c "],[-r+aa,-r],[-r,-r],[-aa,0,"c "],[-r,r-aa],[-r, r]]
|
||||
for n in pnts:
|
||||
ss = "" if len(n)<3 else n[2]
|
||||
d += "%s%s,%s " % (ss, str(n[0]),str(n[1]))
|
||||
return d
|
||||
|
||||
def addTxt(self, node, x, y, text, dy = 0):
|
||||
new2 = self.addEle(inkex.addNS('text','svg'), node,{'x':str(x),'y':str(y)})
|
||||
new = etree.SubElement(new2, inkex.addNS('tspan','svg'), {inkex.addNS('role','sodipodi'): 'line'})
|
||||
new.set('style','text-align:center; vertical-align:bottom; font-size:%s; fill-opacity:1.0; stroke:none; font-weight:normal; font-style:normal; fill:#000000' % self.options.fntsize)
|
||||
new.set('dy', str(dy))
|
||||
new.text = str(text)
|
||||
return new2
|
||||
|
||||
def circsCone(self, sels, sh='rombus'):
|
||||
sO = self.options
|
||||
copyfill = sO.copyfill
|
||||
deleteorigin = sO.deleteorigin
|
||||
joincirctype = sO.joincirctype
|
||||
r2 = sO.joinradius
|
||||
cssEmpty = [['stroke-width','0.5'],['fill','none'],['stroke','#ff0000']]
|
||||
|
||||
strEmpty = 'stroke-width:0.02; fill:none; stroke:#000000; stroke-dasharray:0.5,0.2; stroke-dashoffset:0;'
|
||||
|
||||
for nodos in range(len(sels)-1):
|
||||
node = sels[nodos]
|
||||
node2 = sels[nodos+1]
|
||||
lA, rA, tA, bA, anA, alA = self.limits(node)
|
||||
lB, rB, tB, bB, anB, alB = self.limits(node2)
|
||||
rA, cY = (anA/2.0,alA/2.0)
|
||||
rB, cY2 = (anB/2.0,alB/2.0)
|
||||
|
||||
PtA = XY(lA + rA, tA + cY)
|
||||
PtB = XY(lB + rB, tB + cY2)
|
||||
|
||||
if (circleInCircle(PtA,rA,PtB,rB) or circleInCircle(PtB,rB,PtA,rA)):
|
||||
pass
|
||||
else:
|
||||
pp = node.getparent()
|
||||
rotAB = XY(PtB).getAngle(PtA)
|
||||
dist = PtA.hipo(PtB)
|
||||
if joincirctype=='trapecio':
|
||||
# alineamos las esferas en Y
|
||||
rDif = rA - rB
|
||||
Axis = XY(-rDif,0)
|
||||
#D2 = math.sqrt((dist*dist) - (rDif*rDif)) / dist
|
||||
D2 = triCat(dist, rDif) / dist
|
||||
P1 = XY(Axis).mul(rA / dist)
|
||||
P2 = XY(-dist,0) + XY(Axis).mul(rB / dist)
|
||||
r = P1.VDist(P2)
|
||||
Rot1 = XY(P2.x,rB * D2).getAngleD(XY(P2.x + r, rA * D2))
|
||||
aBez=createArcBez(rA,-90 -Rot1, -270 + Rot1)
|
||||
curva1a = bezs2XYList(aBez)
|
||||
d = XYListSt(curva1a, rotAB, PtA)
|
||||
|
||||
pnts2 = bezs2XYList(createArcBez(rB, 90 + Rot1, 270 - Rot1),XY(-dist,0))
|
||||
d2 = XYListSt(pnts2, rotAB, PtA)
|
||||
nn = self.addEle('path',pp, {'d':"M%s L%sZ" % (d,d2)})
|
||||
self.estilo(nn,node)
|
||||
# ################## B L O B ##############
|
||||
if joincirctype=='blob':
|
||||
if ((r2==0) and (dist<(rA+rB))):
|
||||
r2 = dist - rB
|
||||
if (r2 > 0):
|
||||
rad1 = rA + r2
|
||||
rad2 = rB + r2
|
||||
a = ( pow2(dist) - pow2(rB+r2) + pow2(rA+r2))/(dist*2)
|
||||
else:
|
||||
r2 = dist - rA - rB
|
||||
rad1 = dist - rB
|
||||
rad2 = dist - rA
|
||||
a = (pow2(dist-rB) - pow2(dist-rA) + pow2(dist))/(dist*2);
|
||||
# alineamos las esferas en Y
|
||||
|
||||
rt = math.atan2(PtB.y - PtA.y, PtB.x - PtA.x)
|
||||
# # distancia del centro 1 a la interseccion de los circulos
|
||||
x = (dist * dist - rad2 * rad2 + rad1 * rad1) / (dist*2)
|
||||
if (rad1 * rad1 - x * x) > 0 :
|
||||
#catB = math.sqrt(rad1 * rad1 - x * x)
|
||||
catB = triCat(rad1, x)
|
||||
|
||||
rt = math.degrees(XY(0,0).getAngle(XY(-x, -catB)))
|
||||
rt2 = math.degrees(XY(0,0).getAngle(XY(-(dist - x), -catB)))
|
||||
|
||||
curva1 = bezs2XYList(createArcBez(rA, rt, -rt))
|
||||
curva1.reverse()
|
||||
curva2 = bezs2XYList(createArcBez(r2, -180 + rt, -rt2),XY(-x, -catB))
|
||||
curva3 = bezs2XYList(createArcBez(rB, rt2+180,180-rt2),XY(-dist, 0))
|
||||
curva3.reverse()
|
||||
curva4 = bezs2XYList(createArcBez(r2, rt2, 180 - rt),XY(-x, catB))
|
||||
|
||||
curva1= curva1+curva2[1:]+curva3[1:]+curva4[1:]
|
||||
sCurva1 = XYListSt(curva1, rotAB, PtA)
|
||||
|
||||
nn = self.addEle('path',pp,{'d':"M %s" % (sCurva1)})
|
||||
self.estilo(nn,node)
|
||||
# ################################################
|
||||
# ################## O V A L #####################
|
||||
# ################################################
|
||||
if joincirctype=='oval':
|
||||
minR2 = dist + min(rA,rB)
|
||||
if r2 < minR2:
|
||||
r2 = minR2
|
||||
info('Changed radius to '+str(minR2))
|
||||
rad1 = r2 - rA
|
||||
rad2 = r2 - rB
|
||||
a = ( pow2(dist) - pow2(rB+r2) + pow2(rA+r2))/(dist*2)
|
||||
|
||||
rt = math.atan2(PtB.y - PtA.y, PtB.x - PtA.x)
|
||||
D = dist #XY(PtA).sub(PtB).vlength() # distancia entre los centros
|
||||
# distancia del centro 1 a la interseccion de los circulos
|
||||
x = (D*D - rad2 * rad2 + rad1 * rad1) / (D*2)
|
||||
# catB = math.sqrt(rad1 * rad1 - x * x)
|
||||
catB = triCat(rad1, x)
|
||||
|
||||
rotAB=XY(PtB).getAngle(PtA)
|
||||
rot1 = math.degrees(XY(0,0).getAngle(XY(-x,-catB))) + 180.0
|
||||
curva1 = bezs2XYList(createArcBez(rA, -rot1, rot1))
|
||||
curva1.reverse()
|
||||
#rot2 = math.degrees(XY(-dist,0).getAngle(XY(-x,-catB))) +180.0
|
||||
rot2 = XY(-dist,0).getAngleD(XY(-x,-catB)) +180.0
|
||||
curva2 = bezs2XYList(createArcBez(r2, -rot2,-rot1),XY(-x,catB))
|
||||
curva2.reverse()
|
||||
curva3 = bezs2XYList(createArcBez(rB, rot2,-rot2),XY(-dist,0))
|
||||
curva3.reverse()
|
||||
curva4 = bezs2XYList(createArcBez(r2, rot1,rot2),XY(-x,-catB))
|
||||
curva4.reverse()
|
||||
curva1= curva1+curva2[1:]+curva3[1:]+curva4[1:] #+curva3[1:]+curva4[1:]
|
||||
sCurva1 = XYListSt(curva1, rotAB, PtA)
|
||||
# curva1
|
||||
nn = self.addEle('path',pp,{'d':"M %sZ" % (sCurva1),'style':'stroke-width:0.02;fill:#cc0000;stroke:#000000;'})
|
||||
self.estilo(nn,node)
|
||||
if deleteorigin: node.getparent().remove(node)
|
||||
|
||||
def drawArrow(self, sO, an, al, l, t):
|
||||
arrowType = sO.arrowtype
|
||||
headH, headW, arrowW = (self.getU(sO.headHeight), self.getU(sO.headWidth), self.getU(sO.arrowWidth))
|
||||
hw2=headW/2.0
|
||||
cX = an/2.0
|
||||
if arrowType=="arrowfilled":
|
||||
pnts=[[l+cX,t],[hw2,headH],[-(headW-arrowW)/2.0,0],[0,al-headH],[-arrowW,0],[0,-(al-headH)],[-(headW-arrowW)/2.0,0]]
|
||||
else:
|
||||
#dS = "m %s"
|
||||
pnts=[[l+cX,t],[0,al],[-hw2,-al+headH,"m "],[hw2,-headH],[hw2,headH]]
|
||||
return pnts
|
||||
|
||||
def triSpikes(self, sO, an, al, l, t):
|
||||
spktype, spikesdir, sh, ssep = (sO.spikestype, sO.spikesdir, sO.spikeheight, self.getU(sO.spikesep))
|
||||
ss = self.getU(sO.spikesize)
|
||||
anX, anY = (int( (an + ssep) / (ss * 2 + ssep)), int( (al+ssep) / (ss * 2 + ssep)))
|
||||
iniX, iniY = (((an+ssep) - (anX * (ss * 2 + ssep))) / 2.0, ((al+ssep) - (anY * (ss * 2 + ssep))) / 2.0)
|
||||
if spktype=="trirect" or spktype=="squ":
|
||||
anX, anY = ( int((an + ssep) / (ss + ssep)), int((al + ssep) / (ss + ssep)) )
|
||||
iniX, iniY = (((an + ssep) - (anX * (ss + ssep))) / 2.0, ((al + ssep) - (anY * (ss + ssep))) / 2.0)
|
||||
dir = 1
|
||||
pnts = [[l,t],[iniX,0]]
|
||||
|
||||
if spikesdir=='ins': dir = -1.0
|
||||
#if spktype=="tri" or spktype=="trirect" or spktype=="squ":
|
||||
if spktype in ["tri", "trirect", "squ"]:
|
||||
sDir = sO.spikesdirt # --------------------------------TOP---------------------
|
||||
if sDir=='pre': sDir = spikesdir
|
||||
if sDir=='non':
|
||||
pnts = [[l,t],[an,0]]
|
||||
else :
|
||||
dirT = 1
|
||||
if sDir=='ins': dirT = -1.0
|
||||
for n in range(anX):
|
||||
if sDir=='alt' : dirT = 1 if n % 2 == 1 else -1
|
||||
if spktype=="tri": pnts.extend([[ss,-sh*dirT],[ss,sh*dirT]])
|
||||
if spktype=="trirect": pnts.extend([[0,-sh*dirT],[ss,sh*dirT]])
|
||||
if spktype=="squ": pnts.extend([[0,-sh*dirT],[ss,0],[0,sh*dirT]])
|
||||
if ssep != 0 and n < (anX-1): pnts.append([ssep,0])
|
||||
pnts.append([iniX,0])
|
||||
sDir = sO.spikesdirr # ---------------------------------RIGHT-------------------
|
||||
if sDir == 'pre' : sDir = spikesdir
|
||||
if sDir == 'non' :
|
||||
pnts.append([0,al])
|
||||
else :
|
||||
pnts.append([0,iniY])
|
||||
dirR = -1.0 if sDir=='ins' else 1.0
|
||||
for n in range(anY):
|
||||
if sDir == 'alt' : dirR = 1 if n % 2 == 1 else -1
|
||||
if spktype=="tri": pnts.extend([[sh*dirR,ss],[-sh * dirR,ss]])
|
||||
if spktype=="trirect": pnts.extend([[sh*dirR,0],[-sh * dirR,ss]])
|
||||
if spktype=="squ": pnts.extend([[sh*dirR,0],[0,ss],[-sh * dirR,0]])
|
||||
if ssep != 0 and n < (anY-1): pnts.append([0, ssep])
|
||||
pnts.append([0,iniY])
|
||||
sDir = sO.spikesdirb # -------------------------------BOTTOM--------------------
|
||||
if sDir == 'pre' : sDir = spikesdir
|
||||
if sDir == 'non' :
|
||||
pnts.append([-an,0])
|
||||
else :
|
||||
pnts.append([-iniX,0])
|
||||
dirB = -1.0 if sDir=='ins' else 1.0
|
||||
for n in range(anX):
|
||||
if sDir == 'alt' : dirB = 1 if n % 2 == 1 else -1
|
||||
if spktype=="tri": pnts.extend([[-ss,sh*dirB],[-ss,-sh*dirB]])
|
||||
if spktype=="trirect": pnts.extend([[0,sh*dirB],[-ss,-sh*dirB]])
|
||||
if spktype=="squ": pnts.extend([[0,sh*dirB],[-ss,0],[0,-sh*dirB]])
|
||||
if ssep != 0 and n < (anX-1): pnts.append([-ssep,0])
|
||||
pnts.append([-iniX,0])
|
||||
sDir = sO.spikesdirl # --------------------------------------LEFT---------------
|
||||
if sDir == 'pre' : sDir = spikesdir
|
||||
if sDir != 'non' :
|
||||
pnts.append([0,-iniY])
|
||||
dirL = -1.0 if sDir=='ins' else 1.0
|
||||
#sDir = sO.spikesdir
|
||||
for n in range(anY):
|
||||
if sDir == 'alt' : dirL = 1 if n % 2 == 1 else -1
|
||||
#pnts.extend([[-sh*dirL,-ss],[sh*dirL,-ss]])
|
||||
if spktype=="tri": pnts.extend([[-sh*dirL,-ss],[sh*dirL,-ss]])
|
||||
if spktype=="trirect": pnts.extend([[-sh*dirL,0], [sh*dirL,-ss]])
|
||||
if spktype=="squ": pnts.extend([[-sh*dirL,0], [0,-ss],[sh * dirL,0]])
|
||||
if ssep != 0 and n < (anY-1): pnts.append([0, -ssep])
|
||||
###########################################
|
||||
varBez = 0.551915024494
|
||||
if spktype in ["rnd", "wav"]:
|
||||
dif, difh, dBez, dBezh = (ss-(ss*varBez), sh-(sh*varBez), ss*varBez, sh*varBez)
|
||||
sDir = sO.spikesdirt # --------------------------------TOP---------------------
|
||||
if sDir=='pre': sDir = spikesdir
|
||||
if sDir=='non':
|
||||
pnts = [[l,t],[an,0],[0,iniY]]
|
||||
else :
|
||||
dirT = -1.0 if sDir=='ins' else 1.0
|
||||
for n in range(anX):
|
||||
if sDir=='alt' : dirT = 1 if n % 2 == 1 else -1
|
||||
if spktype == "rnd": pnts.extend([[0,-dBezh*dirT," c"],[dif,-sh*dirT],[ss,-sh*dirT],[dBez,0], [ss,difh*dirT],[ss,sh*dirT]]) #fijo
|
||||
if spktype == "wav": pnts.extend([[0,-dBezh*dirT," c"],[dif,-sh*dirT],[ss,-sh*dirT],[0,dBezh*dirT],[dBez,sh*dirT],[ss,sh*dirT]]) #fijo
|
||||
if ssep!=0 and n < (anX-1): pnts.append([ssep,0,' l'])
|
||||
pnts.extend([[iniX,0,' l'],[0,iniY]])
|
||||
sDir = sO.spikesdirr # ---------------------------------RIGHT-------------------
|
||||
if sDir == 'pre' : sDir = spikesdir
|
||||
if sDir == 'non' :
|
||||
pnts.extend([[0,al - iniY],[-iniX,0]])
|
||||
else :
|
||||
dirR = -1.0 if sDir=='ins' else 1.0
|
||||
for n in range(anY):
|
||||
if sDir == 'alt' : dirR = 1 if n % 2 == 1 else -1
|
||||
if spktype == "rnd": pnts.extend([[dBezh*dirR,0," c"],[sh*dirR,dif],[sh*dirR,ss], [0,dBez] ,[-difh*dirR,ss], [-sh*dirR,ss]]) #fijo
|
||||
if spktype == "wav": pnts.extend([[dBezh*dirR,0," c"],[sh*dirR,dif],[sh*dirR,ss], [-dBezh*dirR,0],[-sh*dirR,dBez], [-sh*dirR,ss]]) #fijo
|
||||
if ssep!=0 and n < (anY-1): pnts.append([0, ssep,' l'])
|
||||
pnts.extend([[0,iniY,' l'],[-iniX,0]])
|
||||
sDir = sO.spikesdirb # -------------------------------BOTTOM--------------------
|
||||
if sDir == 'pre' : sDir = spikesdir
|
||||
if sDir == 'non' :
|
||||
pnts.extend([[-an + iniX,0],[0,-iniY]])
|
||||
else :
|
||||
dirB = -1.0 if sDir=='ins' else 1.0
|
||||
for n in range(anX):
|
||||
if sDir == 'alt' : dirB = 1 if n % 2 == 1 else -1
|
||||
if spktype == "rnd": pnts.extend([[0,dBezh*dirB," c"],[-dif,sh*dirB],[-ss,sh*dirB],[-dBez,0],[-ss,-difh * dirB],[-ss,-sh * dirB]]) #fijo
|
||||
if spktype == "wav": pnts.extend([[0,dBezh*dirB," c"],[-dif,sh*dirB],[-ss,sh*dirB],[0,-dBezh*dirB],[-dif, -sh*dirB],[-ss,-sh * dirB]]) #fijo
|
||||
if ssep!=0 and n < (anX-1): pnts.append([-ssep,0,' l'])
|
||||
pnts.extend([[-iniX,0,' l'],[0,-iniY]])
|
||||
sDir = sO.spikesdirl # --------------------------------------LEFT---------------
|
||||
if sDir == 'pre' : sDir = spikesdir
|
||||
if sDir != 'non' :
|
||||
dirL = -1.0 if sDir=='ins' else 1.0
|
||||
for n in range(anY):
|
||||
if sDir == 'alt' : dirL = 1 if n % 2 == 1 else -1
|
||||
if spktype=="rnd": pnts.extend([[-dBezh*dirL,0," c"],[-sh*dirL,-dif],[-sh*dirL,-ss],[0,-dBez],[difh * dirL,-ss],[sh * dirL,-ss]]) #fijo
|
||||
if spktype=="wav": pnts.extend([[-dBezh*dirL,0," c"],[-sh*dirL,-dif],[-sh*dirL,-ss],[dBezh*dirL,0],[sh*dirL,-dBez],[sh * dirL,-ss]]) #fijo
|
||||
if ssep!=0 and n < (anY-1): pnts.append([0, -ssep,' l'])
|
||||
return pnts # 805
|
||||
|
||||
def draw_shapes(self):
|
||||
sels = []
|
||||
for id, node in self.svg.selected.items(): sels.append(node)
|
||||
tab = str(self.options.tab)
|
||||
if tab != 'extra':
|
||||
for node in sels:
|
||||
self.draw(node, tab)
|
||||
else:
|
||||
Type = str(self.options.joincirctype)
|
||||
if len(sels)<2:
|
||||
inkex.errormsg('Select at least two objects')
|
||||
else:
|
||||
self.circsCone(sels, Type)
|
||||
|
||||
def loc_str(self, str):
|
||||
return locale.format("%.f", float(str), 0)
|
||||
|
||||
def effect(self):
|
||||
slices = self.draw_shapes()
|
||||
|
||||
if __name__ == "__main__":
|
||||
Shapes().run()
|
21
extensions/fablabchemnitz/snap_objects.inx
Normal file
21
extensions/fablabchemnitz/snap_objects.inx
Normal file
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
|
||||
<name>Snap Object Points</name>
|
||||
<id>fablabchemnitz.de.snap_objects</id>
|
||||
<param name="max_dist" type="float" min="1" max="9999" precision="2" gui-text="Maximum snap distance">25</param>
|
||||
<param name="controls" type="bool" gui-text="Snap control points">true</param>
|
||||
<param name="ends" type="bool" gui-text="Snap endpoints">true</param>
|
||||
<param name="first_only" type="bool" gui-text="Modify only the first selected path">false</param>
|
||||
<label>This effect snaps points in each selected object to nearby points in other selected objects.</label>
|
||||
<effect>
|
||||
<object-type>path</object-type>
|
||||
<effects-menu>
|
||||
<submenu name="FabLab Chemnitz">
|
||||
<submenu name="Modify existing Path(s)"/>
|
||||
</submenu>
|
||||
</effects-menu>
|
||||
</effect>
|
||||
<script>
|
||||
<command location="inx" interpreter="python">snap_objects.py</command>
|
||||
</script>
|
||||
</inkscape-extension>
|
137
extensions/fablabchemnitz/snap_objects.py
Executable file
137
extensions/fablabchemnitz/snap_objects.py
Executable file
@ -0,0 +1,137 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
'''
|
||||
Copyright (C) 2020 Scott Pakin, scott-ink@pakin.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 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.
|
||||
|
||||
'''
|
||||
|
||||
from collections import defaultdict
|
||||
import inkex
|
||||
from inkex.paths import Arc, Curve, Horz, Line, Move, Quadratic, Smooth, TepidQuadratic, Vert, ZoneClose
|
||||
|
||||
|
||||
class SnapObjects(inkex.Effect):
|
||||
"Snap the points on multiple paths towards each other."
|
||||
|
||||
def __init__(self):
|
||||
inkex.Effect.__init__(self)
|
||||
self.arg_parser.add_argument('--max_dist', type=float, default=25.0, help='Maximum distance to be considered a "nearby" point')
|
||||
self.arg_parser.add_argument('--controls', type=inkex.Boolean, default=True, help='Snap control points')
|
||||
self.arg_parser.add_argument('--ends', type=inkex.Boolean, default=True, help='Snap endpoints')
|
||||
self.arg_parser.add_argument('--first_only', type=inkex.Boolean, default=True, help='Modify only the first selected path')
|
||||
|
||||
def _bin_points(self):
|
||||
"Associate each path ID with a list of control points and a list of endpoints."
|
||||
cpoints = defaultdict(list)
|
||||
epoints = defaultdict(list)
|
||||
for node in self.svg.selection.filter(inkex.PathElement).values():
|
||||
for cmd in node.path.to_absolute().proxy_iterator():
|
||||
pid = node.get_id()
|
||||
cpoints[pid].extend(cmd.control_points)
|
||||
epoints[pid].append(cmd.end_point)
|
||||
return cpoints, epoints
|
||||
|
||||
def _find_nearest(self, pid, x0, y0, other_points):
|
||||
'''Find the nearest neighbor to a given point, and return the midpoint
|
||||
of the given point and its neighbor.'''
|
||||
max_dist2 = self.options.max_dist**2 # Work with squares instead of wasting time on square roots.
|
||||
bx, by = x0, y0 # Best new point
|
||||
best_dist2 = max_dist2 # Minimal distance observed from (x0, y0)
|
||||
for k, pts in other_points.items():
|
||||
if k == pid:
|
||||
continue # Don't compare to our own points.
|
||||
for vec in pts:
|
||||
x1, y1 = vec.x, vec.y
|
||||
dist2 = (x1 - x0)**2 + (y1 - y0)**2 # Squared distance
|
||||
if dist2 > best_dist2:
|
||||
continue # Not the nearest point
|
||||
best_dist2 = dist2
|
||||
bx, by = x1, y1
|
||||
return (x0 + bx)/2, (y0 + by)/2
|
||||
|
||||
def _simplify_paths(self):
|
||||
'Make all commands absolute, and replace Vert and Horz commands with Line.'
|
||||
for node in self.svg.selection.filter(inkex.PathElement).values():
|
||||
path = node.path.to_absolute()
|
||||
new_path = []
|
||||
prev = inkex.Vector2d()
|
||||
prev_prev = inkex.Vector2d()
|
||||
first = inkex.Vector2d()
|
||||
for i, cmd in enumerate(path):
|
||||
if i == 0:
|
||||
first = cmd.end_point(first, prev)
|
||||
prev, prev_prev = first, first
|
||||
if isinstance(cmd, Vert):
|
||||
cmd = cmd.to_line(prev)
|
||||
elif isinstance(cmd, Horz):
|
||||
cmd = cmd.to_line(prev)
|
||||
new_path.append(cmd)
|
||||
if isinstance(cmd, (Curve, Quadratic, Smooth, TepidQuadratic)):
|
||||
prev_prev = list(cmd.control_points(first, prev, prev_prev))[-2]
|
||||
prev = cmd.end_point(first, prev)
|
||||
node.path = new_path
|
||||
|
||||
def effect(self):
|
||||
"""Snap control points to other objects' control points and endpoints
|
||||
to other objects' endpoints."""
|
||||
# This function uses an O(n^2) algorithm, which shouldn't be too slow
|
||||
# for typical point counts.
|
||||
#
|
||||
# As a preprocessing step, we first simplify the paths to reduce the
|
||||
# number of special cases we'll need to deal with. Then, we associate
|
||||
# each path with all of its control points and endpoints.
|
||||
if len(self.svg.selection.filter(inkex.PathElement)) < 2:
|
||||
raise inkex.utils.AbortExtension(_('Snap Object Points requires that at least two paths be selected.'))
|
||||
self._simplify_paths()
|
||||
cpoints, epoints = self._bin_points()
|
||||
|
||||
|
||||
|
||||
# Process in turn each command on each path.
|
||||
for node in self.svg.selection.filter(inkex.PathElement).values():
|
||||
pid = node.get_id()
|
||||
path = node.path
|
||||
new_path = []
|
||||
for cmd in path:
|
||||
args = cmd.args
|
||||
new_args = list(args)
|
||||
na = len(args)
|
||||
if isinstance(cmd, (Curve, Line, Move, Quadratic, Smooth, TepidQuadratic)):
|
||||
# Zero or more control points followed by an endpoint.
|
||||
if self.options.controls:
|
||||
for i in range(0, na - 2, 2):
|
||||
new_args[i], new_args[i + 1] = self._find_nearest(pid, args[i], args[i + 1], cpoints)
|
||||
if self.options.ends:
|
||||
new_args[na - 2], new_args[na - 1] = self._find_nearest(pid, args[na - 2], args[na - 1], epoints)
|
||||
elif isinstance(cmd, ZoneClose):
|
||||
# No arguments at all.
|
||||
pass
|
||||
elif isinstance(cmd, Arc):
|
||||
# Non-coordinates followed by an endpoint.
|
||||
if self.options.ends:
|
||||
new_args[na - 2], new_args[na - 1] = self._find_nearest(pid, args[na - 2], args[na - 1], epoints)
|
||||
else:
|
||||
# Unexpected command.
|
||||
inkex.errormsg(_('Unexpected path command "%s"' % cmd.name))
|
||||
new_path.append(cmd.__class__(*new_args))
|
||||
node.path = new_path
|
||||
if self.options.first_only:
|
||||
break
|
||||
|
||||
if __name__ == '__main__':
|
||||
SnapObjects().run()
|
@ -157,8 +157,11 @@ class StylesToLayers(inkex.Effect):
|
||||
|
||||
contentlength = 0 #some counter to track if there are layers inside or if it is just a list with empty children
|
||||
for layerNode in layerNodeList:
|
||||
layerNode[0].append(layerNode[2]) #append element to created layer
|
||||
if layerNode[1] is not None: contentlength += 1 #for each found layer we increment +1
|
||||
try: #some nasty workaround. Make better code
|
||||
layerNode[0].append(layerNode[2]) #append element to created layer
|
||||
if layerNode[1] is not None: contentlength += 1 #for each found layer we increment +1
|
||||
except:
|
||||
continue
|
||||
|
||||
# Additionally if threshold was defined re-arrange the previously created layers by putting them into sub layers
|
||||
if self.options.subdividethreshold > 1 and contentlength > 0: #check if we need to subdivide and if there are items we could rearrange into sub layers
|
||||
|
@ -1,53 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
|
||||
<name>Tool Covers</name>
|
||||
<id>fablabchemnitz.de.tool_covers</id>
|
||||
<param name="tabs" type="notebook">
|
||||
<page name="parameters" gui-text="Basic parameters">
|
||||
<param name="title_base" type="description">base</param>
|
||||
<param name="w1" type="float" min="1" max="200" indent="2" gui-text="Width of tip (w1)[mm]">20</param>
|
||||
<param name="w2" type="float" min="2" max="200" indent="2" gui-text="Maximum width (w2)[mm]">40</param>
|
||||
<param name="h1" type="float" min="1" max="200" indent="2" gui-text="Tip length (h1)[mm]">40</param>
|
||||
<param name="h2" type="float" min="1" max="200" indent="2" gui-text="Length other than tip (h2)[mm]">20</param>
|
||||
<param name="title_base" type="description">band</param>
|
||||
<param name="bw" type="float" min="1" max="30" indent="2" gui-text="Band width (bw)[mm]">15</param>
|
||||
<param name="bl" type="float" min="1" max="90" indent="2" gui-text="Band length (bl)[mm]">30</param>
|
||||
<param name="title_base" type="description">fastener</param>
|
||||
<param name="dia1" type="float" min="1" max="50" indent="2" gui-text="Fastener female diameter (dia1)[mm]">10</param>
|
||||
<param name="dia2" type="float" min="1" max="50" indent="2" gui-text="Fastener male diameter (dia2)[mm]">10</param>
|
||||
<param name="title_nuishiro" type="description">margin and needle hole</param>
|
||||
<param name="d1" type="float" min="1" max="5" indent="2" gui-text="Sew it up (d1)[mm]">3</param>
|
||||
<param name="d2" type="float" min="1" max="10" indent="2" gui-text="Needle hole spacing (d2)[mm]">3</param>
|
||||
</page>
|
||||
<page name="details" gui-text="Detailed parameters">
|
||||
<param name="title_base" type="description">band</param>
|
||||
<param name="bf" type="float" min="0.1" max="1" indent="2" gui-text="curve coefficient (bf)">0.7</param>
|
||||
<param name="title_nuishiro" type="description">needle hole</param>
|
||||
<param name="needle_w" type="float" min="0.5" max="4" indent="2" gui-text="Acupoint width (needle_w)[mm]">1</param>
|
||||
<param name="needle_h" type="float" min="0.5" max="4" indent="2" gui-text="Height of the needle hole (needle_h)[mm]">1</param>
|
||||
<param name="needle_tf" type="float" min="-2" max="2" indent="2" gui-text="Needle slope coefficient (needle_tf)">1</param>
|
||||
<param name="needle_corner_rotation" type="bool" indent="2" gui-text="Angle Tilt Adjustment">true</param>
|
||||
</page>
|
||||
<page name="about" gui-text="PliersCover - about">
|
||||
<param name="version" type="description" xml:space="preserve">PiersCover Inkscape extension
|
||||
|
||||
Version 0.92
|
||||
|
||||
by Yoichi Tanibayashi
|
||||
|
||||
https://ytani01.github.io/PliersCover/
|
||||
</param>
|
||||
</page>
|
||||
</param>
|
||||
<effect needs-live-preview="true">
|
||||
<object-type>all</object-type>
|
||||
<effects-menu>
|
||||
<submenu name="FabLab Chemnitz">
|
||||
<submenu name="Shape/Pattern from Generator"/>
|
||||
</submenu>
|
||||
</effects-menu>
|
||||
</effect>
|
||||
<script>
|
||||
<command location="inx" interpreter="python">tool_covers.py</command>
|
||||
</script>
|
||||
</inkscape-extension>
|
@ -1,487 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# (c) 2020 Yoichi Tanibayashi
|
||||
#
|
||||
import inkex
|
||||
from lxml import etree
|
||||
import math
|
||||
|
||||
class Point(object):
|
||||
def __init__(self, x, y):
|
||||
self.x = x
|
||||
self.y = y
|
||||
|
||||
def distance(self, c):
|
||||
return math.sqrt((c.x - self.x) ** 2 + (c.y - self.y) ** 2)
|
||||
|
||||
def rotate(self, rad):
|
||||
new_x = math.cos(rad) * self.x - math.sin(rad) * self.y
|
||||
new_y = math.sin(rad) * self.x + math.cos(rad) * self.y
|
||||
self.x = new_x
|
||||
self.y = new_y
|
||||
return self
|
||||
|
||||
def mirror(self):
|
||||
self.x = -self.x
|
||||
return self
|
||||
|
||||
|
||||
class Vpoint(Point):
|
||||
'''
|
||||
A point with (x, y) coordinates and direction (rad)
|
||||
|
||||
rad: Direction (true up: 0, right: math.pi / 2, ...)
|
||||
'''
|
||||
def __init__(self, x, y, rad=0):
|
||||
super(Vpoint, self).__init__(x, y)
|
||||
self.rad = rad
|
||||
|
||||
def rotate(self, rad):
|
||||
super(Vpoint, self).rotate(rad)
|
||||
self.rad += rad
|
||||
return self
|
||||
|
||||
def mirror(self):
|
||||
super(Vpoint, self).mirror()
|
||||
self.rad = -self.rad
|
||||
return self
|
||||
|
||||
|
||||
class SvgObj(object):
|
||||
DEF_COLOR = '#00FF00'
|
||||
DEF_STROKE_WIDTH = 0.2
|
||||
DEF_STROKE_DASHARRAY = 'none'
|
||||
|
||||
def __init__(self, parent):
|
||||
self.parent = parent
|
||||
self.type = None
|
||||
self.attr = {}
|
||||
|
||||
def draw(self, color=DEF_COLOR,
|
||||
stroke_width=DEF_STROKE_WIDTH,
|
||||
stroke_dasharray=DEF_STROKE_DASHARRAY):
|
||||
|
||||
self.attr['style'] = str(inkex.Style({
|
||||
'stroke': str(color),
|
||||
'stroke-width': str(stroke_width),
|
||||
'stroke-dasharray': str(stroke_dasharray),
|
||||
'fill': 'none'}))
|
||||
return etree.SubElement(self.parent,
|
||||
inkex.addNS(self.type, 'svg'),
|
||||
self.attr)
|
||||
|
||||
|
||||
class SvgCircle(SvgObj):
|
||||
DEF_COLOR = '#FF0000'
|
||||
DEF_STROKE_WIDTH = 0.2
|
||||
DEF_STROKE_DASHARRAY = 'none'
|
||||
|
||||
def __init__(self, parent, r):
|
||||
super(SvgCircle, self).__init__(parent)
|
||||
self.r = r
|
||||
self.type = 'circle'
|
||||
|
||||
def draw(self, point,
|
||||
color=DEF_COLOR,
|
||||
stroke_width=DEF_STROKE_WIDTH,
|
||||
stroke_dasharray=DEF_STROKE_DASHARRAY):
|
||||
self.attr['cx'] = str(point.x)
|
||||
self.attr['cy'] = str(point.y)
|
||||
self.attr['r'] = str(self.r)
|
||||
|
||||
return super(SvgCircle, self).draw(color,
|
||||
stroke_width, stroke_dasharray)
|
||||
|
||||
|
||||
class SvgPath(SvgObj):
|
||||
DEF_COLOR = '#0000FF'
|
||||
DEF_STROKE_WIDTH = 0.2
|
||||
DEF_STROKE_DASHARRAY = 'none'
|
||||
|
||||
def __init__(self, parent, points):
|
||||
super(SvgPath, self).__init__(parent)
|
||||
self.points = points
|
||||
self.type = 'path'
|
||||
|
||||
def create_svg_d(self, origin_vpoint, points):
|
||||
'''
|
||||
to be override
|
||||
|
||||
This is sample code.
|
||||
'''
|
||||
svg_d = ''
|
||||
for i, p in enumerate(points):
|
||||
(x1, y1) = (p.x + origin_vpoint.x, p.y + origin_vpoint.y)
|
||||
if i == 0:
|
||||
svg_d = 'M %f,%f' % (x1, y1)
|
||||
else:
|
||||
svg_d += ' L %f,%f' % (x1, y1)
|
||||
return svg_d
|
||||
|
||||
def rotate(self, rad):
|
||||
for p in self.points:
|
||||
p.rotate(rad)
|
||||
return self
|
||||
|
||||
def mirror(self):
|
||||
for p in self.points:
|
||||
p.mirror()
|
||||
return self
|
||||
|
||||
def draw(self, origin,
|
||||
color=DEF_COLOR, stroke_width=DEF_STROKE_WIDTH,
|
||||
stroke_dasharray=DEF_STROKE_DASHARRAY):
|
||||
|
||||
self.rotate(origin.rad)
|
||||
|
||||
svg_d = self.create_svg_d(origin, self.points)
|
||||
# inkex.errormsg('svg_d=%s' % svg_d)
|
||||
# inkex.errormsg('svg_d=%s' % str(Path( svg_d )))
|
||||
|
||||
self.attr['d'] = svg_d
|
||||
return super(SvgPath, self).draw(color, stroke_width, stroke_dasharray)
|
||||
|
||||
|
||||
class SvgLine(SvgPath):
|
||||
# exactly same as SvgPath
|
||||
pass
|
||||
|
||||
|
||||
class SvgPolygon(SvgPath):
|
||||
def create_svg_d(self, origin, points):
|
||||
svg_d = super(SvgPolygon, self).create_svg_d(origin, points)
|
||||
svg_d += ' Z'
|
||||
return svg_d
|
||||
|
||||
|
||||
class SvgPart1Outline(SvgPolygon):
|
||||
def __init__(self, parent, points, bw_bf):
|
||||
super(SvgPart1Outline, self).__init__(parent, points)
|
||||
self.bw_bf = bw_bf
|
||||
|
||||
def create_svg_d(self, origin, points, bw_bf=1):
|
||||
for i, p in enumerate(points):
|
||||
(x1, y1) = (p.x + origin.x, p.y + origin.y)
|
||||
if i == 0:
|
||||
d = 'M %f,%f' % (x1, y1)
|
||||
elif i == 7:
|
||||
d += ' L %f,%f' % (x1, y1)
|
||||
x2 = x1
|
||||
y2 = y1 + self.bw_bf
|
||||
elif i == 8:
|
||||
d += ' C %f,%f %f,%f %f,%f' % (x2, y2, x1, y2, x1, y1)
|
||||
else:
|
||||
d += ' L %f,%f' % (x1, y1)
|
||||
|
||||
d += ' Z'
|
||||
return d
|
||||
|
||||
|
||||
class SvgNeedleHole(SvgPolygon):
|
||||
def __init__(self, parent, w, h, tf):
|
||||
'''
|
||||
w: width
|
||||
h: height
|
||||
tf: tilt factor
|
||||
'''
|
||||
self.w = w
|
||||
self.h = h
|
||||
self.tf = tf
|
||||
|
||||
self.gen_points(self.w, self.h, self.tf)
|
||||
super(SvgNeedleHole, self).__init__(parent, self.points)
|
||||
|
||||
def gen_points(self, w, h, tf):
|
||||
self.points = []
|
||||
self.points.append(Point(-w / 2, h * tf))
|
||||
self.points.append(Point( w / 2, h * (1 - tf)))
|
||||
self.points.append(Point( w / 2, -h * tf))
|
||||
self.points.append(Point(-w / 2, -h * (1 - tf)))
|
||||
|
||||
|
||||
class Part1(object):
|
||||
def __init__(self, parent,
|
||||
w1, w2, h1, h2, bw, bl, bf, dia1, d1, d2,
|
||||
needle_w, needle_h, needle_tf, needle_corner_rotation):
|
||||
self.parent = parent
|
||||
self.w1 = w1
|
||||
self.w2 = w2
|
||||
self.h1 = h1
|
||||
self.h2 = h2
|
||||
self.bw = bw
|
||||
self.bl = bl
|
||||
self.bf = bf
|
||||
self.dia1 = dia1
|
||||
self.d1 = d1
|
||||
self.d2 = d2
|
||||
self.needle_w = needle_w
|
||||
self.needle_h = needle_h
|
||||
self.needle_tf = needle_tf
|
||||
self.needle_corner_rotation = needle_corner_rotation
|
||||
|
||||
# Group Creation
|
||||
attr = {inkex.addNS('label', 'inkscape'): 'Part1'}
|
||||
self.g = etree.SubElement(self.parent, 'g', attr)
|
||||
|
||||
# drawing
|
||||
self.points_outline = self.create_points_outline()
|
||||
self.svg_outline = SvgPart1Outline(self.g, self.points_outline,
|
||||
(self.bw * self.bf))
|
||||
self.svg_hole = SvgCircle(self.g, self.dia1 / 2)
|
||||
|
||||
self.vpoints_needle = self.create_needle_vpoints()
|
||||
self.svgs_needle_hole = []
|
||||
for v in self.vpoints_needle:
|
||||
svg_nh = SvgNeedleHole(self.g,
|
||||
self.needle_w,
|
||||
self.needle_h,
|
||||
self.needle_tf)
|
||||
self.svgs_needle_hole.append((svg_nh, v))
|
||||
|
||||
def create_points_outline(self):
|
||||
#Generate the coordinates of the outer frame
|
||||
|
||||
points = []
|
||||
(x0, y0) = (-(self.w2 / 2), 0)
|
||||
|
||||
(x, y) = (x0, y0 + self.h1 + self.h2)
|
||||
points.append(Point(x, y))
|
||||
|
||||
y = y0 + self.h1
|
||||
points.append(Point(x, y))
|
||||
|
||||
x = -(self.w1 / 2)
|
||||
y = y0
|
||||
points.append(Point(x, y))
|
||||
|
||||
x = self.w1 / 2
|
||||
points.append(Point(x, y))
|
||||
|
||||
x = self.w2 / 2
|
||||
y += self.h1
|
||||
points.append(Point(x, y))
|
||||
|
||||
y += self.h2
|
||||
points.append(Point(x, y))
|
||||
|
||||
x = self.bw / 2
|
||||
points.append(Point(x, y))
|
||||
|
||||
y += self.bl - self.bw / 2
|
||||
points.append(Point(x, y))
|
||||
|
||||
x = -(self.bw / 2)
|
||||
points.append(Point(x, y))
|
||||
|
||||
y = y0 + self.h1 + self.h2
|
||||
points.append(Point(x, y))
|
||||
|
||||
return points
|
||||
|
||||
def create_needle_vpoints(self):
|
||||
'''
|
||||
針穴の点と方向を生成
|
||||
'''
|
||||
rad1 = math.atan((self.w2 - self.w1) / (2 * self.h1))
|
||||
rad1a = (math.pi - rad1) / 2
|
||||
a1 = self.d1 / math.tan(rad1a)
|
||||
|
||||
rad2 = (math.pi / 2) - rad1
|
||||
rad2a = (math.pi - rad2) / 2
|
||||
a2 = self.d1 / math.tan(rad2a)
|
||||
|
||||
#
|
||||
# summit
|
||||
#
|
||||
vpoints1 = []
|
||||
for i, p in enumerate(self.points_outline):
|
||||
(nx, ny) = (p.x, p.y)
|
||||
if i == 0:
|
||||
nx += self.d1
|
||||
ny -= self.d1 * 1.5
|
||||
vpoints1.append(Vpoint(nx, ny, 0))
|
||||
if i == 1:
|
||||
nx += self.d1
|
||||
ny += a1
|
||||
vpoints1.append(Vpoint(nx, ny, rad1))
|
||||
if i == 2:
|
||||
nx += a2
|
||||
ny += self.d1
|
||||
vpoints1.append(Vpoint(nx, ny, math.pi / 2))
|
||||
if i == 3:
|
||||
nx -= a2
|
||||
ny += self.d1
|
||||
vpoints1.append(Vpoint(nx, ny, (math.pi / 2) + rad2))
|
||||
if i == 4:
|
||||
nx -= self.d1
|
||||
ny += a1
|
||||
vpoints1.append(Vpoint(nx, ny, math.pi))
|
||||
if i == 5:
|
||||
nx -= self.d1
|
||||
ny -= self.d1 * 1.5
|
||||
vpoints1.append(Vpoint(nx, ny, math.pi))
|
||||
if i > 5:
|
||||
break
|
||||
|
||||
# Generate a point that completes a vertex
|
||||
vpoints2 = []
|
||||
for i in range(len(vpoints1)-1):
|
||||
d = vpoints1[i].distance(vpoints1[i+1])
|
||||
n = int(abs(round(d / self.d2)))
|
||||
for p in self.split_vpoints(vpoints1[i], vpoints1[i+1], n):
|
||||
vpoints2.append(p)
|
||||
|
||||
vpoints2.insert(0, vpoints1[0])
|
||||
return vpoints2
|
||||
|
||||
def split_vpoints(self, v1, v2, n):
|
||||
#v1, v2 Generate a list by dividing the space between the two into n pieces
|
||||
|
||||
if n == 0:
|
||||
return [v1]
|
||||
(dx, dy) = ((v2.x - v1.x) / n, (v2.y - v1.y) / n)
|
||||
|
||||
v = []
|
||||
for i in range(n):
|
||||
v.append(Vpoint(v1.x + dx * (i + 1),
|
||||
v1.y + dy * (i + 1),
|
||||
v1.rad))
|
||||
if self.needle_corner_rotation:
|
||||
v[-1].rad = (v1.rad + v2.rad) / 2
|
||||
return v
|
||||
|
||||
def draw(self, origin):
|
||||
origin_base = Vpoint(origin.x + self.w2 / 2,
|
||||
origin.y,
|
||||
origin.rad)
|
||||
self.svg_outline.draw(origin_base, color='#0000FF')
|
||||
|
||||
x = origin.x + self.w2 / 2
|
||||
y = origin.y + self.h1 + self.h2 + self.bl - self.bw / 2
|
||||
origin_hole = Point(x, y)
|
||||
self.svg_hole.draw(origin_hole, color='#FF0000')
|
||||
|
||||
for (svg_nh, p) in self.svgs_needle_hole:
|
||||
origin_nh = Vpoint(origin.x + p.x + self.w2 / 2,
|
||||
origin.y + p.y,
|
||||
p.rad)
|
||||
svg_nh.draw(origin_nh, color='#FF0000')
|
||||
|
||||
|
||||
class Part2(object):
|
||||
def __init__(self, parent, part1, dia2):
|
||||
self.parent = parent
|
||||
self.part1 = part1
|
||||
self.dia2 = dia2
|
||||
|
||||
# Group Creation
|
||||
attr = {inkex.addNS('label', 'inkscape'): 'Part2'}
|
||||
self.g = etree.SubElement(self.parent, 'g', attr)
|
||||
|
||||
# outer frame > Mirroring the points_outline in Part1, and use the first six points.
|
||||
self.points_outline = []
|
||||
for i in range(6):
|
||||
self.points_outline.append(self.part1.points_outline[i].mirror())
|
||||
|
||||
self.svg_outline = SvgPolygon(self.g, self.points_outline)
|
||||
|
||||
# clasp
|
||||
self.svg_hole = SvgCircle(self.g, self.dia2 / 2)
|
||||
|
||||
# pinhole -> Mirroring the vpoints_needle in Part1
|
||||
self.svgs_needle_hole = []
|
||||
for v in self.part1.vpoints_needle:
|
||||
v.mirror()
|
||||
# Mirror also SvgNeedleHole
|
||||
svg_nh = SvgNeedleHole(self.g,
|
||||
self.part1.needle_w,
|
||||
self.part1.needle_h,
|
||||
self.part1.needle_tf)
|
||||
svg_nh.mirror()
|
||||
self.svgs_needle_hole.append((svg_nh, v))
|
||||
|
||||
def draw(self, origin):
|
||||
origin_base = Vpoint(origin.x + self.part1.w2 / 2,
|
||||
origin.y, origin.rad)
|
||||
self.svg_outline.draw(origin_base, color='#0000FF')
|
||||
|
||||
x = origin.x + self.part1.w2 / 2
|
||||
y = origin.y + self.part1.h1 + self.part1.h2
|
||||
y -= (self.svg_hole.r + self.part1.d1)
|
||||
origin_hole = Vpoint(x, y, origin.rad)
|
||||
self.svg_hole.draw(origin_hole, color='#FF0000')
|
||||
|
||||
for (svg_nh, p) in self.svgs_needle_hole:
|
||||
origin_nh = Vpoint(origin.x + p.x + self.part1.w2 / 2,
|
||||
origin.y + p.y,
|
||||
p.rad)
|
||||
svg_nh.draw(origin_nh, color='#FF0000')
|
||||
|
||||
|
||||
class PliersCover(inkex.Effect):
|
||||
DEF_OFFSET_X = 20
|
||||
DEF_OFFSET_Y = 20
|
||||
|
||||
def __init__(self):
|
||||
inkex.Effect.__init__(self)
|
||||
self.arg_parser.add_argument("--tabs")
|
||||
self.arg_parser.add_argument("--w1", type=float)
|
||||
self.arg_parser.add_argument("--w2", type=float)
|
||||
self.arg_parser.add_argument("--h1", type=float)
|
||||
self.arg_parser.add_argument("--h2", type=float)
|
||||
self.arg_parser.add_argument("--bw", type=float)
|
||||
self.arg_parser.add_argument("--bl", type=float)
|
||||
self.arg_parser.add_argument("--bf", type=float)
|
||||
self.arg_parser.add_argument("--dia1", type=float)
|
||||
self.arg_parser.add_argument("--dia2", type=float)
|
||||
self.arg_parser.add_argument("--d1", type=float)
|
||||
self.arg_parser.add_argument("--d2", type=float)
|
||||
self.arg_parser.add_argument("--needle_w", type=float)
|
||||
self.arg_parser.add_argument("--needle_h", type=float)
|
||||
self.arg_parser.add_argument("--needle_tf", type=float)
|
||||
self.arg_parser.add_argument("--needle_corner_rotation", type=inkex.Boolean, default=True)
|
||||
|
||||
def effect(self):
|
||||
# inkex.errormsg('view_center=%s' % str(self.view_center))
|
||||
# inkex.errormsg('selected=%s' % str(self.selected))
|
||||
|
||||
# parameters
|
||||
opt = self.options
|
||||
|
||||
#
|
||||
# error check
|
||||
#
|
||||
if opt.w1 >= opt.w2:
|
||||
msg = "Error: w1(%d) > w2(%d) !" % (opt.w1, opt.w2)
|
||||
inkex.errormsg(msg)
|
||||
return
|
||||
|
||||
if opt.dia1 >= opt.bw:
|
||||
msg = "Error: dia1(%d) >= bw(%d) !" % (opt.dia1, opt.bw)
|
||||
inkex.errormsg(msg)
|
||||
return
|
||||
|
||||
#
|
||||
# draw
|
||||
#
|
||||
origin_vpoint = Vpoint(self.DEF_OFFSET_X, self.DEF_OFFSET_Y)
|
||||
|
||||
# Group Creation
|
||||
attr = {inkex.addNS('label', 'inkscape'): 'PliersCover'}
|
||||
self.g = etree.SubElement(self.svg.get_current_layer(), 'g', attr)
|
||||
|
||||
part1 = Part1(self.g,
|
||||
opt.w1, opt.w2, opt.h1, opt.h2,
|
||||
opt.bw, opt.bl, opt.bf, opt.dia1,
|
||||
opt.d1, opt.d2,
|
||||
opt.needle_w, opt.needle_h, opt.needle_tf,
|
||||
opt.needle_corner_rotation)
|
||||
part1.draw(origin_vpoint)
|
||||
|
||||
origin_vpoint.x += opt.w2 + 10
|
||||
|
||||
part2 = Part2(self.g, part1, opt.dia2)
|
||||
part2.draw(origin_vpoint)
|
||||
|
||||
if __name__ == '__main__':
|
||||
PliersCover().run()
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<inkscape-extension>
|
||||
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
|
||||
<name>Image Triangulation</name>
|
||||
<id>fablabchemnitz.de.triangulation</id>
|
||||
<param name="tab" type="notebook">
|
||||
|
Reference in New Issue
Block a user