This repository has been archived on 2023-03-25. You can view files and clone it, but cannot push or open issues or pull requests.
mightyscape-1.1-deprecated/extensions/fablabchemnitz/cutcraft/cutcraft/core/trace.py

150 lines
4.9 KiB
Python

# Copyright (C) 2018 Michael Matthews
#
# This file is part of CutCraft.
#
# CutCraft 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.
#
# CutCraft 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 CutCraft. If not, see <http://www.gnu.org/licenses/>.
from .point import Point
from .line import Line
from ..util import isclose, iscloselist
class Trace(object):
""" List of coordinates that make a boundary. """
def __init__(self, x=None, y=None):
self.x = [] if x is None else x
self.y = [] if y is None else y
self.closed = False
return
def close(self):
""" Close the trace back to the start. """
if not self.closed:
if isclose(self.x[0], self.x[-1]) and isclose(self.y[0], self.y[-1]):
# Start and end should be the same.
self.x[-1] = self.x[0]
self.y[-1] = self.y[0]
else:
# Add new end point to close the loop.
self.x.append(self.x[0])
self.y.append(self.y[0])
self.closed = True
return
def applykerf(self, kerf):
""" Apply an offset to allow for the kerf when cutting. """
self.close()
# Convert the points to lines.
lines = [Line(Point(x1, y1), Point(x2, y2)) for x1, y1, x2, y2 in
zip(self.x[:-1], self.y[:-1], self.x[1:], self.y[1:])]
# Add the kerf to the lines.
for line in lines:
line.addkerf(kerf)
# Extract the line intersections as the new points.
pts = [line1.intersection(line2) for line1, line2 in zip(lines, lines[-1:] + lines[:-1])]
self.clear()
self.x += [pt.x for pt in pts]
self.y += [pt.y for pt in pts]
self.x += self.x[:1]
self.y += self.y[:1]
return
def offset(self, pt):
""" Move a trace by an x/y offset. """
self.x = [x + pt.x for x in self.x]
self.y = [y + pt.y for y in self.y]
def clear(self):
self.x = []
self.y = []
def svg(self):
# Generate SVG string for this trace.
if len(self.x)<2:
return ""
return "M {} {} ".format(self.x[0], self.y[0]) + \
" ".join(["L {} {}".format(x, y) for x, y in zip(self.x[1:], self.y[1:])])
def bbox(self):
return Point(min(self.x), min(self.y)), Point(max(self.x), max(self.y))
def __len__(self):
return len(self.x)
def __eq__(self, other):
return (iscloselist(self.x,other.x) and iscloselist(self.y, other.y))
def __ne__(self, other):
return (not iscloselist(self.x, other.x) or not iscloselist(self.y, other.y))
def __add__(self, other):
new = Trace()
if isinstance(other, Point):
new.x = self.x + [other.x]
new.y = self.y + [other.y]
elif isinstance(other, Trace):
new.x = self.x + other.x
new.y = self.y + other.y
else:
raise RuntimeError("Can only add a Trace or Point to an existing Trace.")
return new
def __iadd__(self, other):
if isinstance(other, Point):
self.x.append(other.x)
self.y.append(other.y)
elif isinstance(other, Trace):
self.x += other.x
self.y += other.y
else:
raise RuntimeError("Can only add a Trace or Point to an existing Trace.")
return self
def __repr__(self):
return "Trace(" + str(self.x) + ", " + str(self.y) + ")"
def __str__(self):
return "(" + str(self.x) + ", " + str(self.y) + ")"
def __getitem__(self, key):
""" Used to override the slice functionality (eg: reversing). """
new = Trace()
new.x = self.x[key]
new.y = self.y[key]
return new
def __setitem__(self, key, value):
""" Used to override the slice functionality. """
if isinstance(value, Point):
self.x[key] = value.x
self.y[key] = value.y
else:
raise RuntimeError("Can only update a single item in an existing Trace.")
return self
def __delitem__(self, key):
""" Used to override the slice functionality (eg: reversing). """
del self.x[key]
del self.y[key]
return self
def __reversed__(self):
""" Used to override the slice functionality (eg: reversing). """
new = Trace()
new.x = list(reversed(self.x))
new.y = list(reversed(self.y))
return new