380 lines
18 KiB
Python
380 lines
18 KiB
Python
# -*- coding: utf-8 -*-
|
|
# 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 ..core.point import Point
|
|
from ..core.part import Part
|
|
from ..core.trace import Trace
|
|
from ..core.circle import Circle
|
|
from ..core.fingerjoint import FingerJoint
|
|
from ..core.neopixel import NeoPixel
|
|
from .platform import Platform
|
|
from ..util import intersection
|
|
from math import pi, sqrt, asin, atan
|
|
|
|
#import inkex
|
|
|
|
class RollerFrame(Platform):
|
|
""" RollerBot Platform. """
|
|
def __init__(self, supwidth, wheelradius, upperradius, lowerradius,
|
|
facesize, barsize, primarygapwidth, secondarygapwidth,
|
|
scale, part_id, thickness=0.0):
|
|
super(RollerFrame, self).__init__(thickness)
|
|
self.supwidth = supwidth
|
|
self.barsize = barsize
|
|
|
|
cutdepth = supwidth / 3.0
|
|
barradius = sqrt(2.0*(barsize/2.0)**2)
|
|
|
|
facewidth = primarygapwidth*2.0 + thickness*3.0
|
|
faceheight = facesize + thickness
|
|
fjoint = FingerJoint(faceheight, thickness*2.0, 'height', thickness=thickness) # Face
|
|
bjoint = FingerJoint(faceheight, thickness*2.0, 'depth', thickness=thickness) # Base
|
|
wjoint = FingerJoint(facewidth, thickness*2.0, 'width', thickness=thickness) # Length
|
|
|
|
if part_id<5:
|
|
# The circular segments for the main body structure.
|
|
|
|
# Outer section.
|
|
x = barsize/2.0
|
|
y = sqrt(lowerradius**2 - x**2)
|
|
a = atan(x/y)
|
|
outer = Circle(upperradius, 72, 5, cutdepth=cutdepth, start=0.0, end=pi, thickness=thickness) + \
|
|
Point(0.0, -upperradius + cutdepth) + \
|
|
Point(-thickness, -upperradius + cutdepth) + \
|
|
Point(-thickness, -upperradius) + \
|
|
Point(-barsize/2.0, -upperradius) + \
|
|
Circle(lowerradius, 72, 4, cutdepth=cutdepth, start=pi+a, end=pi*2-a, thickness=thickness) + \
|
|
Point(-barsize/2.0, upperradius) + \
|
|
Point(-thickness, upperradius) + \
|
|
Point(-thickness, upperradius - cutdepth) + \
|
|
Point(0.0, upperradius - cutdepth)
|
|
outer.close()
|
|
self.traces.append(outer)
|
|
|
|
if part_id in (0,4):
|
|
# Central Motor Position.
|
|
inner = Trace() + \
|
|
Point(-barsize/2.0, -barsize/2.0) + \
|
|
Point(-barsize/2.0, barsize/2.0) + \
|
|
Point(-barsize/6.0, barsize/2.0) + \
|
|
Point(-barsize/6.0, barsize/2.0-thickness/2.0) + \
|
|
Point(barsize/6.0, barsize/2.0-thickness/2.0) + \
|
|
Point(barsize/6.0, barsize/2.0) + \
|
|
Point(barsize/2.0, barsize/2.0) + \
|
|
Point(barsize/2.0, -barsize/2.0) + \
|
|
Point(barsize/6.0, -barsize/2.0) + \
|
|
Point(barsize/6.0, -barsize/2.0+thickness/2.0) + \
|
|
Point(-barsize/6.0, -barsize/2.0+thickness/2.0) + \
|
|
Point(-barsize/6.0, -barsize/2.0)
|
|
inner.close()
|
|
self.traces.append(reversed(inner))
|
|
|
|
# Outer parts are complete, inner parts have cutouts.
|
|
if part_id in (1,2,3):
|
|
# Central Motor Position and Bar.
|
|
inner = Trace() + \
|
|
Point(-barsize/2.0*1.3, -barsize/2.0) + \
|
|
Point(-barsize/2.0*1.3, -barsize/2.0*0.55) + \
|
|
Point(-barsize/2.0, -barsize/2.0*0.55) + \
|
|
Point(-barsize/2.0, barsize/2.0*0.55) + \
|
|
Point(-barsize/2.0*1.3, barsize/2.0*0.55) + \
|
|
Point(-barsize/2.0*1.3, barsize/2.0) + \
|
|
Point(barsize/2.0, barsize/2.0) + \
|
|
Point(barsize/2.0, barsize/10.0) + \
|
|
Point(barsize/2.0*1.2, barsize/20.0) + \
|
|
Point(barsize/2.0*1.2, -barsize/20.0) + \
|
|
Point(barsize/2.0, -barsize/10.0) + \
|
|
Point(barsize/2.0, -barsize/2.0)
|
|
inner.close()
|
|
self.traces.append(reversed(inner))
|
|
|
|
# Upper segment cut-outs.
|
|
x = supwidth/2.0
|
|
y = sqrt((upperradius-supwidth)**2 - x**2)
|
|
a_outer = atan(x/y)
|
|
y = sqrt((barradius+supwidth)**2 - x**2)
|
|
a_inner = atan(x/y)
|
|
|
|
inner = self._segment(upperradius-supwidth, barradius+supwidth,
|
|
0, 1, cutdepth, 0.0, a_outer, a_inner)
|
|
self.traces.append(reversed(inner))
|
|
|
|
fa = (pi/2.0 - self._faceangle(facesize, upperradius)) / 2.0
|
|
(fx, fy) = intersection(upperradius, angle=fa)
|
|
if 0:
|
|
inner = Trace() + \
|
|
Point(fx, -fy) + \
|
|
Point(fy, -fy) + \
|
|
Point(fy, -fx)
|
|
inner.close()
|
|
self.traces.append(reversed(inner))
|
|
|
|
oy = fy-thickness*2.0
|
|
(ox, oa) = intersection(upperradius, y=oy)
|
|
if 0:
|
|
inner = Trace() + \
|
|
Point(ox, -oy) + \
|
|
Point(oy, -oy) + \
|
|
Point(oy, -ox)
|
|
inner.close()
|
|
self.traces.append(reversed(inner))
|
|
|
|
iy = oy
|
|
(ix, ia) = intersection(upperradius-supwidth, y=iy)
|
|
|
|
if part_id==2:
|
|
inner = Circle(upperradius-supwidth, 18, 0, cutdepth=cutdepth,
|
|
start=pi/3+a_outer, end=pi-a_outer,
|
|
thickness=self.thickness) + \
|
|
reversed(Circle(barradius+supwidth, 18, 0, cutdepth=cutdepth,
|
|
start=pi/3+a_inner, end=pi-a_inner,
|
|
thickness=self.thickness))
|
|
inner.close()
|
|
self.traces.append(reversed(inner))
|
|
|
|
# Temporary cut to remove where the face will be installed.
|
|
oy = fy-thickness
|
|
(ox, oa) = intersection(upperradius, y=oy)
|
|
inner = Trace() + \
|
|
Point(ox, -ox) + \
|
|
Point(oy, -ox) + \
|
|
Point(oy, -oy) + \
|
|
Point(ox, -oy)
|
|
inner.close()
|
|
self.traces.append(reversed(inner))
|
|
|
|
else:
|
|
inner = Circle(upperradius-supwidth, 18, 0, cutdepth=cutdepth,
|
|
start=pi/3*1+a_outer, end=pi/2+ia,
|
|
thickness=self.thickness) + \
|
|
reversed(Circle(barradius+supwidth, 18, 0, cutdepth=cutdepth,
|
|
start=pi/3*1+a_inner, end=pi/3*2-a_inner,
|
|
thickness=self.thickness))
|
|
inner.close()
|
|
self.traces.append(reversed(inner))
|
|
|
|
ia = pi/2 - ia
|
|
(ix, iy) = intersection(upperradius-supwidth, angle=ia)
|
|
|
|
inner = Circle(upperradius-supwidth, 18, 0, cutdepth=cutdepth,
|
|
start=pi/2+ia, end=pi/3*3-a_outer,
|
|
thickness=self.thickness) + \
|
|
reversed(Circle(barradius+supwidth, 18, 0, cutdepth=cutdepth,
|
|
start=pi/3*2+a_inner, end=pi/3*3-a_inner,
|
|
thickness=self.thickness)) + \
|
|
Point(ix, -ix)
|
|
inner.close()
|
|
self.traces.append(reversed(inner))
|
|
|
|
# Face and base cutout slots.
|
|
cy = fy-thickness
|
|
for (x1, x2) in zip(fjoint.fingers[1::2],fjoint.fingers[2::2]):
|
|
inner = Trace() + \
|
|
Point(cy+x1, -cy) + \
|
|
Point(cy+x2, -cy) + \
|
|
Point(cy+x2, -cy-thickness) + \
|
|
Point(cy+x1, -cy-thickness)
|
|
inner.close()
|
|
self.traces.append(reversed(inner))
|
|
for (y1, y2) in zip(bjoint.fingers[1::2],bjoint.fingers[2::2]):
|
|
inner = Trace() + \
|
|
Point(cy, -cy-y2) + \
|
|
Point(cy, -cy-y1) + \
|
|
Point(cy+thickness, -cy-y1) + \
|
|
Point(cy+thickness, -cy-y2)
|
|
inner.close()
|
|
self.traces.append(reversed(inner))
|
|
|
|
if 0:
|
|
if part_id==2:
|
|
for seg in range(2):
|
|
segnext = seg*2+1
|
|
inner = self._segment(upperradius-supwidth, barradius+supwidth,
|
|
seg, segnext, cutdepth, 0.0, a_outer, a_inner)
|
|
self.traces.append(reversed(inner))
|
|
else:
|
|
for seg in range(3):
|
|
segnext = seg+1
|
|
inner = self._segment(upperradius-supwidth, barradius+supwidth,
|
|
seg, segnext, cutdepth, 0.0, a_outer, a_inner)
|
|
self.traces.append(reversed(inner))
|
|
|
|
# Lower segment cut-outs.
|
|
x = supwidth/2.0
|
|
y = sqrt((lowerradius-supwidth)**2 - x**2)
|
|
a_outer = atan(x/y)
|
|
y = sqrt((barradius+supwidth)**2 - x**2)
|
|
a_inner = atan(x/y)
|
|
|
|
for seg in range(3):
|
|
segnext = seg+1
|
|
inner = self._segment(lowerradius-supwidth, barradius+supwidth,
|
|
seg, segnext, cutdepth, pi, a_outer, a_inner)
|
|
self.traces.append(reversed(inner))
|
|
|
|
if part_id in (1,2,3):
|
|
r_mid = barradius+supwidth + ((upperradius-supwidth) - (barradius+supwidth))/2.0
|
|
self._slot(barsize/2.0 + supwidth/2.0, cutdepth*1.5, cutdepth)
|
|
self._slot(barsize/2.0 + supwidth/2.0, -cutdepth*1.5, cutdepth)
|
|
self._slot(barsize/2.0 + supwidth/2.0, r_mid+cutdepth*1.5, cutdepth)
|
|
self._slot(barsize/2.0 + supwidth/2.0, r_mid-cutdepth*1.5, cutdepth)
|
|
|
|
elif part_id in (5,6):
|
|
# The board supports.
|
|
x = primarygapwidth
|
|
y = ((upperradius-supwidth) + (barradius+supwidth))/2.0 + supwidth*2.0
|
|
t = Trace() + \
|
|
Point(0.0, 0.0) + \
|
|
Point(0.0, supwidth-cutdepth*2.0) + \
|
|
Point(-cutdepth, supwidth-cutdepth*2.0) + \
|
|
Point(-cutdepth, supwidth-cutdepth*1.0) + \
|
|
Point(0.0, supwidth-cutdepth*1.0) + \
|
|
Point(0.0, supwidth*2.0-cutdepth*2.0) + \
|
|
Point(-cutdepth, supwidth*2.0-cutdepth*2.0) + \
|
|
Point(-cutdepth, supwidth*2.0-cutdepth*1.0) + \
|
|
Point(0.0, supwidth*2.0-cutdepth*1.0) + \
|
|
Point(0.0, y-supwidth*2.0+cutdepth*1.0) + \
|
|
Point(-cutdepth, y-supwidth*2.0+cutdepth*1.0) + \
|
|
Point(-cutdepth, y-supwidth*2.0+cutdepth*2.0) + \
|
|
Point(0.0, y-supwidth*2.0+cutdepth*2.0) + \
|
|
Point(0.0, y-supwidth+cutdepth*1.0) + \
|
|
Point(-cutdepth, y-supwidth+cutdepth*1.0) + \
|
|
Point(-cutdepth, y-supwidth+cutdepth*2.0) + \
|
|
Point(0.0, y-supwidth+cutdepth*2.0) + \
|
|
Point(0.0, y) + \
|
|
Point(x, y) + \
|
|
Point(x, y-supwidth-cutdepth*1.0) + \
|
|
Point(x+cutdepth, y-supwidth-cutdepth*1.0) + \
|
|
Point(x+cutdepth, y-supwidth-cutdepth*2.0) + \
|
|
Point(x, y-supwidth-cutdepth*2.0) + \
|
|
Point(x, supwidth-cutdepth*1.0) + \
|
|
Point(x+cutdepth, supwidth-cutdepth*1.0) + \
|
|
Point(x+cutdepth, supwidth-cutdepth*2.0) + \
|
|
Point(x, supwidth-cutdepth*2.0) + \
|
|
Point(x, 0.0)
|
|
t.close()
|
|
self.traces.append(t)
|
|
|
|
elif part_id==7:
|
|
# The face components.
|
|
t = Trace(x=[thickness], y=[thickness])
|
|
for i, pos in enumerate(fjoint.fingers[1:-1]):
|
|
if i%2==0:
|
|
t += Point(pos, thickness)
|
|
t += Point(pos, 0.0)
|
|
else:
|
|
t += Point(pos, 0.0)
|
|
t += Point(pos, thickness)
|
|
t += Point(faceheight, thickness)
|
|
t += Point(faceheight, facewidth-thickness)
|
|
for i, pos in enumerate(reversed(fjoint.fingers[1:-1])):
|
|
if i%2==0:
|
|
t += Point(pos, facewidth-thickness)
|
|
t += Point(pos, facewidth)
|
|
else:
|
|
t += Point(pos, facewidth)
|
|
t += Point(pos, facewidth-thickness)
|
|
t += Point(thickness, facewidth-thickness)
|
|
for i, pos in enumerate(reversed(wjoint.fingers[1:-1])):
|
|
if i%2==0:
|
|
t += Point(thickness, pos)
|
|
t += Point(0.0, pos)
|
|
else:
|
|
t += Point(0.0, pos)
|
|
t += Point(thickness, pos)
|
|
t.close()
|
|
self.traces.append(t)
|
|
|
|
elif part_id==8:
|
|
t = Trace(x=[thickness], y=[0.0])
|
|
t += Point(facewidth-thickness, 0.0)
|
|
for i, pos in enumerate(bjoint.fingers[1:-1]):
|
|
if i%2==0:
|
|
t += Point(facewidth-thickness, pos)
|
|
t += Point(facewidth, pos)
|
|
else:
|
|
t += Point(facewidth, pos)
|
|
t += Point(facewidth-thickness, pos)
|
|
t += Point(facewidth-thickness, faceheight)
|
|
for i, pos in enumerate(reversed(wjoint.fingers[1:-1])):
|
|
if i%2==0:
|
|
t += Point(pos, faceheight)
|
|
t += Point(pos, faceheight-thickness)
|
|
else:
|
|
t += Point(pos, faceheight-thickness)
|
|
t += Point(pos, faceheight)
|
|
t += Point(thickness, faceheight)
|
|
for i, pos in enumerate(reversed(bjoint.fingers[1:-1])):
|
|
if i%2==0:
|
|
t += Point(thickness, pos)
|
|
t += Point(0.0, pos)
|
|
else:
|
|
t += Point(0.0, pos)
|
|
t += Point(thickness, pos)
|
|
t.close()
|
|
self.traces.append(t)
|
|
|
|
for eye in range(2):
|
|
np = NeoPixel(style='rings', origin=Point(facewidth/4.0*(1+eye*2), faceheight/2.5), scale=scale, rotate=pi/2)
|
|
self.traces.extend(np.traces)
|
|
np = NeoPixel(style='strip', origin=Point(facewidth/2.0, faceheight*0.80), scale=scale)
|
|
self.traces.extend(np.traces)
|
|
|
|
# Camera
|
|
csize = 8.5*scale
|
|
t = Trace() + \
|
|
Point(facewidth/2.0 - csize/2.0, faceheight/2.5 - csize/2.0) + \
|
|
Point(facewidth/2.0 + csize/2.0, faceheight/2.5 - csize/2.0) + \
|
|
Point(facewidth/2.0 + csize/2.0, faceheight/2.5 + csize/2.0) + \
|
|
Point(facewidth/2.0 - csize/2.0, faceheight/2.5 + csize/2.0)
|
|
t.close()
|
|
self.traces.append(t)
|
|
|
|
def _faceangle(self, size, radius):
|
|
# Returns total angle required for a face.
|
|
o = sqrt(2.0*(size**2))*0.5
|
|
h = radius
|
|
return 2.0*asin(o/h)
|
|
|
|
def _segment(self, r_outer, r_inner, seg, segnext, cutdepth, a_offset, a_outer, a_inner):
|
|
# Create an inner segment cutout.
|
|
t = Circle(r_outer, 18, 0, cutdepth=cutdepth,
|
|
start=a_offset+pi/3*seg+a_outer, end=a_offset+pi/3*segnext-a_outer,
|
|
thickness=self.thickness) + \
|
|
reversed(Circle(r_inner, 18, 0, cutdepth=cutdepth,
|
|
start=a_offset+pi/3*seg+a_inner, end=a_offset+pi/3*segnext-a_inner,
|
|
thickness=self.thickness))
|
|
if a_offset == 0.0 and seg == 0:
|
|
r_mid = r_inner + (r_outer - r_inner)/2.0
|
|
t += Trace() + \
|
|
Point(self.supwidth / 2.0, r_mid - self.supwidth) + \
|
|
Point(self.barsize/2.0 + self.supwidth, r_mid - self.supwidth) + \
|
|
Point(self.barsize/2.0 + self.supwidth, r_mid + self.supwidth) + \
|
|
Point(self.supwidth / 2.0, r_mid + self.supwidth)
|
|
t.close()
|
|
return t
|
|
|
|
def _slot(self, x, y, cutdepth):
|
|
slot = Trace() + \
|
|
Point(x-self.thickness/2.0, y+cutdepth/2.0) + \
|
|
Point(x+self.thickness/2.0, y+cutdepth/2.0) + \
|
|
Point(x+self.thickness/2.0, y-cutdepth/2.0) + \
|
|
Point(x-self.thickness/2.0, y-cutdepth/2.0)
|
|
slot.close()
|
|
self.traces.append(reversed(slot))
|