refactor everything out of lib/__init__.py

pull/163/head
Lex Neva 2018-05-01 21:21:07 -04:00
rodzic 1b31806423
commit 05daffb7e0
31 zmienionych plików z 381 dodań i 359 usunięć

Wyświetl plik

@ -1,298 +0,0 @@
#!/usr/bin/env python
# http://www.achatina.de/sewing/main/TECHNICL.HTM
import os
import sys
import gettext
from copy import deepcopy
import math
import libembroidery
from .utils import cache
from .utils.geometry import Point
import inkex
import simplepath
import simplestyle
import simpletransform
from bezmisc import bezierlength, beziertatlength, bezierpointatt
from cspsubdiv import cspsubdiv
import cubicsuperpath
from shapely import geometry as shgeo
# modern versions of Inkscape use 96 pixels per inch as per the CSS standard
PIXELS_PER_MM = 96 / 25.4
SVG_PATH_TAG = inkex.addNS('path', 'svg')
SVG_POLYLINE_TAG = inkex.addNS('polyline', 'svg')
SVG_DEFS_TAG = inkex.addNS('defs', 'svg')
SVG_GROUP_TAG = inkex.addNS('g', 'svg')
INKSCAPE_LABEL = inkex.addNS('label', 'inkscape')
INKSCAPE_GROUPMODE = inkex.addNS('groupmode', 'inkscape')
EMBROIDERABLE_TAGS = (SVG_PATH_TAG, SVG_POLYLINE_TAG)
dbg = open(os.devnull, "w")
translation = None
_ = lambda message: message
def localize():
if getattr(sys, 'frozen', False):
# we are in a pyinstaller installation
locale_dir = sys._MEIPASS
else:
locale_dir = os.path.dirname(__file__)
locale_dir = os.path.join(locale_dir, 'locales')
global translation, _
translation = gettext.translation("inkstitch", locale_dir, fallback=True)
_ = translation.gettext
localize()
# cribbed from inkscape-silhouette
def parse_length_with_units( str ):
'''
Parse an SVG value which may or may not have units attached
This version is greatly simplified in that it only allows: no units,
units of px, mm, and %. Everything else, it returns None for.
There is a more general routine to consider in scour.py if more
generality is ever needed.
'''
u = 'px'
s = str.strip()
if s[-2:] == 'px':
s = s[:-2]
elif s[-2:] == 'mm':
u = 'mm'
s = s[:-2]
elif s[-2:] == 'pt':
u = 'pt'
s = s[:-2]
elif s[-2:] == 'pc':
u = 'pc'
s = s[:-2]
elif s[-2:] == 'cm':
u = 'cm'
s = s[:-2]
elif s[-2:] == 'in':
u = 'in'
s = s[:-2]
elif s[-1:] == '%':
u = '%'
s = s[:-1]
try:
v = float( s )
except:
raise ValueError(_("parseLengthWithUnits: unknown unit %s") % s)
return v, u
def convert_length(length):
value, units = parse_length_with_units(length)
if not units or units == "px":
return value
if units == 'pt':
value /= 72
units = 'in'
if units == 'pc':
value /= 6
units = 'in'
if units == 'cm':
value *= 10
units = 'mm'
if units == 'mm':
value = value / 25.4
units = 'in'
if units == 'in':
# modern versions of Inkscape use CSS's 96 pixels per inch. When you
# open an old document, inkscape will add a viewbox for you.
return value * 96
raise ValueError(_("Unknown unit: %s") % units)
@cache
def get_doc_size(svg):
doc_width = convert_length(svg.get('width'))
doc_height = convert_length(svg.get('height'))
return doc_width, doc_height
@cache
def get_viewbox_transform(node):
# somewhat cribbed from inkscape-silhouette
doc_width, doc_height = get_doc_size(node)
viewbox = node.get('viewBox').strip().replace(',', ' ').split()
dx = -float(viewbox[0])
dy = -float(viewbox[1])
transform = simpletransform.parseTransform("translate(%f, %f)" % (dx, dy))
try:
sx = doc_width / float(viewbox[2])
sy = doc_height / float(viewbox[3])
scale_transform = simpletransform.parseTransform("scale(%f, %f)" % (sx, sy))
transform = simpletransform.composeTransform(transform, scale_transform)
except ZeroDivisionError:
pass
return transform
@cache
def get_stroke_scale(node):
doc_width, doc_height = get_doc_size(node)
viewbox = node.get('viewBox').strip().replace(',', ' ').split()
return doc_width / float(viewbox[2])
class Stitch(Point):
def __init__(self, x, y, color=None, jump=False, stop=False, trim=False, no_ties=False):
self.x = x
self.y = y
self.color = color
self.jump = jump
self.trim = trim
self.stop = stop
self.no_ties = no_ties
def __repr__(self):
return "Stitch(%s, %s, %s, %s, %s, %s, %s)" % (self.x, self.y, self.color, "JUMP" if self.jump else " ", "TRIM" if self.trim else " ", "STOP" if self.stop else " ", "NO TIES" if self.no_ties else " ")
def make_thread(color):
thread = libembroidery.EmbThread()
thread.color = libembroidery.embColor_make(*color.rgb)
thread.description = color.name
thread.catalogNumber = ""
return thread
def add_thread(pattern, thread):
"""Add a thread to a pattern and return the thread's index"""
libembroidery.embPattern_addThread(pattern, thread)
return libembroidery.embThreadList_count(pattern.threadList) - 1
def get_flags(stitch):
flags = 0
if stitch.jump:
flags |= libembroidery.JUMP
if stitch.trim:
flags |= libembroidery.TRIM
if stitch.stop:
flags |= libembroidery.STOP
return flags
def _string_to_floats(string):
floats = string.split(',')
return [float(num) for num in floats]
def get_origin(svg):
# The user can specify the embroidery origin by defining two guides
# named "embroidery origin" that intersect.
namedview = svg.find(inkex.addNS('namedview', 'sodipodi'))
all_guides = namedview.findall(inkex.addNS('guide', 'sodipodi'))
label_attribute = inkex.addNS('label', 'inkscape')
guides = [guide for guide in all_guides
if guide.get(label_attribute, "").startswith("embroidery origin")]
# document size used below
doc_size = list(get_doc_size(svg))
# convert the size from viewbox-relative to real-world pixels
viewbox_transform = get_viewbox_transform(svg)
simpletransform.applyTransformToPoint(simpletransform.invertTransform(viewbox_transform), doc_size)
default = [doc_size[0] / 2.0, doc_size[1] / 2.0]
simpletransform.applyTransformToPoint(viewbox_transform, default)
default = Point(*default)
if len(guides) < 2:
return default
# Find out where the guides intersect. Only pay attention to the first two.
guides = guides[:2]
lines = []
for guide in guides:
# inkscape's Y axis is reversed from SVG's, and the guide is in inkscape coordinates
position = Point(*_string_to_floats(guide.get('position')))
position.y = doc_size[1] - position.y
# This one baffles me. I think inkscape might have gotten the order of
# their vector wrong?
parts = _string_to_floats(guide.get('orientation'))
direction = Point(parts[1], parts[0])
# We have a theoretically infinite line defined by a point on the line
# and a vector direction. Shapely can only deal in concrete line
# segments, so we'll pick points really far in either direction on the
# line and call it good enough.
lines.append(shgeo.LineString((position + 100000 * direction, position - 100000 * direction)))
intersection = lines[0].intersection(lines[1])
if isinstance(intersection, shgeo.Point):
origin = [intersection.x, intersection.y]
simpletransform.applyTransformToPoint(viewbox_transform, origin)
return Point(*origin)
else:
# Either the two guides are the same line, or they're parallel.
return default
def write_embroidery_file(file_path, stitch_plan, svg):
origin = get_origin(svg)
pattern = libembroidery.embPattern_create()
for color_block in stitch_plan:
add_thread(pattern, make_thread(color_block.color))
for stitch in color_block:
if stitch.stop and stitch is not color_block.last_stitch:
# A STOP stitch that is not at the end of a color block
# occurs when the user specified "STOP after". "STOP" is the
# same thing as a color change, and the user will assign a
# special color at the machine that tells it to pause after.
# We need to add another copy of the same color here so that
# the stitches after the STOP are still the same color.
add_thread(pattern, make_thread(color_block.color))
flags = get_flags(stitch)
libembroidery.embPattern_addStitchAbs(pattern, stitch.x - origin.x, stitch.y - origin.y, flags, 1)
libembroidery.embPattern_addStitchAbs(pattern, stitch.x - origin.x, stitch.y - origin.y, libembroidery.END, 1)
# convert from pixels to millimeters
libembroidery.embPattern_scale(pattern, 1/PIXELS_PER_MM)
# SVG and embroidery disagree on the direction of the Y axis
libembroidery.embPattern_flipVertical(pattern)
libembroidery.embPattern_write(pattern, file_path)

