Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added ponoko-specific changes to color and line width. #105

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions boxes/Color.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class Color:
BLACK = [ 0.0, 0.0, 0.0 ]
BLUE = [ 0.0, 0.0, 1.0 ]
GREEN = [ 0.0, 1.0, 0.0 ]
RED = [ 1.0, 0.0, 0.0 ]
WHITE = [ 1.0, 1.0, 1.0 ]
154 changes: 83 additions & 71 deletions boxes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,15 @@
import re
from functools import wraps
from xml.sax.saxutils import quoteattr
from contextlib import contextmanager

from boxes import edges
from boxes import formats
from boxes import svgutil
from boxes import gears
from boxes import pulley
from boxes import parts

from boxes.Color import *

### Helpers

Expand All @@ -48,6 +49,17 @@ def dist(dx, dy):
"""
return (dx * dx + dy * dy) ** 0.5

@contextmanager
def saved(cr):
"""
Generator: for saving and restoring cairo contexts.
:param cr: cairo context
"""
cr.save()
try:
yield cr
finally:
cr.restore()

def restore(func):
"""
Expand All @@ -59,10 +71,9 @@ def restore(func):

@wraps(func)
def f(self, *args, **kw):
self.ctx.save()
pt = self.ctx.get_current_point()
func(self, *args, **kw)
self.ctx.restore()
with saved(self.ctx):
pt = self.ctx.get_current_point()
func(self, *args, **kw)
self.ctx.move_to(*pt)

return f
Expand All @@ -79,10 +90,10 @@ def holeCol(func):
@wraps(func)
def f(self, *args, **kw):
self.ctx.stroke()
self.ctx.set_source_rgb(0.0, 0.0, 1.0)
func(self, *args, **kw)
self.ctx.stroke()
self.ctx.set_source_rgb(0.0, 0.0, 0.0)
with saved(self.ctx):
self.ctx.set_source_rgb(*Color.BLUE)
func(self, *args, **kw)
self.ctx.stroke()

return f

Expand Down Expand Up @@ -264,7 +275,12 @@ def open(self):
self.bedBoltSettings = (3, 5.5, 2, 20, 15) # d, d_nut, h_nut, l, l1
self.hexHolesSettings = (5, 3, 'circle') # r, dist, style
self.surface, self.ctx = self.formats.getSurface(self.format, self.output)
self.ctx.set_line_width(max(2 * self.burn, 0.05))
self.ctx.set_source_rgb(*Color.BLACK)

if self.format_variant == 'svg_Ponoko':
self.ctx.set_source_rgb(*Color.BLUE)
self.ctx.set_line_width(0.01)

self.ctx.select_font_face("sans-serif")
self._buildObjects()
if self.reference:
Expand Down Expand Up @@ -385,8 +401,14 @@ def parseArgs(self, args=None):
setattr(self, key, value)

# Change file ending to format if not given explicitly
format = getattr(self, "format", "svg")
if format == "svg_Ponoko":
setattr(self, "format_variant", format)
setattr(self, "format", "svg")
format = "svg"

if getattr(self, 'output', None) == 'box.svg':
self.output = 'box.' + getattr(self, "format", "svg")
self.output = 'box.' + format

def addPart(self, part, name=None):
"""
Expand Down Expand Up @@ -512,13 +534,12 @@ def cc(self, callback, number, x=0.0, y=None):
pass

if callback and callable(callback):
self.ctx.save()
self.moveTo(x, y)
if number is None:
callback()
else:
callback(number)
self.ctx.restore()
with saved(self.ctx):
self.moveTo(x, y)
if number is None:
callback()
else:
callback(number)
self.ctx.move_to(0, 0)

def getEntry(self, param, idx):
Expand Down Expand Up @@ -871,20 +892,18 @@ def handle(self, x, h, hl, r=30):
"""
d = (x - hl - 2 * r) / 2.0

self.ctx.save()

# Hole
self.moveTo(d + 2 * r, 0)
self.edge(hl - 2 * r)
self.corner(-90, r)
self.edge(h - 3 * r)
self.corner(-90, r)
self.edge(hl - 2 * r)
self.corner(-90, r)
self.edge(h - 3 * r)
self.corner(-90, r)
with saved(self.ctx):
self.moveTo(d + 2 * r, 0)
self.edge(hl - 2 * r)
self.corner(-90, r)
self.edge(h - 3 * r)
self.corner(-90, r)
self.edge(hl - 2 * r)
self.corner(-90, r)
self.edge(h - 3 * r)
self.corner(-90, r)

self.ctx.restore()
self.moveTo(0, 0)

