convex_hull for faster rotate_and_scale_to_fit

This commit is contained in:
Michael Fogleman 2018-02-10 17:52:56 -05:00
parent 3b35f93223
commit 38c71e1fe5
3 changed files with 42 additions and 18 deletions

View File

@ -2,7 +2,8 @@ from __future__ import division
from math import sin, cos, radians from math import sin, cos, radians
from .paths import simplify_paths, sort_paths, join_paths, crop_paths from .paths import (
simplify_paths, sort_paths, join_paths, crop_paths, convex_hull)
try: try:
import cairo import cairo
@ -61,10 +62,18 @@ class Drawing(object):
with open(filename, 'w') as fp: with open(filename, 'w') as fp:
fp.write(self.dumps_svg()) fp.write(self.dumps_svg())
@property
def points(self):
return [(x, y) for path in self.paths for x, y in path]
@property
def convex_hull(self):
return convex_hull(self.points)
@property @property
def bounds(self): def bounds(self):
if not self._bounds: if not self._bounds:
points = [(x, y) for path in self.paths for x, y in path] points = self.points
if points: if points:
x1 = min(x for x, y in points) x1 = min(x for x, y in points)
x2 = max(x for x, y in points) x2 = max(x for x, y in points)
@ -168,16 +177,18 @@ class Drawing(object):
scale = min(width / self.width, height / self.height) scale = min(width / self.width, height / self.height)
return self.scale(scale, scale).center(width, height) return self.scale(scale, scale).center(width, height)
def rotate_and_scale_to_fit(self, width, height, padding=0, step=5): def rotate_and_scale_to_fit(self, width, height, padding=0, step=1):
drawings = [] values = []
width -= padding * 2 width -= padding * 2
height -= padding * 2 height -= padding * 2
hull = Drawing([self.convex_hull])
for angle in range(0, 180, step): for angle in range(0, 180, step):
drawing = self.rotate(angle) d = hull.rotate(angle)
scale = min(width / drawing.width, height / drawing.height) scale = min(width / d.width, height / d.height)
drawings.append((scale, angle, drawing)) print angle, d.width, d.height
scale, angle, drawing = max(drawings) values.append((scale, angle))
return drawing.scale(scale, scale).center(width, height) scale, angle = max(values)
return self.rotate(angle).scale(scale, scale).center(width, height)
def remove_paths_outside(self, width, height): def remove_paths_outside(self, width, height):
e = 1e-8 e = 1e-8
@ -192,10 +203,12 @@ class Drawing(object):
paths.append(path) paths.append(path)
return Drawing(paths) return Drawing(paths)
def render(self, scale=109, margin=1, line_width=0.5/25.4, show_bounds=True): def render(self, scale=109, margin=1, line_width=0.5/25.4,
use_axi_bounds=True, show_axi_bounds=True):
if cairo is None: if cairo is None:
raise Exception('Drawing.render() requires cairo') raise Exception('Drawing.render() requires cairo')
# x1, y1, x2, y2 = self.bounds x1, y1, x2, y2 = self.bounds
if use_axi_bounds:
x1, y1, x2, y2 = (0, 0, 12, 8.5) x1, y1, x2, y2 = (0, 0, 12, 8.5)
w = x2 - x1 w = x2 - x1
h = y2 - y1 h = y2 - y1
@ -211,7 +224,7 @@ class Drawing(object):
dc.translate(-x1, -y1) dc.translate(-x1, -y1)
dc.set_source_rgb(1, 1, 1) dc.set_source_rgb(1, 1, 1)
dc.paint() dc.paint()
if show_bounds: if show_axi_bounds:
dc.set_source_rgb(0.5, 0.5, 0.5) dc.set_source_rgb(0.5, 0.5, 0.5)
dc.set_line_width(1 / scale) dc.set_line_width(1 / scale)
dc.rectangle(0, 0, 12, 8.5) dc.rectangle(0, 0, 12, 8.5)

View File

@ -1,5 +1,5 @@
from math import hypot from math import hypot
from shapely.geometry import LineString from shapely import geometry
from .spatial import Index from .spatial import Index
@ -17,7 +17,7 @@ def load_paths(filename):
def simplify_path(points, tolerance): def simplify_path(points, tolerance):
if len(points) < 2: if len(points) < 2:
return points return points
line = LineString(points) line = geometry.LineString(points)
line = line.simplify(tolerance, preserve_topology=False) line = line.simplify(tolerance, preserve_topology=False)
return list(line.coords) return list(line.coords)
@ -109,3 +109,13 @@ def crop_paths(paths, x1, y1, x2, y2):
for path in paths: for path in paths:
result.extend(crop_path(path, x1, y1, x2, y2)) result.extend(crop_path(path, x1, y1, x2, y2))
return result return result
def convex_hull(points):
hull = geometry.MultiPoint(points).convex_hull
if isinstance(hull, geometry.Polygon):
return list(hull.exterior.coords)
if isinstance(hull, geometry.LineString):
return list(hull.coords)
if isinstance(hull, geometry.Point):
return list(hull.coords)
raise Exception('unhandled convex hull geometry')

View File

@ -84,15 +84,16 @@ def main():
ds = [] ds = []
for filename, angle in zip(filenames, angles): for filename, angle in zip(filenames, angles):
ds.append(axi.Drawing(axi.load_paths(filename)).scale_to_fit(8.5, 12).scale(1, -1)) ds.append(axi.Drawing(axi.load_paths(filename)).scale_to_fit(8.5, 12).scale(1, -1))
d = grid_drawings(ds, 2, 1) # d = grid_drawings(ds, 2, 1)
d = ds[0]
print len(d.paths) print len(d.paths)
print 'joining paths' print 'joining paths'
d = d.join_paths(0.01) d = d.join_paths(0.01)
print len(d.paths) print len(d.paths)
print 'transforming paths' print 'transforming paths'
# d = d.scale(1, -1) # d = d.scale(1, -1)
d = d.rotate_and_scale_to_fit(8.5, 12 - text.height - 0.75, step=5) d = d.rotate_and_scale_to_fit(8.5, 12 - text.height - 0.75)
d = d.origin() # d = d.origin()
print 'sorting paths' print 'sorting paths'
d = d.sort_paths() d = d.sort_paths()
print 'joining paths' print 'joining paths'