Wyświetl plik

@ -1,10 +1,10 @@
import math
from .. import _
from .element import param, Patch
from ..utils import cache
from .fill import Fill
from shapely import geometry as shgeo
from ..i18n import _
from ..utils import cache
from ..stitches import auto_fill
from .element import param, Patch
from .fill import Fill
class AutoFill(Fill):

Wyświetl plik

@ -1,9 +1,10 @@
import sys
from copy import deepcopy
from ..utils import cache
from shapely import geometry as shgeo
from .. import _, PIXELS_PER_MM, get_viewbox_transform, get_stroke_scale, convert_length
from ..i18n import _
from ..utils import cache
from ..svg import PIXELS_PER_MM, get_viewbox_transform, convert_length, get_doc_size
# inkscape-provided utilities
import simpletransform
@ -11,6 +12,7 @@ import simplestyle
import cubicsuperpath
from cspsubdiv import cspsubdiv
class Patch:
"""A raw collection of stitches with attached instructions."""
@ -144,6 +146,15 @@ class EmbroideryElement(object):
style = simplestyle.parseStyle(self.node.get("style"))
return style_name in style
@property
@cache
def stroke_scale(self):
svg = self.node.getroottree().getroot()
doc_width, doc_height = get_doc_size(svg)
viewbox = svg.get('viewBox', '0 0 %s %s' % (doc_width, doc_height))
viewbox = viewbox.strip().replace(',', ' ').split()
return doc_width / float(viewbox[2])
@property
@cache
def stroke_width(self):
@ -153,8 +164,7 @@ class EmbroideryElement(object):
return 1.0
width = convert_length(width)
return width * get_stroke_scale(self.node.getroottree().getroot())
return width * self.stroke_scale
@property
def path(self):

