Added primitive extension

This commit is contained in:
Mario Voigt 2020-08-21 16:05:51 +02:00
parent db0f31054b
commit ac56527f8f
3 changed files with 189 additions and 0 deletions

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Primitive (Michael Fogleman)</name>
<id>fablabchemnitz.de.primitive</id>
<label appearance="header">Settings</label>
<separator/>
<param name="keeporiginal" type="bool" gui-text="Keep original image on canvas">false</param>
<param name="n" type="int" min="0" max="99999" gui-text="Number of shapes">100</param>
<param name="m" appearance="combo" type="optiongroup" default="1" gui-text="Mode">
<option value="0">Combo</option>
<option value="1">Triangle</option>
<option value="2">Rectangle</option>
<option value="3">Ellipse</option>
<option value="4">Circle</option>
<option value="5">Rotated rectangle</option>
<option value="6">Beziers</option>
<option value="7">Rotated ellipse</option>
<option value="8">Polygon</option>
</param>
<param name="rep" type="int" min="0" max="99999" gui-text="Extra shapes/iteration" gui-description="Sdd N extra shapes each iteration with reduced search (mostly good for beziers)">0</param>
<param name="r" type="int" min="1" max="99999" gui-text="Resize to size before processing (px)">256</param>
<param name="s" type="int" min="1" max="99999" gui-text="Output image size (px)">1024</param>
<param name="a" type="int" min="0" max="255" gui-text="Color alpha" gui-description="Use 0 to let the algorithm choose alpha for each shape">128</param>
<param name="bg_enabled" type="bool" gui-text="Use average starting background color">true</param>
<param name="bg" type="color" appearance="colorbutton" min="0" max="255" gui-text="Starting background color" gui-description="You need to disable average starting background to use this option">255</param>
<param name="j" type="int" min="0" max="32" gui-text="Number of parallel workers" gui-description="Default (0) uses all cores">0</param>
<spacer/>
<label appearance="header">About</label>
<separator/>
<label>Primitive - Reproducing images with geometric primitives written in Go. Wrapped for InkScape 1.X by Mario Voigt / Stadtfabrikanten e.V. (2020)</label>
<label appearance="url">https://github.com/fogleman/primitive</label>
<label appearance="url">https://fablabchemnitz.de</label>
<label>License: GNU GPL v3 for the wrapper, MIT for primitive</label>
<effect needs-live-preview="true">
<object-type>image</object-type>
<effects-menu>
<submenu name="FabLab Chemnitz">
<submenu name="Tracing/Edge Detection"/>
</submenu>
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">./fablabchemnitz_primitive/fablabchemnitz_primitive.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,144 @@
#!/usr/bin/env python3
import sys
import inkex
import os
import base64
import urllib.request as urllib
from PIL import Image
from io import BytesIO
from lxml import etree
from inkex import Color
"""
Extension for InkScape 1.X
Features
- Primitive - Reproducing images with geometric primitives written in Go.
Author: Mario Voigt / FabLab Chemnitz
Mail: mario.voigt@stadtfabrikanten.org
Date: 21.08.2020
Last patch: 21.08.2020
License: GNU GPL v3
Used version of Primitive: https://github.com/fogleman/primitive/commit/0373c216458be1c4b40655b796a3aefedf8b7d23
"""
class Primitive (inkex.Effect):
def rgbToHex(self, pickerColor):
longcolor = int(pickerColor)
if longcolor < 0:
longcolor = longcolor & 0xFFFFFFFF
return '#' + format(longcolor >> 8, '06X')
def checkImagePath(self, node):
xlink = node.get('xlink:href')
if xlink and xlink[:5] == 'data:':
# No need, data alread embedded
return
url = urllib.urlparse(xlink)
href = urllib.url2pathname(url.path)
# Primary location always the filename itself.
path = self.absolute_href(href or '')
# Backup directory where we can find the image
if not os.path.isfile(path):
path = node.get('sodipodi:absref', path)
if not os.path.isfile(path):
inkex.errormsg('File not found "{}". Unable to embed image.').format(path)
return
if (os.path.isfile(path)):
return path
def __init__(self):
inkex.Effect.__init__(self)
self.arg_parser.add_argument("--keeporiginal", type=inkex.Boolean, default=False, help="Keep original image on canvas")
self.arg_parser.add_argument("--n", type=int, default=100, help="Number of shapes")
self.arg_parser.add_argument("--m", default=1, help="Mode")
self.arg_parser.add_argument("--rep", type=int, default=0,help="Extra shapes/iteration")
self.arg_parser.add_argument("--r", type=int, default=256, help="Resize to size before processing (px)")
self.arg_parser.add_argument("--s", type=int, default=1024, help="Output image size (px)")
self.arg_parser.add_argument("--a", type=int, default=128, help="Color alpha")
self.arg_parser.add_argument("--bg_enabled", type=inkex.Boolean, default=True, help="Use average starting background color")
self.arg_parser.add_argument("--bg", type=Color, default=255, help="Starting background color")
self.arg_parser.add_argument("--j", type=int, default=0, help="Number of parallel workers")
def effect(self):
# internal overwrite for scale:
self.options.scale = 1.0
if (self.options.ids):
for node in self.svg.selected.values():
if node.tag == inkex.addNS('image', 'svg'):
self.path = self.checkImagePath(node) # This also ensures the file exists
if self.path is None: # check if image is embedded or linked
image_string = node.get('{http://www.w3.org/1999/xlink}href')
# find comma position
i = 0
while i < 40:
if image_string[i] == ',':
break
i = i + 1
image = Image.open(BytesIO(base64.b64decode(image_string[i + 1:len(image_string)])))
else:
image = Image.open(self.path)
# Write the embedded or linked image to temporary directory
exportfile = "Primitive.png"
image.save(exportfile, "png")
## Build up Primitive command according to your settings from extension GUI
command = "primitive"
command += " -m " + str(self.options.m)
command += " -rep " + str(self.options.rep)
command += " -r " + str(self.options.r)
command += " -s " + str(self.options.s)
command += " -a " + str(self.options.a)
if not self.options.bg_enabled:
command += " -bg " + self.rgbToHex(self.options.bg)
command += " -j " + str(self.options.j)
command += " -i " + exportfile
command += " -o " + exportfile + ".svg"
command += " -n " + str(self.options.n)
inkex.utils.debug(command)
# Create the vector new SVG file
with os.popen(command, "r") as proc:
result = proc.read()
#inkex.utils.debug(result)
# proceed if new SVG file was successfully created
if os.path.exists(exportfile + ".svg"):
# Delete the temporary png file again because we do not need it anymore
if os.path.exists(exportfile):
os.remove(exportfile)
# new parse the SVG file and insert it as new group into the current document tree
doc = etree.parse(exportfile + ".svg").getroot()
newGroup = self.document.getroot().add(inkex.Group())
newGroup.attrib['transform'] = "matrix(" + \
str(float(node.get('width')) / float(doc.get('width'))) + \
", 0, 0 , " + \
str(float(node.get('height')) / float(doc.get('height'))) + \
"," + node.get('x') + \
"," + node.get('y') + ")"
newGroup.append(doc)
# Delet the temporary svg file
if os.path.exists(exportfile + ".svg"):
os.remove(exportfile + ".svg")
#remove the old image or not
if self.options.keeporiginal is not True:
node.getparent().remove(node)
else:
inkex.utils.debug("No image found for tracing. Please select an image first.")
Primitive().run()

Binary file not shown.