self.curveTo(d, 0, d, 0, d, -h + r)
Expand Down Expand Up @@ -1134,15 +1153,15 @@ def text(self, text, x=0, y=0, angle=0, align="", fontsize=10, color=[0.0, 0.0,
raise ValueError("Unknown alignment: %s" % align)

self.ctx.stroke()
self.ctx.set_source_rgb(1.0, 1.0, 1.0)
self.ctx.set_source_rgb(*Color.WHITE)
self.ctx.rectangle(0, 0, width, height)
self.ctx.stroke()
self.ctx.set_source_rgb(*color)
self.ctx.scale(1, -1)
for line in reversed(text):
self.ctx.show_text(line)
self.moveTo(0, 1.4 * -lheight)
self.ctx.set_source_rgb(0.0, 0.0, 0.0)
self.ctx.set_source_rgb(*Color.BLACK)
self.ctx.set_font_size(10)

tx_sizes = {
Expand Down Expand Up @@ -1372,23 +1391,19 @@ def flex2D(self, x, y, width=1):
for i in range(cx):
for j in range(cy):
if (i + j) % 2:
self.ctx.save()
self.moveTo((5 * i) * wx, (5 * j) * wy)
self.polyline(*armx)
self.ctx.restore()
self.ctx.save()
self.moveTo((5 * i + 5) * wx, (5 * j + 5) * wy, -180)
self.polyline(*armx)
self.ctx.restore()
with saved(self.ctx):
self.moveTo((5 * i) * wx, (5 * j) * wy)
self.polyline(*armx)
with saved(self.ctx):
self.moveTo((5 * i + 5) * wx, (5 * j + 5) * wy, -180)
self.polyline(*armx)
else:
self.ctx.save()
self.moveTo((5 * i + 5) * wx, (5 * j) * wy, 90)
self.polyline(*army)
self.ctx.restore()
self.ctx.save()
self.moveTo((5 * i) * wx, (5 * j + 5) * wy, -90)
self.polyline(*army)
self.ctx.restore()
with saved(self.ctx):
self.moveTo((5 * i + 5) * wx, (5 * j) * wy, 90)
self.polyline(*army)
with saved(self.ctx):
self.moveTo((5 * i) * wx, (5 * j + 5) * wy, -90)
self.polyline(*army)
self.ctx.stroke()

##################################################
Expand Down Expand Up @@ -1543,21 +1558,19 @@ def surroundingWall(self, x, y, r, h,
bottom(l / 2.)
tops.append(l / 2.)

self.ctx.save()
# complete wall segment
self.edgeCorner(bottom, right, 90)
right(h)
self.edgeCorner(right, top, 90)
for n, d in enumerate(reversed(tops)):
if n % 2: # flex
self.edge(d)
else:
top(d)
self.edgeCorner(top, left, 90)
left(h)
self.edgeCorner(left, bottom, 90)

self.ctx.restore()
with saved(self.ctx):
self.edgeCorner(bottom, right, 90)
right(h)
self.edgeCorner(right, top, 90)
for n, d in enumerate(reversed(tops)):
if n % 2: # flex
self.edge(d)
else:
top(d)
self.edgeCorner(top, left, 90)
left(h)
self.edgeCorner(left, bottom, 90)

if nr == len(sides) - 1:
break
Expand Down Expand Up @@ -1802,15 +1815,14 @@ def partsMatrix(self, n, width, move, part, *l, **kw):
part(*l, **kw)
# draw matrix
for i in range(rows):
self.ctx.save()
for j in range(width):
if "only" in move:
break
if width*i+j >= n:
break
kw["move"] = "right"
part(*l, **kw)
self.ctx.restore()
with saved(self.ctx):
for j in range(width):
if "only" in move:
break
if width*i+j >= n:
break
kw["move"] = "right"
part(*l, **kw)
kw["move"] = "up only"
part(*l, **kw)

Expand Down
36 changes: 31 additions & 5 deletions boxes/formats.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,27 @@
#!/usr/bin/env python3
# Copyright (C) 2013-2014 Florian Festi
#
# 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, see <http://www.gnu.org/licenses/>.


import subprocess
import tempfile
import os
import cairo
import re
from boxes import svgutil

from boxes.Color import *

class PSFile:
def __init__(self, filename):
Expand All @@ -26,10 +43,18 @@ def adjustDocumentMedia(self):


class Formats:

class ContextManager(cairo.Context):
def __init__(self, target: cairo.Surface):
super().__init__()

pstoedit = "/usr/bin/pstoedit"

_BASE_FORMATS = ['svg', 'svg_Ponoko', 'ps']

formats = {
"svg": None,
"svg_Ponoko": None,
"ps": None,
"dxf": "-flat 0.1 -f dxf:-mm".split(),
"gcode": "-f gcode".split(),
Expand All @@ -40,6 +65,7 @@ class Formats:

http_headers = {
"svg": [('Content-type', 'image/svg+xml; charset=utf-8')],
"svg_Ponoko": [('Content-type', 'image/svg+xml; charset=utf-8')],
"ps": [('Content-type', 'application/postscript')],
"dxf": [('Content-type', 'image/vnd.dxf')],
"plt": [('Content-type', ' application/vnd.hp-hpgl')],
Expand All @@ -55,7 +81,7 @@ def getFormats(self):
if os.path.isfile(self.pstoedit):
return sorted(self.formats.keys())
else:
return ['svg', 'ps']
return self._BASE_FORMATS

def getSurface(self, fmt, filename):

Expand All @@ -74,21 +100,21 @@ def getSurface(self, fmt, filename):
ctx.translate(0, height)
ctx.scale(mm2pt, -mm2pt)

ctx.set_source_rgb(0.0, 0.0, 0.0)
ctx.set_source_rgb(*Color.BLACK)

return surface, ctx

def convert(self, filename, fmt):

if fmt == 'svg':
if fmt in ['svg', 'svg_Ponoko']:
svg = svgutil.SVGFile(filename)
svg.getEnvelope()
svg.rewriteViewPort()
else:
ps = PSFile(filename)
ps.adjustDocumentMedia()

if fmt not in ("svg", "ps"):
if fmt not in self._BASE_FORMATS:
fd, tmpfile = tempfile.mkstemp(dir=os.path.dirname(filename))
cmd = [self.pstoedit] + self.formats[fmt] + [filename, tmpfile]
err = subprocess.call(cmd)
Expand Down