Wyświetl plik

@ -1,10 +1,13 @@
from .. import _, PIXELS_PER_MM
from .element import param, EmbroideryElement, Patch
from ..utils import cache
from shapely import geometry as shgeo
import math
from .element import param, EmbroideryElement, Patch
from ..i18n import _
from ..svg import PIXELS_PER_MM
from ..utils import cache
from ..stitches import running_stitch, auto_fill, legacy_fill
class Fill(EmbroideryElement):
element_name = _("Fill")

Wyświetl plik

@ -1,5 +1,6 @@
from .. import _, Point
from .element import param, EmbroideryElement, Patch
from ..i18n import _
from ..utils.geometry import Point
from ..utils import cache

Wyświetl plik

@ -1,10 +1,10 @@
from itertools import chain, izip
from .. import _, Point
from .element import param, EmbroideryElement, Patch
from ..utils import cache
from shapely import geometry as shgeo, ops as shops
from .element import param, EmbroideryElement, Patch
from ..i18n import _
from ..utils import cache, Point
class SatinColumn(EmbroideryElement):
element_name = _("Satin Column")

Wyświetl plik

@ -1,7 +1,8 @@
import sys
from .. import _, Point
from .element import param, EmbroideryElement, Patch
from ..utils import cache
from ..i18n import _
from ..utils import cache, Point
warned_about_legacy_running_stitch = False

Wyświetl plik

@ -3,8 +3,9 @@ import re
import json
from copy import deepcopy
from collections import MutableMapping
from ..svg.tags import *
from ..elements import AutoFill, Fill, Stroke, SatinColumn, Polyline, EmbroideryElement
from .. import SVG_POLYLINE_TAG, SVG_GROUP_TAG, SVG_DEFS_TAG, INKSCAPE_GROUPMODE, EMBROIDERABLE_TAGS, PIXELS_PER_MM
from ..utils import cache

