Spaces:
Sleeping
Sleeping
| .. _freefuncapi: | |
| ***************** | |
| Free function API | |
| ***************** | |
| .. warning:: The free function API is experimental and may change. | |
| For situations when more freedom in crafting individual objects is required, a free function API is provided. | |
| This API has no hidden state, but may result in more verbose code. One can still use selectors as methods, but all other operations are implemented as free functions. | |
| Placement of objects and creation of patterns can be achieved using the various overloads of the moved method. | |
| Currently this documentation is incomplete, more examples can be found in the tests. | |
| Tutorial | |
| -------- | |
| The purpose of this section is to demonstrate how to construct Shape objects using the free function API. | |
| .. cadquery:: | |
| :height: 600px | |
| from cadquery.func import * | |
| dh = 2 | |
| r = 1 | |
| # construct edges | |
| edge1 = circle(r) | |
| edge2 = circle(1.5*r).moved(z=dh) | |
| edge3 = circle(r).moved(z=1.5*dh) | |
| # loft the side face | |
| side = loft(edge1, edge2, edge3) | |
| # bottom face | |
| bottom = fill(side.edges('<Z')) | |
| # top face with continuous curvature | |
| top = cap(side.edges('>Z'), side, [(0,0,1.6*dh)]) | |
| # assemble into a solid | |
| s = solid(side, bottom, top) | |
| # construct the final result | |
| result = s.moved((-3*r, 0, 0), (3*r, 0, 0)) | |
| The code above builds a non-trivial object by sequentially constructing individual faces, assembling them into a solid and finally generating a pattern. | |
| It begins with defining few edges. | |
| .. code-block:: python | |
| edge1 = circle(r) | |
| edge2 = circle(2*r).moved(z=dh) | |
| edge3 = circle(r).moved(z=1.5*dh) | |
| Those edges are used to create the side faces of the final solid using :meth:`~cadquery.occ_impl.shapes.loft`. | |
| .. code-block:: python | |
| side = loft(edge1, edge2, edge3) | |
| Once the side is there, :meth:`~cadquery.occ_impl.shapes.cap` and :meth:`~cadquery.occ_impl.shapes.fill` are used to define the top and bottom faces. | |
| Note that :meth:`~cadquery.occ_impl.shapes.cap` tries to maintain curvature continuity with respect to the context shape. This is not the case for :meth:`~cadquery.occ_impl.shapes.fill`. | |
| .. code-block:: python | |
| # bottom face | |
| bottom = fill(side.edges('<Z')) | |
| # top face with continuous curvature | |
| top = cap(side.edges('>Z'), side, [(0,0,1.75*dh)]) | |
| Next, all the faces are assembled into a solid. | |
| .. code-block:: python | |
| s = solid(side, bottom, top) | |
| Finally, the solid is duplicated and placed in the desired locations creating the final compound object. Note various usages of :meth:`~cadquery.Shape.moved`. | |
| .. code-block:: python | |
| result = s.moved((-3*r, 0, 0), (3*r, 0, 0)) | |
| In general all the operations are implemented as free functions, with the exception of placement and selection which are strictly related to a specific shape. | |
| Primitives | |
| ---------- | |
| Various 1D, 2D and 3D primitives are supported. | |
| .. cadquery:: | |
| from cadquery.func import * | |
| e = segment((0,0), (0,1)) | |
| c = circle(1) | |
| f = plane(1, 1.5) | |
| b = box(1, 1, 1) | |
| result = compound(e, c.move(2), f.move(4), b.move(6)) | |
| Boolean operations | |
| ------------------ | |
| Boolean operations are supported and implemented as operators and free functions. | |
| In general boolean operations are slow and it is advised to avoid them and not to perform the in a loop. | |
| One can for example union multiple solids at once by first combining them into a compound. | |
| .. cadquery:: | |
| from cadquery.func import * | |
| c1 = cylinder(1, 2) | |
| c2 = cylinder(0.5, 3) | |
| f1 = plane(2, 2).move(z=1) | |
| f2 = plane(1, 1).move(z=1) | |
| e1 = segment((0,-2.5, 1), (0,2.5,1)) | |
| # union | |
| r1 = c2 + c1 | |
| r2 = fuse(f1, f2) | |
| # difference | |
| r3 = c1 - c2 | |
| r4 = cut(f1, f2) | |
| # intersection | |
| r5 = c1*c2 | |
| r6 = intersect(f1, f2) | |
| # splitting | |
| r7 = (c1 / f1).solids('<Z') | |
| r8 = split(f2, e1).faces('<X') | |
| results = (r1, r2, r3, r4, r5, r6, r7, r8) | |
| result = compound([el.moved(2*i) for i,el in enumerate(results)]) | |
| Note that bool operations work on 2D shapes as well. | |
| Shape construction | |
| ------------------ | |
| Constructing complex shapes from simple shapes is possible in various contexts. | |
| .. cadquery:: | |
| from cadquery.func import * | |
| e1 = segment((0,0), (1,0)) | |
| e2 = segment((1,0), (1,1)) | |
| # wire from edges | |
| r1 = wire(e1, e2) | |
| c1 = circle(1) | |
| # face from a planar wire | |
| r2 = face(c1) | |
| # solid from faces | |
| f1 = plane(1,1) | |
| f2 = f1.moved(z=1) | |
| f3 = extrude(f1.wires(), (0,0,1)) | |
| r3 = solid(f1,f2,*f3) | |
| # compound from shapes | |
| s1 = circle(1).moved(ry=90) | |
| s2 = plane(1,1).move(rx=90).move(y=2) | |
| s3 = cone(1,1.5).move(y=4) | |
| r4 = compound(s1, s2, s3) | |
| results = (r1, r2, r3, r4,) | |
| result = compound([el.moved(2*i) for i,el in enumerate(results)]) | |
| Operations | |
| ---------- | |
| Free function API currently supports :meth:`~cadquery.occ_impl.shapes.extrude`, :meth:`~cadquery.occ_impl.shapes.loft`, :meth:`~cadquery.occ_impl.shapes.revolve` and :meth:`~cadquery.occ_impl.shapes.sweep` operations. | |
| .. cadquery:: | |
| from cadquery.func import * | |
| r = rect(1,0.5) | |
| f = face(r, circle(0.2).moved(0.2), rect(0.2, 0.4).moved(-0.2)) | |
| c = circle(0.2) | |
| p = spline([(0,0,0), (0,-1,2)], [(0,0,1), (0,-1,1)]) | |
| # extrude | |
| s1 = extrude(r, (0,0,2)) | |
| s2 = extrude(fill(r), (0,0,1)) | |
| # sweep | |
| s3 = sweep(r, p) | |
| s4 = sweep(f, p) | |
| # loft | |
| s5 = loft(r, c.moved(z=2)) | |
| s6 = loft(r, c.moved(z=1), cap=True)\ | |
| # revolve | |
| s7 = revolve(fill(r), (0.5, 0, 0), (0, 1, 0), 90) | |
| results = (s1, s2, s3, s4, s5, s6, s7) | |
| result = compound([el.moved(2*i) for i,el in enumerate(results)]) | |
| Placement | |
| --------- | |
| Placement and creation of arrays is possible using :meth:`~cadquery.Shape.move` and :meth:`~cadquery.Shape.moved`. | |
| .. cadquery:: | |
| from cadquery.func import * | |
| locs = [(0,-1,0), (0,1,0)] | |
| s = sphere(1).moved(locs) | |
| c = cylinder(1,2).move(rx=15).moved(*locs) | |
| result = compound(s, c.moved(2)) | |
| Text | |
| ---- | |
| The free function API has extensive text creation capabilities including text on | |
| planar curves and text on surfaces. | |
| .. cadquery:: | |
| from cadquery.func import * | |
| from math import pi | |
| # parameters | |
| D = 5 | |
| H = 2*D | |
| S = H/10 | |
| TH = S/10 | |
| TXT = "CadQuery" | |
| # base and spine | |
| c = cylinder(D, H).moved(rz=-135) | |
| cf = c.faces("%CYLINDER") | |
| spine = (c*plane().moved(z=D)).edges().trim(pi/2, pi) | |
| # planar | |
| r1 = text(TXT, 1, spine, planar=True).moved(z=-S) | |
| # normal | |
| r2 = text(TXT, 1, spine) | |
| # projected | |
| r3 = text(TXT, 1, spine, cf).moved(z=S) | |
| # projected and thickened | |
| r4 = offset(r3, TH).moved(z=S) | |
| result = compound(r1, r2, r3, r4) | |
| Adding features manually | |
| ------------------------ | |
| In certain cases it is desirable to add features such as holes or protrusions manually. | |
| E.g., for complicated shapes it might be beneficial performance-wise because it | |
| avoids boolean operations. One can add or remove faces, add holes to existing faces | |
| and last but not least reconstruct existing solids. | |
| .. cadquery:: | |
| from cadquery.func import * | |
| w = 1 | |
| r = 0.9*w/2 | |
| # box | |
| b = box(w, w, w) | |
| # bottom face | |
| b_bot = b.faces('<Z') | |
| # top faces | |
| b_top = b.faces('>Z') | |
| # inner face | |
| inner = extrude(circle(r), (0,0,w)) | |
| # add holes to the bottom and top face | |
| b_bot_hole = b_bot.addHole(inner.edges('<Z')) | |
| b_top_hole = b_top.addHole(inner.edges('>Z')) | |
| # construct the final solid | |
| result = solid( | |
| b.remove(b_top, b_bot).faces(), #side faces | |
| b_bot_hole, # bottom with a hole | |
| inner, # inner cylinder face | |
| b_top_hole, # top with a hole | |
| ) | |
| If the base shape is more complicated, it is possible to use local sewing that | |
| takes into account on indicated elements of the context shape. This, however, | |
| necessitates a two step approach - first a shell needs to be explicitly sewn | |
| and only then the final solid can be constructed. | |
| .. cadquery:: | |
| from cadquery.func import * | |
| w = 1 | |
| h = 0.1 | |
| r = 0.9*w/2 | |
| # box | |
| b = box(w, w, w) | |
| # top face | |
| b_top = b.faces('>Z') | |
| # protrusion | |
| feat_side = extrude(circle(r).moved(b_top.Center()), (0,0,h)) | |
| feat_top = face(feat_side.edges('>Z')) | |
| feat = shell(feat_side, feat_top) # sew into a shell | |
| # add hole to the box | |
| b_top_hole = b_top.addHole(feat.edges('<Z')) | |
| b = b.replace(b_top, b_top_hole) | |
| # local sewing - only two faces are taken into account | |
| sh = shell(b_top_hole, feat.faces('<Z'), ctx=(b, feat)) | |
| # construct the final solid | |
| result = solid(sh) | |
| Mapping onto parametric space | |
| ----------------------------- | |
| To complement functionalities described, it is possible to trim edges and faces explicitly using simple rectangular | |
| trims, polygons, splines or arbitrary wires. | |
| .. cadquery:: | |
| from math import pi | |
| from cadquery.func import cylinder, edgeOn, compound, wire | |
| # parameters | |
| d = 1.5 | |
| h = 3 | |
| du = pi | |
| Nturns = 2 | |
| # construct the base surface | |
| base = cylinder(d, h).faces("%CYLINDER") | |
| # rectangular trim | |
| r1 = base.trim(-pi/2, 0, 0, h/3) | |
| # polyline trim | |
| r2 = base.trim((0,0), (pi,0), (pi/2, h/2)) | |
| # construct a pcurve | |
| pcurve = edgeOn(base, [(pi/2, h/4), (pi, h/4), (pi, h/2), (pi/2, h/2)], periodic=True) | |
| # pcurve trim | |
| r3 = base.trim(wire(pcurve)) | |
| result = compound(r1, r2.moved(x=2), r3.moved(x=4)) | |
| This in principle allows to model arbitrary shapes in the parametric domain, but often it is more desirable | |
| to work with higher level objects like wires. | |
| .. cadquery:: | |
| from cadquery.func import cylinder, loft, wireOn, segment | |
| from math import pi | |
| # parameters | |
| d = 1.5 | |
| h = 3 | |
| du = pi | |
| Nturns = 2 | |
| # construct the base surface | |
| base = cylinder(d, h).faces("%CYLINDER") | |
| # construct a planar 2D patch for u,v trimming | |
| uv_patch = loft( | |
| segment((0, 0), (du, 0)), segment((Nturns * 2 * pi, h), (Nturns * 2 * pi + du, h)) | |
| ) | |
| # map it onto the cylinder | |
| w = wireOn(base, uv_patch) | |
| # check that the pcurves were created | |
| for e in w: | |
| assert e.hasPCurve(base), "No p-curve on base present" | |
| # trim the base surface | |
| result = base.trim(w) | |
| Note that trimming of periodic faces requires manual seam construction and an additional sewing | |
| step to ensure correctness. | |
| .. cadquery:: | |
| from cadquery.func import circle, extrude, spline, edgeOn, segment, wire, shell | |
| from math import pi | |
| # base | |
| r = 5 | |
| h = 5 | |
| f = extrude(circle(r), (0, 0, -h)) | |
| # trimming edges | |
| spl = spline([(0, h), (pi, h / 2.5), (2 * pi, h)], tgts=[(0.1, 0), (0.1, 0)]) | |
| top = edgeOn(f, spl) | |
| bot = edgeOn(f, segment((2 * pi, 0), (0, 0))) | |
| side1 = edgeOn(f, segment((0, 0), (0, h))) | |
| side2 = edgeOn(f, segment((2 * pi, h), (2 * pi, 0))) | |
| # trimming wire | |
| trim_wire = wire(top, side1, bot, side2) | |
| # trim and sew | |
| result = shell(f.trim(trim_wire)) | |
| Finally, it is also possible to map complete faces. | |
| .. cadquery:: | |
| from cadquery.func import sphere, text, faceOn | |
| base = sphere(5).faces() | |
| result = faceOn(base, text("CadQuery", 1)) | |