Package: cqkit
CQ-KitThis repository contains utility classes and functions to extend the features and capabilities of theCadQuerypython library.  CadQuery is a python module designed to build parametric 3D CAD models. Since CadQuery is based on python, you can develop a very capable scripted software stack to build 3D models and produce all of the various asset files used for design, prototyping, documentation and manufacturing.  An example use case is howFx Bricksmakes hobby products and isdescribed here.CQ-Kitis designed to be easily included as a companion module to CadQuery and extends its functionality in these key areas:File I/O for import and export to various CAD and graphic file formats (including STEP, IGES, STL, LDraw)AdditionalSelectorclasses to extend CadQuery's powerful entity selection, filtering, and logicAdditional solid construction utilities for building more sophisticated shapesShape analysis and debug printingInstallationAssumingCadQueryis installed, you can installCQ-Kitusing aPyPI packageas follows:$pipinstallcqkitTheCQ-Kitpackage can be installed directly from the source code:$gitclonehttps://github.com/michaelgale/cq-kit.git$cdcq-kit$pythonsetup.pyinstallIf you want to create a fresh anaconda environment withCadQueryandCQ-Kit:$cdcq-kit$condaenvcreate-fenvironment.yml--name$MY_NAME$condaactivate$MY_NAME$condainstall-cconda-forge-cdefaults-ccadquerypython=$VERSIONcadquery=master$pythonsetup.pyinstallSubstitute your desired python$VERSIONwith 3.8, 3.9, or 3.10 and optionally replace$MY_NAMEwith a different desired environment name than the default ofcadqueryspecified inenvironment.yml.Basic UsageAfter installation, the package can imported:$python>>>importcqkit>>>cqkit.__version__An example of the package can be seen belowimportcadqueryascqfromcqkitimport*# make a simple boxr=cq.Workplane("XY").rect(3,5).extrude(2)# export the box with StepFileExporterexport_step_file(r,"mybox.step",title="My Awesome Box",author="Michael Gale")File I/OCQ-Kit adds some convenient file import and export functions to a few supported formats.export_iges_file(shape, filename, author=None, organization=None)- Export CQ shape to IGES file. The IGES file can optionally include an author and organization name written to the IGES file meta data.import_iges_file(filename)- Imports the content of a IGES file and returns a new CQ Workplane object.export_stl_file(shape, filename, tolerance=1e-4)- Export CQ shape to STL mesh file format. The shape is automatically meshed by the OCCT kernel and the resolution/tolerance of the mesh can be optionally specified.import_step_file(filename)- Imports the content of a STEP file and returns a new CQ Workplane object.export_step_file(shape, filename, title=None, author=None, organization=None)- Convenient function to use the CQ-Kit enhancedStepFileExporterclass.  The CQ-Kit STEP file exporter offers the following features:Better Floating Point Representation- The number of significant figures for floating point coordinate data in the STEP file can be specified.  Reducing the tolerance
can significantly reduce the file size by stripping away redundant and
often inconsequential residue and round-off quantities from points.  For
example, it is not uncommon to find values such as "1E-19" or "3.99999999999"
in a STEP file which can re-written as "0." and "4.0" respectively.  The
round off behaviour is performed by the sophisticated and robust
Decimal python built in type.Enable/Disable P-Curve Entities-
The default behaviour of the OCCT STEP file writer is to add redundant
P-Curve entities to the STEP file.  This can often double the size of the
resulting STEP file.  Turning off P-Curves can save file size and almost
never impacts the quality of the STEP file.Precision Mode- The precision mode parameter corresponds to the OCCT STEP file precision
for writing geometric data.  The default value of 1 for maximum precision
is used, but can be changed if desired.Enhanced Meta Data- Adding rich meta data to the STEP file allows for better identification
of the geometric entity when imported into other applications.  It also
allows information about the author, organization, copyright, etc. to be
added to the header for better configuration management.Discrete GeometryCQ-Kit includes functions to discretize either edges or solids:discretize_edge(edge, resolution)- samples an edge with the specified resolution into discrete line segments approximating the edge.  This function returns a list of 3D points corresponding to the approximate line segment endpoints.  Therefore,resolution + 1points are returned representingresolutionnumber of line segments.discretize_all_edges(edges, curve_res, circle_res, as_pts=False)- Processes all edges into discrete/sampled line segments approximating each of the provided edges. Unlikediscretize_edge, straight line segments resolve exactly as one segment, curved/splined edges resolve intocurve_resnumber of segments, and circles resolve intocircle_resnumber of segments.  A list ofEdgeobjects is returned by default; however, ifas_pts=True, then a list of (start, end) point tuples is returned instead.triangle_mesh_solid(solid, lin_tol, ang_tol)- computes a triangular mesh approximation for a solid. The quality/resolution of the mesh can be controlled with both the linear and angular deviation tolerance parameters.  Smaller values yield a better mesh approximation at the expense of larger mesh size.  The computed mesh is returned as a tuple of lists:triangles- a list of each triangles' 3x vertices represented as indexes into the vertices listvertices- a list of the mesh's 3D verticesPretty Printers for ObjectsCQ-Kit offers useful functions which return a string representing a geometric object. The string representation is automatically determined by the type of object.  Objects which are containers for multiple other objects are automatically expanded, e.g. aWirewill expand itsEdgesand those edges will expand into coordinate tuples.obj_str(obj, show_type=False)- returns a pretty string of the passed objectpprint_obj(obj, show_type=False)- pretty prints the object string to the console.Examplespprint_obj((1,2))# ( 1, 2)pprint_obj((3,4,5))# ( 3, 4, 5)pprint_obj(Vector(-2,-4,0))# (-2,-4, 0)pprint_obj(Vertex.makeVertex(-1,0,2))# (-1, 0, 2)pprint_obj(gp_Vec(8,9,10))# ( 8, 9, 10)r=cq.Workplane("XY").rect(1,2)pprint_obj(r.edges().vals())# 4x Edges# 1/4 Line: ( -0.5, -1,  0) -> (  0.5, -1,  0) length:  1# 2/4 Line: (  0.5, -1,  0) -> (  0.5,  1,  0) length:  2# 3/4 Line: (  0.5,  1,  0) -> ( -0.5,  1,  0) length:  1# 4/4 Line: ( -0.5,  1,  0) -> ( -0.5, -1,  0) length:  2r=cq.Workplane("XY").rect(1,2).extrude(5)pprint_obj(r)# Compound (1x Solid), Solid (6x Faces)#   1/6 Face (1x Wire), Wire (4x Edges) length:  12#       1/4 Line: ( -0.5, -1,  0) -> ( -0.5, -1,  5) length:  5#       2/4 Line: (  0.5, -1,  0) -> (  0.5, -1,  5) length:  5#       3/4 Line: ( -0.5, -1,  0) -> (  0.5, -1,  0) length:  1#       4/4 Line: ( -0.5, -1,  5) -> (  0.5, -1,  5) length:  1#   2/6 Face (1x Wire), Wire (4x Edges) length:  14#       1/4 Line: (  0.5, -1,  0) -> (  0.5, -1,  5) length:  5#       2/4 Line: (  0.5,  1,  0) -> (  0.5,  1,  5) length:  5#       3/4 Line: (  0.5, -1,  0) -> (  0.5,  1,  0) length:  2#       4/4 Line: (  0.5, -1,  5) -> (  0.5,  1,  5) length:  2#   3/6 Face (1x Wire), Wire (4x Edges) length:  12#       1/4 Line: (  0.5,  1,  0) -> (  0.5,  1,  5) length:  5#       2/4 Line: ( -0.5,  1,  0) -> ( -0.5,  1,  5) length:  5#       3/4 Line: (  0.5,  1,  0) -> ( -0.5,  1,  0) length:  1#       4/4 Line: (  0.5,  1,  5) -> ( -0.5,  1,  5) length:  1#   4/6 Face (1x Wire), Wire (4x Edges) length:  14#       1/4 Line: ( -0.5,  1,  0) -> ( -0.5,  1,  5) length:  5#       2/4 Line: ( -0.5, -1,  0) -> ( -0.5, -1,  5) length:  5#       3/4 Line: ( -0.5,  1,  0) -> ( -0.5, -1,  0) length:  2#       4/4 Line: ( -0.5,  1,  5) -> ( -0.5, -1,  5) length:  2#   5/6 Face (1x Wire), Wire (4x Edges) length:  6#       1/4 Line: ( -0.5, -1,  0) -> (  0.5, -1,  0) length:  1#       2/4 Line: (  0.5, -1,  0) -> (  0.5,  1,  0) length:  2#       3/4 Line: (  0.5,  1,  0) -> ( -0.5,  1,  0) length:  1#       4/4 Line: ( -0.5,  1,  0) -> ( -0.5, -1,  0) length:  2#   6/6 Face (1x Wire), Wire (4x Edges) length:  6#       1/4 Line: ( -0.5, -1,  5) -> (  0.5, -1,  5) length:  1#       2/4 Line: (  0.5, -1,  5) -> (  0.5,  1,  5) length:  2#       3/4 Line: (  0.5,  1,  5) -> ( -0.5,  1,  5) length:  1#       4/4 Line: ( -0.5,  1,  5) -> ( -0.5, -1,  5) length:  2Note that you can pass in eitherobj.edges().val(),obj.edges().vals(),obj.edges()etc. and the correct string representation will automatically be inferred.  For more complex or compound objects,pprint_objwill recursively unwrap the hierarchy of shapes as well as computing length, radius, and coordinate data where applicable. Additionally, coordinate values are represented with colour highlighting.  You will need install thecrayonspython module in order to see colour highlighting, otherwise it will use your terminal default style.crayonsis optional and CQ-Kit will detect its availability.XSectionClassTheXSectionobject is a convenience container for points which represent a closed path cross-section.  The points can be supplied either as-is or as the mirror-half of all the points.  If provided as a mirrored/symmetric half, then only one half of the points need be specified and the other opposite (mirrored) points will automatically be generated.This container object is useful for storing cross-sectional profiles which are used for extruded/lofted solid objects.  It is also useful for obtaining variants of the cross-section such as:flipped : mirrored in the opposite axis, e.g. upside down version of a left-right symmetric profile)scaled : rescaled by some scalar for bigger/smaller variants. Scale can be a single scalar for uniform scaling in both axes or a tuple of scalars representing different scale factors for each axis.translated : a translated variant offset by a fixed coordinate pairThe cross-section is initialized with the workplane ("XY", "XZ", "YZ", etc.), its 2D points (all points for unsymmetric, or half the points for symmetric), and if symmetric, then a specification of the mirror axis, e.g. for points in the XY plane, mirror_axis=X means that either the upper or lower half of the points are specified and mirror_axis=Y means that either the left or right half of the points are specified.Points are usually supplied as 2D tuples; however, special points which result in curved lines can be specified with a simple dictionary:-{"radiusArc":((2,3),1)}-{"tangentArc":(2,3)}A list of points can involve a mix of types such as:[(0,0),(3,0),(2.5,0.5),(2.5,4),{"radiusArc":((2,4.5),-0.5)},(0,4.5)]get_points(self, flipped=False, scaled=None, translated=None, only_tuples=False)- returns a list of points inXSectionwith optional scaling, mirroring, or translation.render(self, flipped=False, scaled=None, translated=None)- returns a CQ object representing the closed wire path of the cross-section.get_bounding_outline(self, flipped=False, scaled=None, translated=None)- returns a CQ object the rectangular bounding box of the cross-section.Examples# half a triangle on XY planexc=XSection([(0,0),(1,0),(0,3)],"XY",symmetric=True,mirror_axis="Y")# get the outline objectr=xc.render()# get an upside down outline objectr=xc.render(flipped=True)# get an extruded version 2x taller:r=xc.render(scaled=(1,2)).extrude(10)RibbonClassTheRibbonclass generates an arbitrary closed wire path of constant width.  The path of ribbon/wire is described by a list of "turtle graphics" style
plotting commands.  From the starting position, one side of of the ribbon is drawn by parsing the commmands from start to finish. The opposite side of the ribbon is then drawn by parsing the commands in reverse order.The commands describing the path are contained in a list of 2 element tuples.  The first item of each tuple is a command, and the second item is a dictionary."start"- a mandatory first (and only instance of) command. It specifies the start point, trajectory direction, and width of the ribbon path as a dictionary.  Its keys are:"position"- starting coordinate of ribbon path"direction"- initial trajectory of ribbon path in degrees"width"- ribbon width"line"- specifies a simple straight line segment with one dictionary key called"length""arc"- specifies a fixed radius curve segment scribing a sector angle.  Its keys are:"radius"- radius of arc segment"angle"- sector angle scribed by the arc relative to the current trajectory of the ribbon path in degrees.An example command list is as follows:path=[("start",{"position":(10.0,0.0),"direction":30.0,"width":0.5}),("line",{"length":2.0}),("arc",{"radius":2.0,"angle":145.0}),("line",{"length":2}),("arc",{"radius":0.5,"angle":-170}),("line",{"length":3}),]ARibbonobject is initialized with CadQuery workplane specification representing the 2D plane which the ribbon is constructed and a command list.  Therendermethod is called to construct the ribbon object and it is returned as a closed wire path CadQuery workplane object.  This object can then be chained as any other CQ workplane object, e.g. usingextrude()to transform the ribbon object into a 3D solid.Selector ClassesCQ-Kit extends CadQuery's powerfulSelectorbase class with some additional utility classes.  They are described below and are grouped by Selectors for edges and faces.  Almost all of these custom selector classes can be passed atolerancekeyword argument to control the numerical tolerance of filtering operations (usually based on length).The CQ-Kit Selector classes are categorized as follows:Geometric Property SelectorsOrientation SelectorsAssociation SelectorsLocation Selectors1. Geometric Property SelectorsGrouped as follows:HasCoordinateSelector(Selector)- Filters entities with one or more of its vertices having a desired coordinate value.HasXCoordinateSelector()HasYCoordinateSelector()HasZCoordinateSelector()LengthSelector(Selector)- Filters entities by their length. One or more values of length can be specified as the filter criteria, including string constraints such as ">2.5"EdgeLengthSelector()WireLengthSelector()RadiusSelector()DiameterSelector()AreaSelector(Selector)ObjectCountSelector(Selector)- Filters entities by the quantity of one its geometric attributes.VertexCountSelector()EdgeCountSelector()WireCountSelector()FaceCountSelector()2. Orientation SelectorsVerticalSelector(Selector)- Filters entities which are more or less "vertical" or "standing up" with respect to the XY-plane.  Optional length criteria can be specified to filter entities even more.VerticalEdgeSelector()VerticalWireSelector()VerticalFaceSelector()FlatSelector(Selector)- Filters entities which are more or less "lying flat" with respect to the XY-plane. Optional length criteria can be specified to filter entities even more.FlatEdgeSelector()FlatWireSelector()FlatFaceSelector()3. Association SelectorsSharedVerticesWithObjectSelector(Selector)- Filters entities which have one or more points in common with a specified reference object.  The reference object can be any individual solid, face, wire, edge or vertex.SameLengthAsObjectSelector(Selector)- Filters entities which have the same length as the reference object.  The reference object can either be a an edge or wire.SameHeightAsObjectSelector(Selector)- Filters entities which have the same vertical "height" as a reference object.SameVertexCountAsObjectSelector(Selector)- Filters entities which have the same number of vertices as a reference object.SameEdgeCountAsObjectSelector(Selector)- Filters entities with the same number of edges as a reference object.4. Location SelectorsRotatedBoxSelector(Selector)- Filters entities which fall inside a box which is rotated along the Z axis.get_box_selector(pt=(0, 0, 0), dp=(1, 1, 1))- convenience function which returns a CQBoxSelectorbased on a centre coordinate and a tuple of X,Y,Z sizeget_shifted_box_selector(from_selector, offset_by)- returns a newBoxSelectortranslated to a new location using an offset from its current position.get_box_selector_array(pts, dp=(1, 1, 1))- returns a compositeSelectorwhich is the sum of an array ofBoxSelectoreach centred at centre points defined byptsand each have the same size specified bydp.Selector ExamplesEdgeLengthSelectorSelects edges based on their length. Lengths can be specified in several different ways.# selects edges from solid with length 3.0es=EdgeLengthSelector(3.0)r=solid.edges(es)# selects edges with a list of edge lengthses=EdgeLengthSelector([3.0,1.4])# selects edges using string rules with >, <, >=, <=es=EdgeLengthSelector(">3.5")# selects edges which are 4.0 +/- 0.5 longes=EdgeLengthSelector(4.0,tolerance=0.5)VerticalEdgeSelectorIs a convenience selector which selects "vertical" edges, i.e edges withZcoordinate difference which exceed a tolerance (default tolerance is 0.1).# selects all vertical edgesvs=VerticalEdgeSelector()r=solid.edges(vs)# selects all vertical edges 3.2 and 2.0 mm tallvs=VerticalEdgeSelector([3.2,2.0])FlatEdgeSelectorSelects all edges which lie "flat" at a certainZaxis height.# select all edges at height = 1.0es=FlatEdgeSelector(1.0)# select all edges at heights 2, 5es=FlatEdgeSelector([2,5])HasXCoordinateSelectorHasYCoordinateSelectorHasZCoordinateSelector- selects edges which have specific coordinate values.  Theboth_endskeyword can specify whether both vertices of the edge conform to the coordinate requirement (True) or at least one vertex (False)# selects all edges which have X coordinate values = 3.0 at both endses=HasXCoordinateSelector(3.0,min_points=2)# selects all edges which have X coordinate values = 3.0 at least one endes=HasXCoordinateSelector(3.0,min_points=1)SharedVerticesWithObjectSelector- selects entities which have common vertices with a reference object.  The reference object can be either a solid, face, wire, edge or vertex.# selects all edges which have one of its end points common with a specific vertexes=SharedVerticesWithObjectSelector(Vector(1,2,1))# selects all edges which have any of its end points common with any vertex# belonging to a specified faceface1=solid.faces(FlatFaceSelector(1.0)).val()es=SharedVerticesWithObjectSelector(face1)To DoMore modules/functionality for the package extracted from previous work in different placesSolids: solid construction classesOthers TBDDocumentation (possibly with sphinx, but in this README as a minimum)Deployment notesReleasesv.0.4.0 - First release on PyPIv.0.5.0 - Update requires python v.3.8+ and OCP v.7.6+ CadQuery 2.1+v.0.5.3 - Updated with new helpers and updated setup.pyAuthorsCQ-Kitwas written byMichael Gale