That work that I want to bring to your attention, there is an attempt to once again write a scripting 3D modeling system. Write as I would like to see her.
The ZenCad recipe is pretty simple. The concept of scripting 3d modeling OpenScad, the geometric core of OpenCascade, python3 as a glue, the lazy computing library evalcache for aggressive computing caching. Add more spices from a couple of tools, season the gui with PyQt sauce, and serve to the table, mixing it, but not shaking.
CAD, or in our CAD system, is a computer-aided design system. Unlike interactive 3D editors, script CAD systems interpret the word “automated” in the sense that its creators usually understand. Ie not as a set of computing tools, but as a crystal clear algorithm that requires human intervention only at the time of its creation.
Working in the script CAD, we do not draw a model directly on the screen, but we build a program according to which the model is generated. For people familiar only with interactive CAD, this approach can be described as absolute parametric modeling. Strictly speaking, scripts are often used to complement an interactive environment, to write additional tools, but working in paradigmically pure script CAD requires a different workflow organization, a way of thinking and is designed for a somewhat different circle of tasks.
The most famous and cleanest scripting CAD is currently OpenScad.
There is a certain circle of people who prefer a comfortable and unpretentious OpenScad to a convenient Compass, SolidWorks, FreeCad. It is not easy to answer the question of the secret of its success, but you can definitely say that it is easy, quite flexible to use, has a minimum of settings. Parts of the models written on it are easy to reuse.
However, openscad has several offensive flaws:
Unfortunately, with all the power of the scripting approach, it is quite problematic to go beyond utilitarian combinational writing of simple models with OpenScad.
The first thing I want to correct here is to take a general-purpose language as a military instrument. The use of a general purpose language makes it possible to use the fullness of its syntactic capabilities and a set of previously written libraries for solving 3D modeling problems.
ZenCad interface:
OpenScad interface:
Using python allows you to simplify the openscad code by making the model code more transparent in comparison with OpenScad.
#!/usr/bin/env python #coding: utf-8 from zencad import * lazy.diag = True c1 = 100 c2 = 130 c3 = c2/2 + 20 base = box(c1,c1,c1,center=True) f1 = ngon(r = 35, n = 3) f2 = ngon(r = 35, n = 5) f3 = circle(35) s1 = linear_extrude(f1, c2, center=True) s2 = linear_extrude(f2, c2, center=True).rotateY(deg(90)) s3 = linear_extrude(f3, c2, center=True).rotateX(deg(90)) # 3 . m1 = base - s1 - s2 - s3 m2 = base ^ s1 ^ s2 ^ s3 m3 = s1 + s2 + s3 ystep = 240 xstep = 240 fontpath = os.path.join(zencad.moduledir, "examples/fonts/testfont.ttf") # . . t1 = textshape("difference", fontpath, 40) t1c = t1.center() t1=t1.translate(-t1c.x, -t1c.y, 0).rotateZ(deg(45)) t2 = textshape("intersect", fontpath, 40) t2c = t2.center() t2=t2.translate(-t2c.x, -t2c.y, 0).rotateZ(deg(45)) t3 = textshape("union", fontpath, 40) t3c = t3.center() t3=t3.translate(-t3c.x, -t3c.y, 0).rotateZ(deg(45)) # , . disp(base.forw(ystep)) disp(s1) disp(s2.left(xstep)) disp(s3.right(xstep)) disp(m1.back(ystep)) disp(m2.left(xstep).back(ystep)) disp(m3.right(xstep).back(ystep)) disp(t1.back(ystep).up(c3), Color(1,1,0)) disp(t2.left(xstep).back(ystep).up(c3), Color(1,1,0)) disp(t3.right(xstep).back(ystep).up(c3), Color(1,1,0)) disp(s1.left(xstep).back(ystep), Color(0.5,0,0,0.95)) disp(s2.left(xstep).back(ystep), Color(0.5,0,0,0.95)) disp(s3.left(xstep).back(ystep), Color(0.5,0,0,0.95)) disp(s1.back(ystep), Color(0.5,0,0,0.95)) disp(s2.back(ystep), Color(0.5,0,0,0.95)) disp(s3.back(ystep), Color(0.5,0,0,0.95)) show()
It is very convenient, for example, to filter the point cloud using the generator syntax.
#!/usr/bin/env python3 from zencad import * # . ng = ngon(r = 10, n = 6) # . vertices = ng.vertices() filtered_vertices = [v for v in vertices if vx < 0] # . m = ng.fillet(4, filtered_vertices) disp(m) show()
Thanks to python, unofficially occupying the title of the king of glue in a modern software ecosystem, zencad is easily integrated with other libraries and software complexes. We can use sympy in a single script to generate an analytic surface, numpy to process a cloud of points generated on this surface and, of course, zencad for building, visualizing and post-processing.
from zencad import * import numpy xcoords = numpy.linspace(-10,10,50) ycoords = numpy.linspace(-10,15,50) lines = [ interpolate([point(x, y, 0.01*(x**2 + y**3)) for x in xcoords]) for y in ycoords ] wires = [] for l in lines: trans = translate(0,0,-30) sf = l.endpoints() w=sew([l, segment(sf[0], trans(sf[0])), trans(l), segment(sf[1], trans(sf[1]))]) wires.append(w) for l in lines: disp(l.left(30)) disp(loft(wires) - halfspace().down(10)) show()
The mathematics of polygonal meshes is much simpler than the mathematics of the boundary representation, but the boundary representation is much more practical. In particular, polygonal meshes have a combinatorial explosion problem, which manifests itself especially when it is time to render a model. In OpenScad, it is often necessary to develop a product with a resolution much smaller than the resolution of the real model, which violates the purity of the paradigm.
Thus, the second point of implementation is the use of a full-fledged geometric kernel using a boundary representation instead of a mesh model. Built around the hacker geometric kernel OpenCascade, ZenCad, of course, does not set out to provide the fullness of its capabilities in the python environment. Attempting to fully convey OpenCascade, would lead to writing the second pythonOCC. ZenCad takes tops, trying to maintain a balance between functionality and ergonomics.
#!/usr/bin/env python3 #coding: utf-8 from zencad import * import zencad.surface as surface import zencad.curve2 as curve2 lazy.diag=True height = 70 width = 50 thickness = 30 # BASE pnt1 = point(-width/2,0,0); pnt2 = point(-width/2,-thickness/4,0); pnt3 = point(0,-thickness/2,0); pnt4 = point(width/2,-thickness/4,0); pnt5 = point(width/2,0,0); edge1 = segment(pnt1, pnt2) edge2 = circle_arc(pnt2, pnt3, pnt4) edge3 = segment(pnt4, pnt5) wire = sew([edge1, edge2, edge3]) profile = sew([wire, wire.mirrorX()]) body = profile.fill().extrude(height) body = fillet(body, thickness/12) hl(body.forw(140)) # NECK neck_radius = thickness/4.; neck_height = height/10; neck = cylinder(r=neck_radius, h=neck_height).up(height) body = body + neck hl(body.forw(100)) # THICK body = thicksolid(body, -thickness / 50, [point(0,0,height+height/10)]) hl(body.forw(60)) # THREAD ( 2 .) cylsurf1 = surface.cylinder(neck_radius * 0.99) cylsurf2 = surface.cylinder(neck_radius * 1.05) major = 2 * math.pi; minor = neck_height / 10; angle = math.atan2(neck_height / 4, 2 * math.pi) ellipse1 = curve2.ellipse(major, minor).rotate(angle) arc1 = cylsurf1.map(curve2.trimmed_curve2(ellipse1, 0, math.pi)) segment1 = cylsurf1.map(curve2.segment(ellipse1.value(0), ellipse1.value(math.pi))) ellipse2 = curve2.ellipse(major, minor/4).rotate(angle) arc2 = cylsurf2.map(curve2.trimmed_curve2(ellipse2, 0, math.pi)) segment2 = cylsurf2.map(curve2.segment(ellipse2.value(0), ellipse2.value(math.pi))) m1 = sew([arc1, segment1]) m2 = sew([arc2, segment2]) thread = loft([m1, m2]).up(height + neck_height / 2) hl(m1.up(height + neck_height / 2).right(80)) hl(m2.up(height + neck_height / 2).right(60)) hl(thread.right(40)) # FINAL m = thread + body display(m) show()
Zencad syntax solutions, like his older brother and the OpenScad teacher, minimize the number of entities in the library. Like OpenScad, ZenCad is fundamentally unable to create a primitive at the point (x, y, z), despite the fact that OpenCascade allows it. ZenCad first creates a primitive at the origin of coordinates, and then sets the position it needs, applying transformations. Conversions in ZenCad exist as separate objects and as methods of bodies.
# . cube(40, center=True).rotateX(deg(45)).rotateZ(deg(45)).right(20) # . (right(20) * rotateZ(deg(45)) * rotateX(deg(45)))(cube(40, center=True)) # . trans = right(20) * rotateZ(deg(45)) * rotateX(deg(45)) cube(40, center=True).transform(trans)
A set of transformations is standard and includes translation, rotation, reflection, and zoom.
In order to minimize the computation time, the math in ZenCad is lazified, and all the calculations are aggressively cached. The management of the lenification algorithms takes over the [del] blockchain [/ del] library of evalcache, which I talked about on the pages of Habrahabr some time ago: Disk caching of trees of lazy calculations . Results of calculations zencad saves in the general cache, the state of which can be monitored through the interface of the visualizer. The sha512 hash algorithm used with heightened redundancy excludes the possibility of hash keys collisions of lazy objects (The hash space is 10 ^ 74 times the number of atoms in the universe).
When creating this model, it generates four megabytes of geometry and during the first pass it can be calculated for quite a long time:
Working with threaded surfaces is computationally demanding:
OpenScad does not have chamfering or rounding operations. OpenCascade provides them. These are very important operations, and it would be a shame not to use them for ZenCad. There are other operations that require the indication of a topological object, for example, the operation of taking a thin-walled model in the example with the bottle OpenCascade. In the graphic CAD system, we indicate the topological object (edge, face, vertex) with the mouse. When writing a script, we do not have such an opportunity. Native OpenCascade solves the problem of reflection and uses it to work with graphic CAD. Although ZenCad supports reflection on the model, using it as the main tool has some significant drawbacks. First, the level of knowledge required to use these tools increases dramatically, for you must at least understand the internal topological representation. Secondly, as soon as if appears in the script, the harmony of lazy algorithms immediately breaks down, and the model code becomes quite complicated. In the process of quite a long meditation and experimentation, I settled on the near-point method. In short, when performing topologically dependent operations, the program walks around an object and finds a topological object closest to a given point from among those included in the body. This item is considered selected. Such a solution is more computationally expensive, but, thanks to caching, it performs well. This approach is used for all operations that depend on the elements of the topology.
As mentioned above, the possibility of reflection of the model, according to common sense, is also preserved, as already shown in the example above (Example: Filtering an array of points).
Dimensions of the model can be difficult to read from the screen due to the non-obvious scale. In part, this problem can be solved by markers. Having an intuitive interface (there is nowhere more intuitive), the markers signal coordinates and show the distance, which simplifies the analysis of the correctness of the geometry and the choice of points for chamfer / rounding operations.
Like the older brother (OpenScad), ZenCad is able to update the generated model when modifying the source file. In combination with the caching system, this allows you to quite comfortably modify the script, having the changing state of the product in almost real time.
At this point, the advantages of zencad do not end.
ZenCad (thanks to the bright opencascade core) can redraw the scene in real time, which allows you to animate the 3D model. The animation is implemented by the ordinary python function and allows you to treat yourself quite freely. Given that we are in the python environment, zencad is able to visualize data from external sources as model movements (for example, using multithreading + tcpsocket). Thus, zencad can be used, for example, for semi-natural testing of robotic products. Oh hello gazebo !!! Hello ROS !!! Nice to see you too in the auditorium. The library of kinematics, which would significantly simplify the construction of the kinematic chains of robotic manipulators, by the way, in development.
The animation is currently still in a semi-experimental version (especially in terms of camera control), so I will not dwell on it in detail.
Currently export and import in brep format are supported, which allows integrating with freecad and export in stl format, which allows generating models for 3D printing. Screenshot generation is also supported. Including automatic. In particular, the screenshots in the online manual are generated by ZenCad automatically.
At the moment, ZenCad is still very far from being completed, and yet it is fully operational in the part in which it is ready.
The library is available in pipy for Debian compatible axes with versions of the language python3.5, python3.6, python3.7
(You may need to install qt5-default, due to some problems with plugins in PyQt5)
python3 -m pip install zencad apt install qt5-default
Starting gui from the command line:
python3 -m zencad
Run the gui from the python script:
#!/usr/bin/env python3 import zencad m = zencad.cube(20) zencad.disp(m) zencad.show()
Unfortunately, the progress of the system is not as fast as we would like. Most of the api of two-dimensional geometry and api of working with surfaces, support for exporting and importing standard formats have not yet been implemented, error handling is not always transparent, automatic testing has not been worked out, and libraries for constructing threaded connections and gear products have not been implemented yet. As an external editor, which is completely abnormal, suddenly hard-coded !!! Sublime Text ... I also really want to modify the system so that it can be run under Windows (this requires quite extensive expert intelligence work).
However, now zencad allows you to design fairly complex 3D models, create models for 3D printing, and even visualize the kinematics of mechanisms.
github: https://github.com/mirmik/zencad , https://github.com/mirmik/servoce
pypi: https://pypi.org/project/zencad/ , https://pypi.org/project/pyservoce/
Thanks for attention.
Source: https://habr.com/ru/post/443140/
All Articles