Wyświetl plik

@ -1,12 +1,13 @@
import sys
import traceback
import os
import inkex
from .. import _, PIXELS_PER_MM, write_embroidery_file
from .base import InkstitchExtension
from ..i18n import _
from ..output import write_embroidery_file
from ..stitch_plan import patches_to_stitch_plan
from ..svg import render_stitch_plan
from ..svg import render_stitch_plan, PIXELS_PER_MM
class Embroider(InkstitchExtension):

Wyświetl plik

@ -1,17 +1,19 @@
import os
from os.path import realpath, dirname, join as path_join
import sys
from inkex import etree
import inkex
# help python find libembroidery when running in a local repo clone
if getattr(sys, 'frozen', None) is None:
sys.path.append(realpath(path_join(dirname(__file__), '..', '..')))
from libembroidery import *
from inkex import etree
import inkex
from .. import PIXELS_PER_MM, INKSCAPE_LABEL, _
from ..svg import PIXELS_PER_MM, render_stitch_plan
from ..svg.tags import INKSCAPE_LABEL
from ..i18n import _
from ..stitch_plan import StitchPlan
from ..svg import render_stitch_plan
class Input(object):

Wyświetl plik

@ -10,6 +10,7 @@ import time
import logging
import wx
import inkex
from ..utils import guess_inkscape_config_path

Wyświetl plik

@ -13,8 +13,8 @@ from collections import defaultdict
from functools import partial
from itertools import groupby
from .. import _
from .base import InkstitchExtension
from ..i18n import _
from ..stitch_plan import patches_to_stitch_plan
from ..elements import EmbroideryElement, Fill, AutoFill, Stroke, SatinColumn
from ..utils import save_stderr, restore_stderr

Wyświetl plik

@ -10,22 +10,21 @@ from copy import deepcopy
import wx
import appdirs
import json
import inkex
from .. import _, PIXELS_PER_MM, SVG_GROUP_TAG, translation as inkstitch_translation
from .base import InkstitchExtension
from ..stitch_plan import patches_to_stitch_plan
from ..svg import render_stitch_plan
from ..threads import ThreadCatalog
from jinja2 import Environment, FileSystemLoader, select_autoescape
from datetime import date
import base64
from flask import Flask, request, Response, send_from_directory, jsonify
import webbrowser
import requests
from .base import InkstitchExtension
from ..i18n import _, translation as inkstitch_translation
from ..svg import PIXELS_PER_MM, render_stitch_plan
from ..svg.tags import SVG_GROUP_TAG
from ..stitch_plan import patches_to_stitch_plan
from ..threads import ThreadCatalog
def datetimeformat(value, format='%Y/%m/%d'):
return value.strftime(format)

Wyświetl plik

@ -1,6 +1,7 @@
import wx
from .base import InkstitchExtension
from ..i18n import _
from ..simulator import EmbroiderySimulator
from ..stitch_plan import patches_to_stitch_plan

21
lib/i18n.py 100644
Wyświetl plik

@ -0,0 +1,21 @@
import sys
import os
import gettext
_ = translation = None
def localize():
if getattr(sys, 'frozen', False):
# we are in a pyinstaller installation
locale_dir = sys._MEIPASS
else:
locale_dir = os.path.dirname(__file__)
locale_dir = os.path.join(locale_dir, 'locales')
global translation, _
translation = gettext.translation("inkstitch", locale_dir, fallback=True)
_ = translation.gettext
localize()

130
lib/output.py 100644
Wyświetl plik

