Add minimal doc strings

This commit is contained in:
Florian Festi 2016-03-28 16:55:41 +02:00
parent 31a6af5116
commit 09871be797
2 changed files with 375 additions and 21 deletions

View File

@ -23,9 +23,21 @@ from functools import wraps
from boxes import edges
def dist(dx, dy):
"""
Return distance
:param dx: delta x
:param dy: delat y
"""
return (dx*dx+dy*dy)**0.5
def restore(func):
"""
Wrapper: Restore coordiantes after function
:param func: function to wrap
"""
@wraps(func)
def f(self, *args, **kw):
self.ctx.save()
@ -41,6 +53,7 @@ def restore(func):
#############################################################################
class NutHole:
"""Draw a hex nut"""
sizes = {
"M1.6" : (3.2, 1.3),
"M2" : (4, 1.6),
@ -80,6 +93,12 @@ class NutHole:
self.boxes.corner(-60)
def argparseSections(s):
"""
Parse sections parameter
:param s: string to parse
"""
m = re.match(r"(\d+(\.\d+)?)/(\d+)", s)
if m:
n = int(m.group(3))
@ -95,6 +114,7 @@ def argparseSections(s):
raise argparse.ArgumentTypeError("Don't understand sections string")
class Boxes:
"""Main class -- Generator should sub class this """
def __init__(self):
self.argparser = ArgumentParser(description=self.__doc__)
@ -112,6 +132,15 @@ class Boxes:
help="burn correction in mm")
def open(self, width, height):
"""
Prepare for rendering
Call this function from your .render() method
:param width: width of canvas in mm
:param height: height of canvas in mm
"""
self.spacing = 2*self.burn + 0.5 * self.thickness
self.fingerHoleEdgeWidth = 1.0 # multitudes of self.thickness
@ -121,6 +150,12 @@ class Boxes:
self._buildObjects()
def buildArgParser(self, *l):
"""
Add commonly used commandf line parameters
:param \*l: parameter names
"""
for arg in l:
if arg == "x":
self.argparser.add_argument(
@ -153,9 +188,22 @@ class Boxes:
raise ValueError("No default for argument", arg)
def parseArgs(self, args=None):
"""
Parse command line parameters
:param args: (Default value = None) parameters, None for using sys.argv
"""
self.argparser.parse_args(args=args, namespace=self)
def addPart(self, part, name=None):
"""
Add Edge or other part instance to this one and add it as attribute
:param part: Callable
:param name: (Default value = None) attribute name (__name__ as default)
"""
if name is None:
name = part.__class__.__name__
name = name[0].lower() + name[1:]
@ -165,6 +213,7 @@ class Boxes:
self.edges[part.char] = part
def _buildObjects(self):
"""Add default edges and parts """
self.edges = {}
self.addPart(edges.Edge(self, None))
self.addPart(edges.OutSetEdge(self, None))
@ -185,6 +234,13 @@ class Boxes:
self.addPart(NutHole(self, None))
def _init_surface(self, width, height):
"""
Initialize cairo canvas
:param width: canvas size
:param height: canvas height
"""
#mm2pt = 90 / 25.4 / 1.25
mm2pt = 1
#width *= mm2pt
@ -203,7 +259,14 @@ class Boxes:
def cc(self, callback, number, x=0.0, y=None):
"""call callback"""
"""Call callback from edge of a part
:param callback: callback (callable or list of callables)
:param number: number of the callback
:param x: (Default value = 0.0) x position to be call on
:param y: (Default value = None) y position to be called on (default does burn correction)
"""
if y is None:
y = self.burn
self.ctx.save()
@ -223,6 +286,13 @@ class Boxes:
self.ctx.restore()
def getEntry(self, param, idx):
"""
Get entry from list or items itself
:param param: list or item
:param idx: index in list
"""
if isinstance(param, list):
if len(param)>idx:
return param[idx]
@ -232,6 +302,9 @@ class Boxes:
return param
def close(self):
"""Finish rendering
Call at the end of your .render() method"""
self.ctx.stroke()
self.surface.flush()
self.surface.finish()
@ -256,6 +329,15 @@ class Boxes:
############################################################
def corner(self, degrees, radius=0):
"""
Draw a corner
This is what does the burn corrections
:param degrees: angle
:param radius: (Default value = 0)
"""
rad = degrees*math.pi/180
if degrees > 0:
self.ctx.arc(0, radius+self.burn, radius+self.burn,
@ -270,12 +352,26 @@ class Boxes:
self.continueDirection(rad)
def edge(self, length):
"""
Simple line
:param length: length in mm
"""
self.ctx.move_to(0,0)
self.ctx.line_to(length, 0)
self.ctx.translate(*self.ctx.get_current_point())
def curveTo(self, x1, y1, x2, y2, x3, y3):
"""control point 1, control point 2, end point"""
"""control point 1, control point 2, end point
:param x1:
:param y1:
:param x2:
:param y2:
:param x3:
:param y3:
"""
self.ctx.curve_to(x1, y1, x2, y2, x3, y3)
dx = x3-x2
dy = y3-y2
@ -283,6 +379,12 @@ class Boxes:
self.continueDirection(rad)
def polyline(self, *args):
"""
Draw multiple connected lines
:param \*args: Alternating length in mm and angle
"""
for i, arg in enumerate(args):
if i % 2:
self.corner(arg)
@ -290,6 +392,13 @@ class Boxes:
self.edge(arg)
def bedBoltHole(self, length, bedBoltSettings=None):
"""
Draw an edge with slot for a bed bolt
:param length: length of the edge in mm
:param bedBoltSettings: (Default value = None) Dimmensions of the slot
"""
d, d_nut, h_nut, l, l1 = bedBoltSettings or self.bedBoltSettings
self.edge((length-d)/2.0)
self.corner(90)
@ -319,7 +428,12 @@ class Boxes:
def grip(self, length, depth):
"""corrugated edge useful as an gipping area"""
"""Corrugated edge useful as an gipping area
:param length: length
:param depth: depth of the grooves
"""
grooves = int(length // (depth*2.0)) + 1
depth = length / grooves / 4.0
for groove in range(grooves):
@ -328,6 +442,11 @@ class Boxes:
self.corner(90, depth)
def _latchHole(self, length):
"""
:param length:
"""
self.edge(1.1*self.thickness)
self.corner(-90)
self.edge(length/2.0+0.2*self.thickness)
@ -335,14 +454,22 @@ class Boxes:
self.edge(1.1*self.thickness)
def _latchGrip(self, length):
"""
:param length:
"""
self.corner(90, self.thickness/4.0)
self.grip(length/2.0-self.thickness/2.0-0.2*self.thickness, self.thickness/2.0)
self.corner(90, self.thickness/4.0)
def latch(self, length, positive=True, reverse=False):
"""Fix a flex box door at the box
positive: False: Door side; True: Box side
reverse: True when running away from the latch
"""Latch to fix a flex box door to the box
:param length: length in mm
:param positive: (Default value = True) False: Door side; True: Box side
:param reverse: (Default value = False) True when running away from the latch
"""
if positive:
if reverse:
@ -368,7 +495,14 @@ class Boxes:
self.corner(90)
def handle(self, x, h, hl, r=30):
"""Creates and Edge with a handle"""
"""Creates an Edge with a handle
:param x: width in mm
:param h: height in mm
:param hl: height if th grip hole
:param r: (Default value = 30) radius of the corners
"""
d = (x-hl-2*r)/2.0
if d < 0:
print("Handle too wide")
@ -398,12 +532,26 @@ class Boxes:
### Navigation
def moveTo(self, x, y=0.0, degrees=0):
"""
Move coordinate system to given point
:param x:
:param y: (Default value = 0.0)
:param degrees: (Default value = 0)
"""
self.ctx.move_to(0, 0)
self.ctx.translate(x, y)
self.ctx.rotate(degrees*math.pi/180.0)
self.ctx.move_to(0, 0)
def continueDirection(self, angle=0):
"""
Set coordinate system to current position (end point)
:param angle: (Default value = 0) heading
"""
self.ctx.translate(*self.ctx.get_current_point())
self.ctx.rotate(angle)
@ -413,6 +561,12 @@ class Boxes:
when "only" is included the move is only done when before is True
The function returns whether actual drawing of the part
should be omited.
:param x: width of part
:param y: height of part
:param where: which direction to move
:param before: (Default value = False) called before or after part being drawn
"""
if not where:
return False
@ -443,6 +597,17 @@ class Boxes:
def fingerHolesAt(self, x, y, length, angle=90,
bedBolts=None, bedBoltSettings=None):
"""
Draw holes for a matching finger joint edge
:param x: position
:param y: position
:param length: length of matching edge
:param angle: (Default value = 90)
:param bedBolts: (Default value = None)
:param bedBoltSettings: (Default value = None)
"""
self.ctx.save()
self.moveTo(x, y, angle)
self.fingerHoles(length, bedBolts, bedBoltSettings)
@ -450,11 +615,32 @@ class Boxes:
@restore
def hole(self, x, y, r):
"""
Draw a round hole
:param x: position
:param y: postion
:param r: radius
"""
radius -= self.burn
if radius < 0:
radius = 1E-9
self.moveTo(x+r, y)
self.ctx.arc(-r, 0, r, 0, 2*math.pi)
@restore
def rectangularHole(self, x, y, dx, dy, r=0):
"""
Draw an rectangulat hole
:param x: position
:param y: position
:param dx: width
:param dy: height
:param r: (Default value = 0) radius of the corners
"""
self.moveTo(x+r-dx/2.0, y-dy/2.0, 180)
for d in (dy, dx, dy, dx):
self.corner(-90, r)
@ -462,6 +648,16 @@ class Boxes:
@restore
def text(self, text, x=0, y=0, angle=0, align=""):
"""
Draw text
:param text: text to render
:param x: (Default value = 0)
:param y: (Default value = 0)
:param angle: (Default value = 0)
:param align: (Default value = "") string with combinations of (top|middle|bottom) and (left|center|right) separated by a space
"""
self.moveTo(x, y, angle)
(tx, ty, width, height, dx, dy) = self.ctx.text_extents(text)
align = align.split()
@ -484,6 +680,14 @@ class Boxes:
@restore
def NEMA(self, size, x=0, y=0, angle=0):
"""Draw holes for mounting a NEMA stepper motor
:param size: Nominal size in tenths of inches
:param x: (Default value = 0)
:param y: (Default value = 0)
:param angle: (Default value = 0)
"""
nema = {
# motor,flange, holes, screws
8 : (20.3, 16, 15.4, 3),
@ -511,13 +715,20 @@ class Boxes:
# hexHoles
def hexHolesRectangle(self, x, y, settings=None, skip=None):
"""
Fills a rectangle with holes.
"""Fills a rectangle with holes in a hex pattern.
Settings have:
r : radius of holes
b : space between holes
style : what types of holes (not yet implemented)
skip : function to check if hole should be present
:param x: width
:param y: heigth
:param settings: (Default value = None)
:param skip: (Default value = None) function to check if hole should be present
gets x, y, r, b, posx, posy
"""
if settings is None:
settings = self.hexHolesSettings
@ -549,11 +760,37 @@ class Boxes:
return (dist(posx-cx, posy-cy) > (cx-r))
def hexHolesCircle(self, d, settings=None):
"""
Fill circle with holes in a hex pattern
:param d: diameter of the circle
:param settings: (Default value = None)
"""
d2 = d/2.0
self.hexHolesRectangle(d, d, settings=settings, skip=self.__skipcircle)
def hexHolesPlate(self, x, y, rc, settings=None):
"""
Fill a plate with holes in a hex pattern
:param x: width
:param y: height
:param rc: radius of the corners
:param settings: (Default value = None)
"""
def skip(x, y, r, b, posx, posy):
"""
:param x:
:param y:
:param r:
:param b:
:param posx:
:param posy:
"""
posx = abs(posx-(x/2.0))
posy = abs(posy-(y/2.0))
@ -567,6 +804,14 @@ class Boxes:
self.hexHolesRectangle(x, y, settings, skip=skip)
def hexHolesHex(self, h, settings=None, grow=None):
"""
Fill a hexagon with holes in a hex pattern
:param h: height
:param settings: (Default value = None)
:param grow: (Default value = None)
"""
if settings is None:
settings = self.hexHolesSettings
r, b, style = settings
@ -600,12 +845,22 @@ class Boxes:
holesMargin=None, holesSettings=None,
bedBolts=None, bedBoltSettings=None,
move=None):
"""fits surroundingWall
first edge is split to have a joint in the middle of the side
"""Plate with rounded corner fitting to .surroundingWall()
First edge is split to have a joint in the middle of the side
callback is called at the beginning of the straight edges
0, 1 for the two part of the first edge, 2, 3, 4 for the others
set holesMargin to get hex holes.
:param x: width
:param y: hight
:param r: radius of the corners
:param callback: (Default value = None)
:param holesMargin: (Default value = None) set to get hex holes
:param holesSettings: (Default value = None)
:param bedBolts: (Default value = None)
:param bedBoltSettings: (Default value = None)
:param move: (Default value = None)
"""
overallwidth = x+2*self.fingerJointEdge.spacing()
@ -654,13 +909,23 @@ class Boxes:
bottom='e', top='e',
callback=None,
move=None):
"""
h : inner height, not counting the joints
"""h : inner height, not counting the joints
callback is called a beginn of the flat sides with
0 for right half of first x side;
1 and 3 for y sides;
2 for second x side
4 for second half of the first x side
* 0 for right half of first x side;
* 1 and 3 for y sides;
* 2 for second x side
* 4 for second half of the first x side
:param x: width of matching roundedPlate
:param y: height of matching roundedPlate
:param r: corner radius of matching roundedPlate
:param h: height of the wall
:param bottom: (Default value = 'e') Edge type
:param top: (Default value = 'e') Edge type
:param callback: (Default value = None)
:param move: (Default value = None)
"""
c4 = (r+self.burn)*math.pi*0.5 # circumference of quarter circle
c4 = 0.9 * c4 # stretch flex 10%
@ -721,6 +986,20 @@ class Boxes:
bedBolts=None, bedBoltSettings=None,
callback=None,
move=None):
"""
Rectangular wall for all kind of box like objects
:param x: width
:param y: height
:param edges: (Default value = "eeee") bottom, right, top, left
:param holesMargin: (Default value = None)
:param holesSettings: (Default value = None)
:param bedBolts: (Default value = None)
:param bedBoltSettings: (Default value = None)
:param callback: (Default value = None)
:param move: (Default value = None)
"""
if len(edges) != 4:
raise ValueError("four edges required")
edges = [self.edges.get(e, e) for e in edges]
@ -764,6 +1043,7 @@ class DemoBox(Boxes):
self.buildArgParser("x", "y", "h")
def render(self):
""" """
x, y, h, t = self.x, self.y, self.h, self.thickness
self.open(2*x+10*self.thickness, y+2*h+20*self.thickness)
self.ctx.save()

View File

@ -20,18 +20,39 @@ class BoltPolicy:
"""Abstract class
Distributes (bed) bolts on a number of segments
(fingers of a finger joint)
"""
def drawbolt(self, pos):
"""Add a bolt to this segment?"""
"""Add a bolt to this segment?
:param pos: number of the finger
"""
return False
def numFingers(self, numfingers):
"""returns next smaller, possible number of fingers"""
"""Return next smaller, possible number of fingers
:param numfingers: number of fingers to aim for
"""
return numFingers
def _even(self, numFingers):
"""
Return same or next smaller even number
:param numFingers:
"""
return (numFingers//2) * 2
def _odd(self, numFingers):
"""
Return same or next smaller odd number
:param numFingers:
"""
if numFingers % 2:
return numFingers
else:
@ -41,6 +62,7 @@ class Bolts(BoltPolicy):
"""Distribute a fixed number of bolts evenly"""
def __init__(self, bolts=1):
self.bolts = bolts
def numFingers(self, numFingers):
if self.bolts % 2:
self.fingers = self._even(numFingers)
@ -49,6 +71,12 @@ class Bolts(BoltPolicy):
return self.fingers
def drawBolt(self, pos):
"""
Return if this finger needs a bolt
:param pos: number of this finger
"""
if pos > self.fingers//2:
pos = self.fingers - pos
if pos==0:
@ -65,6 +93,12 @@ class Bolts(BoltPolicy):
#############################################################################
class Settings:
"""Generic Settings class
Used by different other classes to store messurements and details.
Supports absolutevalues and settings that grow with the thinckness
of the material used.
"""
absolute_params = { }
relative_params = { }
@ -79,6 +113,14 @@ class Settings:
self.setValues(thickness, relative, **kw)
def setValues(self, thickness, relative=True, **kw):
"""
Set values
:param thickness: thickness of the material used
:param relative: (Default value = True) Do scale by thinckness
:param **kw: parameters to set
"""
factor = 1.0
if relative:
factor = thickness
@ -100,6 +142,7 @@ class Settings:
class Edge:
"""Straight edge"""
char = 'e'
def __init__(self, boxes, settings):
@ -117,27 +160,35 @@ class Edge:
self.ctx.translate(*self.ctx.get_current_point())
def width(self):
"""Amount of space the beginning of the edge is set below the inner space of the part """
return 0.0
def margin(self):
"""Space needed right of the starting point"""
return self.boxes.spacing
def spacing(self):
"""Space the edge needs outside of the inner space of the part"""
return self.width() + self.margin()
def startAngle(self):
"""Not yet supported"""
return 0.0
def endAngle(self):
"""Not yet supported"""
return 0.0
class OutSetEdge(Edge):
"""Straight edge out set by one thickness"""
char = 'E'
def width(self):
return self.boxes.thickness
class CompoundEdge(Edge):
"""Edge composed of multiple different Edges"""
def __init__(self, boxes, types, lengths):
Edge.__init__(self, boxes, None)
self.types = [self.edges.get(edge, edge) for edge in types]
@ -158,6 +209,7 @@ class CompoundEdge(Edge):
e(l)
class Slot(Edge):
"""Edge with an slot to slid another pice through """
def __init__(self, boxes, depth):
Edge.__init__(self, boxes, None)
self.depth = depth
@ -175,6 +227,7 @@ class Slot(Edge):
self.boxes.edge(self.length)
class SlottedEdge(Edge):
"""Edge with multiple slots"""
def __init__(self, boxes, sections, edge="e", slots=0):
Edge.__init__(self, boxes, None)
@ -198,6 +251,10 @@ class SlottedEdge(Edge):
self.edge(self.sections[-1])
class FingerJointSettings(Settings):
"""Setting for all different finger joint components
Both sides should use the same instance to ensure they match"""
absolute_params = {
"surroundingspaces" : 2,
}
@ -210,6 +267,7 @@ class FingerJointSettings(Settings):
}
class FingerJointEdge(Edge):
"""Finger joint edge """
char = 'f'
positive = True
@ -253,19 +311,24 @@ class FingerJointEdge(Edge):
self.edge(leftover/2.0)
def margin(self):
""" """
return self.boxes.spacing + self.boxes.thickness
class FingerJointEdgeCounterPart(FingerJointEdge):
"""Finger joint edge - other side"""
char = 'F'
positive = False
def width(self):
""" """
return self.boxes.thickness
def margin(self):
""" """
return self.boxes.spacing
class FingerHoleEdge(Edge):
"""Edge with holes for a parallel finger joint"""
char = 'h'
def __call__(self, length, dist=None,
@ -282,9 +345,11 @@ class FingerHoleEdge(Edge):
self.ctx.translate(*self.ctx.get_current_point())
def width(self):
""" """
return (self.fingerHoleEdgeWidth+1) * self.thickness
class FingerHoles:
"""Hole mathcing a finger joint edge"""
def __init__(self, boxes, settings):
self.boxes = boxes
self.ctx = boxes.ctx
@ -313,6 +378,7 @@ class FingerHoles:
self.ctx.translate(*self.ctx.get_current_point())
class CrossingFingerHoleEdge(Edge):
"""Edge with holes for finger joints 90° above"""
def __init__(self, boxes, height, **kw):
Edge.__init__(self, boxes, None, **kw)
self.height = height
@ -323,6 +389,9 @@ class CrossingFingerHoleEdge(Edge):
class DoveTailSettings(Settings):
"""Settings used for dove tail joints
Both sides should use the same instance to ensure they match"""
absolute_params = {
"angle" : 50,
}
@ -333,6 +402,7 @@ class DoveTailSettings(Settings):
}
class DoveTailJoint(Edge):
"""Edge with dove tail joints """
char = 'd'
positive = True
@ -367,9 +437,11 @@ class DoveTailJoint(Edge):
self.ctx.translate(*self.ctx.get_current_point())
def margin(self):
""" """
return self.settings.depth + self.boxes.spacing
class DoveTailJointCounterPart(DoveTailJoint):
"""Edge for other side of dove joints """
char = 'D'
positive = False
@ -381,6 +453,7 @@ class DoveTailJointCounterPart(DoveTailJoint):
return self.boxes.spacing
class FlexSettings(Settings):
"""Settings for one directional flex cuts"""
relative_params = {
"distance" : 0.5,
"connection" : 1.0,
@ -391,6 +464,7 @@ class FlexSettings(Settings):
}
class FlexEdge(Edge):
"""Edge with flex cuts - use straight edge for the opposing side"""
char = 'X'
def __call__(self, x, h, **kw):