Browse Source

add tree

master
wes 12 months ago
parent
commit
a7f9a87cf5
  1. 31
      planter.py
  2. 118
      svg_path.py

31
planter.py

@ -1,16 +1,18 @@
from cadquery import exporters
from cq_warehouse.bearing import SingleRowDeepGrooveBallBearing from cq_warehouse.bearing import SingleRowDeepGrooveBallBearing
from cq_warehouse.extensions import Workplane
from cq_warehouse.fastener import * from cq_warehouse.fastener import *
from cq_warehouse.thread import * from cq_warehouse.thread import *
from cqmore.curve import archimedeanSpiral, circle from cqmore.curve import archimedeanSpiral, circle
from cqmore.polygon import regularPolygon, star from cqmore.polygon import regularPolygon, star
from cqmore.polyhedron import polarZonohedra, Polyhedron, superellipsoid from cqmore.polyhedron import polarZonohedra, Polyhedron, superellipsoid
from cq_warehouse.extensions import Workplane from svg_path import addSvgPath
import functools as fnc from svgpathtools import svg2paths
import itertools as it
import cadquery as cq import cadquery as cq
import cqmore import cqmore
Workplane = cqmore.extend(Workplane) Workplane = cqmore.extend(Workplane)
Workplane.addSvgPath = addSvgPath
planter_height = 210 planter_height = 210
planter_radius = 129 planter_radius = 129
@ -29,8 +31,7 @@ result = Workplane().polyhedron(*outer_poly)
holder_angle_offset = 20 holder_angle_offset = 20
result = ( planter_polygon = (result.workplane(offset=-36)
result.workplane(offset=-36)
.makePolygon( .makePolygon(
regularPolygon( regularPolygon(
nSides=4, nSides=4,
@ -39,11 +40,29 @@ result = (
thetaEnd=360 + holder_angle_offset, thetaEnd=360 + holder_angle_offset,
), ),
forConstruction=True, forConstruction=True,
) ))
result = (
planter_polygon
.vertices() .vertices()
.sphere(8, combine="cut") .sphere(8, combine="cut")
) )
paths, attributes = svg2paths("/home/deck/cad_files/tree_of_gondor_small.svg")
#print(dir(result.addSvgPath(paths[0])))
tree = (Workplane("YZ")
.center(0, 75)
.addSvgPath(paths[0])
.extrude(-4)
.translate((94.8, -100, 0))
)
print(tree)
result = result.cut(tree, clean=False)
cone = cq.Solid.makeCone(planter_radius, 33, planter_height + 95) cone = cq.Solid.makeCone(planter_radius, 33, planter_height + 95)
result = result.workplane().cut(cone) result = result.workplane().cut(cone)

118
svg_path.py

@ -0,0 +1,118 @@
import svgpathtools
import numpy as np
from math import sin, cos, sqrt, pi, acos, fmod, degrees
# See https://gist.github.com/dov/8d9b0304ba85e3229aabccac3c6468ef/d007910a9b68201a6c95852a220f96711ba3faa1
def tpl(cplx):
"""Convert a complex number to a tuple"""
return (cplx.real, cplx.imag)
def angle_between(u, v):
"""Find the angle between the vectors u an v"""
ux, uy = u
vx, vy = v
sign = 1 if ux * vy - uy * vx > 0 else -1
arg = (ux * vx + uy * vy) / (sqrt(ux * ux + uy * uy) * sqrt(vx * vx + vy * vy))
return sign * acos(arg)
# Implementation of https://www.w3.org/TR/SVG/implnote.html#ArcConversionCenterToEndpoint
def arc_endpoint_to_center(start, end, flag_a, flag_s, radius, phi):
"""Convert a endpoint elliptical arc description to a center description"""
rx, ry = radius.real, radius.imag
x1, y1 = start.real, start.imag
x2, y2 = end.real, end.imag
cosphi = cos(phi)
sinphi = sin(phi)
rx2 = rx * rx
ry2 = ry * ry
# Step 1. Compute x1p,y1p
x1p, y1p = (
np.array([[cosphi, sinphi], [-sinphi, cosphi]])
@ np.array([x1 - x2, y1 - y2])
* 0.5
).flatten()
x1p2 = x1p * x1p
y1p2 = y1p * y1p
# Step 2: Compute (cx', cy')
cxyp = sqrt(
(rx2 * ry2 - rx2 * y1p2 - ry2 * x1p2) / (rx2 * y1p2 + ry2 * x1p2)
) * np.array([rx * y1p / ry, -ry * x1p / rx])
if flag_a == flag_s:
cxyp = -cxyp
cxp, cyp = cxyp.flatten()
# Step 3: compute (cx,cy) from (cx',cy')
cx, cy = (
cosphi * cxp - sinphi * cyp + 0.5 * (x1 + x2),
sinphi * cxp + cosphi * cyp + 0.5 * (y1 + y2),
)
# Step 4: compute theta1 and deltatheta
theta1 = angle_between((1, 0), ((x1p - cxp) / rx, (y1p - cyp) / ry))
delta_theta = fmod(
angle_between(
((x1p - cxp) / rx, (y1p - cyp) / ry), ((-x1p - cxp) / rx, (-y1p - cyp) / ry)
),
2 * pi,
)
# Choose the right edge according to the flags
if not flag_s and delta_theta > 0:
delta_theta -= 2 * pi
elif flag_s and delta_theta < 0:
delta_theta += 2 * pi
return (cx, cy), theta1, delta_theta
def addSvgPath(self, path):
"""Add the svg path object to the current workspace"""
res = self
path_start = None
arc_id = 0
for p in path:
if path_start is None:
path_start = p.start
res = res.moveTo(*tpl(p.start))
# Support the four svgpathtools different objects
if isinstance(p, svgpathtools.CubicBezier):
coords = (tpl(p.start), tpl(p.control1), tpl(p.control2), tpl(p.end))
res = res.bezier(coords)
print("cubic bezier")
elif isinstance(p, svgpathtools.QuadraticBezier):
coords = (tpl(p.start), tpl(p.control), tpl(p.end))
res = res.bezier(coords)
print("quadratic bezier")
pass
elif isinstance(p, svgpathtools.Arc):
arc_id += 1
center, theta1, delta_theta = arc_endpoint_to_center(
p.start, p.end, p.large_arc, p.sweep, p.radius, p.rotation
)
res = res.ellipseArc(
x_radius=p.radius.real,
y_radius=p.radius.imag,
rotation_angle=degrees(p.rotation),
angle1=degrees(theta1),
angle2=degrees(theta1 + delta_theta),
)
print("ellipse arc")
elif isinstance(p, svgpathtools.Line):
res = res.lineTo(p.end.real, p.end.imag)
print("line")
if path_start == p.end:
path_start = None
res = res.close()
return res
Loading…
Cancel
Save