import cadquery as cq from cadquery.occ_impl.geom import Vector from cadquery.occ_impl.shape_protocols import ( geom_LUT_EDGE, geom_LUT_FACE, ) from pyparsing import ( pyparsing_common, Literal, Word, nums, Optional, Combine, oneOf, Group, infixNotation, opAssoc, ) def _makeGrammar(): """ Define the simple string selector grammar using PyParsing """ # float definition point = Literal(".") plusmin = Literal("+") | Literal("-") number = Word(nums) integer = Combine(Optional(plusmin) + number) floatn = Combine(integer + Optional(point + Optional(number))) # vector definition lbracket = Literal("(") rbracket = Literal(")") comma = Literal(",") vector = Combine( lbracket + floatn("x") + comma + floatn("y") + comma + floatn("z") + rbracket, adjacent=False, ) # direction definition simple_dir = oneOf(["X", "Y", "Z", "XY", "XZ", "YZ"] + ["x", "y", "z", "xy", "xz", "yz"]) direction = simple_dir("simple_dir") | vector("vector_dir") # CQ type definition cqtype = oneOf( set(geom_LUT_EDGE.values()) | set(geom_LUT_FACE.values()), caseless=True, ) cqtype = cqtype.setParseAction(pyparsing_common.upcaseTokens) # type operator type_op = Literal("%") # direction operator direction_op = oneOf([">", "<"]) # center Nth operator center_nth_op = oneOf([">>", "<<"]) # index definition ix_number = Group(Optional("-") + Word(nums)) lsqbracket = Literal("[").suppress() rsqbracket = Literal("]").suppress() index = lsqbracket + ix_number("index") + rsqbracket # other operators other_op = oneOf(["|", "#", "+", "-"]) # named view named_view = oneOf(["front", "back", "left", "right", "top", "bottom"]) return ( direction("only_dir") | (type_op("type_op") + cqtype("cq_type")) | (direction_op("dir_op") + direction("dir") + Optional(index)) | (center_nth_op("center_nth_op") + direction("dir") + Optional(index)) | (other_op("other_op") + direction("dir")) | named_view("named_view") ) old_getVector = cq.selectors._SimpleStringSyntaxSelector._getVector def _getVector(self, pr): if "simple_dir" in pr and pr.simple_dir in cq.selectors._SimpleStringSyntaxSelector.local_axes: return cq.selectors._SimpleStringSyntaxSelector.local_axes[pr.simple_dir] else: return old_getVector(self, pr) class LocalCoordinates: def __init__(self, plane): self.plane = plane self.old_axes = None def __enter__(self): self.old_axes, cq.selectors._SimpleStringSyntaxSelector.local_axes = (cq.selectors._SimpleStringSyntaxSelector.local_axes, { 'x': self.plane.xDir, 'y': self.plane.yDir, 'z': self.plane.zDir, 'xy': self.plane.xDir + self.plane.yDir, 'yz': self.plane.yDir + self.plane.zDir, 'xz': self.plane.xDir + self.plane.zDir, }) def __exit__(self, _exc_type, _exc_value, _traceback): cq.selectors._SimpleStringSyntaxSelector.local_axes = self.old_axes def _filter(self, objs, selector): selectorObj: Selector if selector: if isinstance(selector, str): with LocalCoordinates(self.plane): selectorObj = cq.selectors.StringSyntaxSelector(selector) else: selectorObj = selector toReturn = selectorObj.filter(objs) else: toReturn = objs return toReturn cq.selectors._SimpleStringSyntaxSelector.local_axes = { "x": Vector(1, 0, 0), "y": Vector(0, 1, 0), "z": Vector(0, 0, 1), "xy": Vector(1, 1, 0), "yz": Vector(0, 1, 1), "xz": Vector(1, 0, 1), } cq.selectors._SimpleStringSyntaxSelector._getVector = _getVector cq.selectors._grammar = _makeGrammar() # make a grammar instance cq.selectors._expression_grammar = cq.selectors._makeExpressionGrammar(cq.selectors._grammar) cq.Workplane._filter = _filter