@ -0,0 +1,130 @@
import libembroidery
import inkex
import simpletransform
from .utils import Point
from .svg import PIXELS_PER_MM, get_doc_size, get_viewbox_transform
def make_thread(color):
thread = libembroidery.EmbThread()
thread.color = libembroidery.embColor_make(*color.rgb)
thread.description = color.name
thread.catalogNumber = ""
return thread
def add_thread(pattern, thread):
"""Add a thread to a pattern and return the thread's index"""
libembroidery.embPattern_addThread(pattern, thread)
return libembroidery.embThreadList_count(pattern.threadList) - 1
def get_flags(stitch):
flags = 0
if stitch.jump:
flags |= libembroidery.JUMP
if stitch.trim:
flags |= libembroidery.TRIM
if stitch.stop:
flags |= libembroidery.STOP
return flags
def _string_to_floats(string):
floats = string.split(',')
return [float(num) for num in floats]
def get_origin(svg):
# The user can specify the embroidery origin by defining two guides
# named "embroidery origin" that intersect.
namedview = svg.find(inkex.addNS('namedview', 'sodipodi'))
all_guides = namedview.findall(inkex.addNS('guide', 'sodipodi'))
label_attribute = inkex.addNS('label', 'inkscape')
guides = [guide for guide in all_guides
if guide.get(label_attribute, "").startswith("embroidery origin")]
# document size used below
doc_size = list(get_doc_size(svg))
# convert the size from viewbox-relative to real-world pixels
viewbox_transform = get_viewbox_transform(svg)
simpletransform.applyTransformToPoint(simpletransform.invertTransform(viewbox_transform), doc_size)
default = [doc_size[0] / 2.0, doc_size[1] / 2.0]
simpletransform.applyTransformToPoint(viewbox_transform, default)
default = Point(*default)
if len(guides) < 2:
return default
# Find out where the guides intersect. Only pay attention to the first two.
guides = guides[:2]
lines = []
for guide in guides:
# inkscape's Y axis is reversed from SVG's, and the guide is in inkscape coordinates
position = Point(*_string_to_floats(guide.get('position')))
position.y = doc_size[1] - position.y
# This one baffles me. I think inkscape might have gotten the order of
# their vector wrong?
parts = _string_to_floats(guide.get('orientation'))
direction = Point(parts[1], parts[0])
# We have a theoretically infinite line defined by a point on the line
# and a vector direction. Shapely can only deal in concrete line
# segments, so we'll pick points really far in either direction on the
# line and call it good enough.
lines.append(shgeo.LineString((position + 100000 * direction, position - 100000 * direction)))
intersection = lines[0].intersection(lines[1])
if isinstance(intersection, shgeo.Point):
origin = [intersection.x, intersection.y]
simpletransform.applyTransformToPoint(viewbox_transform, origin)
return Point(*origin)
else:
# Either the two guides are the same line, or they're parallel.
return default
def write_embroidery_file(file_path, stitch_plan, svg):
origin = get_origin(svg)
pattern = libembroidery.embPattern_create()
for color_block in stitch_plan:
add_thread(pattern, make_thread(color_block.color))
for stitch in color_block:
if stitch.stop and stitch is not color_block.last_stitch:
# A STOP stitch that is not at the end of a color block
# occurs when the user specified "STOP after". "STOP" is the
# same thing as a color change, and the user will assign a
# special color at the machine that tells it to pause after.
# We need to add another copy of the same color here so that
# the stitches after the STOP are still the same color.
add_thread(pattern, make_thread(color_block.color))
flags = get_flags(stitch)
libembroidery.embPattern_addStitchAbs(pattern, stitch.x - origin.x, stitch.y - origin.y, flags, 1)
libembroidery.embPattern_addStitchAbs(pattern, stitch.x - origin.x, stitch.y - origin.y, libembroidery.END, 1)
# convert from pixels to millimeters
libembroidery.embPattern_scale(pattern, 1/PIXELS_PER_MM)
# SVG and embroidery disagree on the direction of the Y axis
libembroidery.embPattern_flipVertical(pattern)
libembroidery.embPattern_write(pattern, file_path)

Wyświetl plik

@ -4,8 +4,7 @@ import wx
import colorsys
from itertools import izip
from . import PIXELS_PER_MM
from .svg import color_block_to_point_lists
from .svg import PIXELS_PER_MM, color_block_to_point_lists
class EmbroiderySimulator(wx.Frame):

Wyświetl plik

@ -1 +1,2 @@
from stitch_plan import patches_to_stitch_plan, StitchPlan, ColorBlock
from .stitch import Stitch

Wyświetl plik

@ -0,0 +1,15 @@
from ..utils.geometry import Point
class Stitch(Point):
def __init__(self, x, y, color=None, jump=False, stop=False, trim=False, no_ties=False):
self.x = x
self.y = y
self.color = color
self.jump = jump
self.trim = trim
self.stop = stop
self.no_ties = no_ties
def __repr__(self):
return "Stitch(%s, %s, %s, %s, %s, %s, %s)" % (self.x, self.y, self.color, "JUMP" if self.jump else " ", "TRIM" if self.trim else " ", "STOP" if self.stop else " ", "NO TIES" if self.no_ties else " ")

Wyświetl plik

@ -1,8 +1,9 @@
from .. import Stitch, PIXELS_PER_MM
from ..utils.geometry import Point
from .stitch import Stitch
from .stop import process_stop
from .trim import process_trim
from .ties import add_ties
from ..svg import PIXELS_PER_MM
from ..utils.geometry import Point
from ..threads import ThreadColor

Wyświetl plik

@ -1,7 +1,9 @@
from copy import deepcopy
from .stitch import Stitch
from ..utils import cut_path
from ..stitches import running_stitch
from .. import Stitch
from copy import deepcopy
def add_tie(stitches, tie_path):
if stitches[-1].no_ties:

Wyświetl plik

@ -1,5 +1,3 @@
from fill import intersect_region_with_grating, row_num, stitch_row
from .. import _, PIXELS_PER_MM, Point as InkstitchPoint
import sys
import shapely
import networkx
@ -7,6 +5,11 @@ import math
from itertools import groupby
from collections import deque
from .fill import intersect_region_with_grating, row_num, stitch_row
from ..i18n import _
from ..svg import PIXELS_PER_MM
from ..utils.geometry import Point as InkstitchPoint
class MaxQueueLengthExceeded(Exception):
pass

Wyświetl plik

@ -1,9 +1,10 @@
from .. import PIXELS_PER_MM
from ..utils import cache, Point as InkstitchPoint
import shapely
import math
import sys
from ..svg import PIXELS_PER_MM
from ..utils import cache, Point as InkstitchPoint
def legacy_fill(shape, angle, row_spacing, end_row_spacing, max_stitch_length, flip, staggers):
rows_of_segments = intersect_region_with_grating(shape, angle, row_spacing, end_row_spacing, flip)
@ -242,4 +243,3 @@ def pull_runs(rows, shape, row_spacing):
count += 1
return runs

Wyświetl plik

@ -0,0 +1,2 @@
from .svg import color_block_to_point_lists, render_stitch_plan
from .units import *

Wyświetl plik

@ -1,5 +1,10 @@
import simpletransform, simplestyle, inkex
from . import _, get_viewbox_transform, cache, SVG_GROUP_TAG, INKSCAPE_LABEL, INKSCAPE_GROUPMODE, SVG_PATH_TAG
from .units import get_viewbox_transform
from .tags import SVG_GROUP_TAG, INKSCAPE_LABEL, INKSCAPE_GROUPMODE, SVG_PATH_TAG
from ..i18n import _
from ..utils import cache
def color_block_to_point_lists(color_block):
point_lists = [[]]

12
lib/svg/tags.py 100644
Wyświetl plik

@ -0,0 +1,12 @@
import inkex
SVG_PATH_TAG = inkex.addNS('path', 'svg')
SVG_POLYLINE_TAG = inkex.addNS('polyline', 'svg')
SVG_DEFS_TAG = inkex.addNS('defs', 'svg')
SVG_GROUP_TAG = inkex.addNS('g', 'svg')
INKSCAPE_LABEL = inkex.addNS('label', 'inkscape')
INKSCAPE_GROUPMODE = inkex.addNS('groupmode', 'inkscape')
EMBROIDERABLE_TAGS = (SVG_PATH_TAG, SVG_POLYLINE_TAG)

105
lib/svg/units.py 100644
Wyświetl plik

@ -0,0 +1,105 @@
import simpletransform
from ..utils import cache
# modern versions of Inkscape use 96 pixels per inch as per the CSS standard
PIXELS_PER_MM = 96 / 25.4
# cribbed from inkscape-silhouette
def parse_length_with_units( str ):
'''
Parse an SVG value which may or may not have units attached
This version is greatly simplified in that it only allows: no units,
units of px, mm, and %. Everything else, it returns None for.
There is a more general routine to consider in scour.py if more
generality is ever needed.
'''
u = 'px'
s = str.strip()
if s[-2:] == 'px':
s = s[:-2]
elif s[-2:] == 'mm':
u = 'mm'
s = s[:-2]
elif s[-2:] == 'pt':
u = 'pt'
s = s[:-2]
elif s[-2:] == 'pc':
u = 'pc'
s = s[:-2]
elif s[-2:] == 'cm':
u = 'cm'
s = s[:-2]
elif s[-2:] == 'in':
u = 'in'
s = s[:-2]
elif s[-1:] == '%':
u = '%'
s = s[:-1]
try:
v = float( s )
except:
raise ValueError(_("parseLengthWithUnits: unknown unit %s") % s)
return v, u
def convert_length(length):
value, units = parse_length_with_units(length)
if not units or units == "px":
return value
if units == 'pt':
value /= 72
units = 'in'
if units == 'pc':
value /= 6
units = 'in'
if units == 'cm':
value *= 10
units = 'mm'
if units == 'mm':
value = value / 25.4
units = 'in'
if units == 'in':
# modern versions of Inkscape use CSS's 96 pixels per inch. When you
# open an old document, inkscape will add a viewbox for you.
return value * 96
raise ValueError(_("Unknown unit: %s") % units)
@cache
def get_doc_size(svg):
doc_width = convert_length(svg.get('width'))
doc_height = convert_length(svg.get('height'))
return doc_width, doc_height
@cache
def get_viewbox_transform(node):
# somewhat cribbed from inkscape-silhouette
doc_width, doc_height = get_doc_size(node)
viewbox = node.get('viewBox').strip().replace(',', ' ').split()
dx = -float(viewbox[0])
dy = -float(viewbox[1])
transform = simpletransform.parseTransform("translate(%f, %f)" % (dx, dy))
try:
sx = doc_width / float(viewbox[2])
sy = doc_height / float(viewbox[3])
scale_transform = simpletransform.parseTransform("scale(%f, %f)" % (sx, sy))
transform = simpletransform.composeTransform(transform, scale_transform)
except ZeroDivisionError:
pass
return transform

Wyświetl plik

@ -3,8 +3,10 @@ from os.path import dirname, realpath
import sys
from glob import glob
from collections import Sequence
from .palette import ThreadPalette
class _ThreadCatalog(Sequence):
"""Holds a set of ThreadPalettes."""

Wyświetl plik

@ -1,9 +1,10 @@
from collections import Set
from .color import ThreadColor
from colormath.color_objects import sRGBColor, LabColor
from colormath.color_conversions import convert_color
from colormath.color_diff import delta_e_cie1994
from .color import ThreadColor
def compare_thread_colors(color1, color2):
# K_L=2 indicates textiles

Wyświetl plik

@ -2,6 +2,7 @@ import os
import sys
from cStringIO import StringIO
def save_stderr():
# GTK likes to spam stderr, which inkscape will show in a dialog.
null = open(os.devnull, 'w')

Wyświetl plik

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2018-04-29 21:45-0400\n"
"POT-Creation-Date: 2018-05-01 21:21-0400\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -17,17 +17,6 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.5.3\n"
#, python-format
msgid "parseLengthWithUnits: unknown unit %s"
msgstr ""
#, python-format
msgid "Unknown unit: %s"
msgstr ""
msgid "Stitch Plan"
msgstr ""
msgid "Auto-Fill"
msgstr ""
@ -333,6 +322,17 @@ msgid ""
"file to lexelby@github."
msgstr ""
msgid "Stitch Plan"
msgstr ""
#, python-format
msgid "parseLengthWithUnits: unknown unit %s"
msgstr ""
#, python-format
msgid "Unknown unit: %s"
msgstr ""
msgid "Color"
msgstr ""