Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plugin for local coordinate system selectors #35

Merged
merged 11 commits into from
Jun 8, 2024
37 changes: 37 additions & 0 deletions plugins/localselectors/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Sample Plugin

This plugin modifies `Workplane` selectors so that they can be used to specify axes in the local coordinate plane.
This is done by using the lowercase letters `x`, `y`, and `z` instead of the uppercase ones.

## Installation

```
pip install -e "git+https://github.com/CadQuery/cadquery-plugins.git#egg=localcoordinates&subdirectory=plugins/localcoordinates"
```


## Dependencies

This plugin has no dependencies other than the cadquery library. To install CadQuery, follow the [instructions in its readme](https://github.com/CadQuery/cadquery#getting-started).
It uses a lot of internal structures, so it may break more easily on later versions of CadQuery than other plugins.
It was tested on CadQuery 2.5, feel free to post an issue in my [fork](https://github.com/cactorium/cadquery-plugins) if you run into any issues

## Usage

To use this plugin after it has been installed, import it to automatically patch its function(s) into the `cadquery.Workplane` class. Any function that uses string selectors should now work with these new selectors after import, but be sure to import `cadquery` first.

```python
import cadquery as cq

result = (cq.Workplane().rect(50, 50)
.extrude(50))

new_workplane = (result.faces(">x") # this should be the same as '>X' because we're starting off in the default coordinate system
.workplane())
result2 = (new_workplane.rect(30, 30)
.extrude(30))

new_workplane = (result2
.faces(">z")
.workplane()) # this should be the face sticking away from the first cube
```
Empty file.
153 changes: 153 additions & 0 deletions plugins/localselectors/localselectors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
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
51 changes: 51 additions & 0 deletions plugins/localselectors/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from setuptools import setup, find_packages

# Change these variables to set the information for your plugin
version = "1.0.0" # Please update this version number when updating the plugin
plugin_name = "localselectors" # The name of your plugin
description = "Adds local coordinator selectors to CadQuery"
long_description = (
"Monkey patches in local coordinate selectors so you can use things like '>x'"
)
author = "Kelvin Ly"
author_email = "cactorium"
packages = [] # List of packages that will be installed with this plugin
py_modules = ["localselectors"] # Put the name of your plugin's .py file here
install_requires = (
[]
) # Any dependencies that pip also needs to install to make this plugin work


setup(
name=plugin_name,
version=version,
url="https://github.com/CadQuery/cadquery-plugins",
license="Apache Public License 2.0",
author=author,
author_email=author_email,
description=description,
long_description=long_description,
packages=packages,
py_modules=py_modules,
install_requires=install_requires,
include_package_data=True,
zip_safe=False,
platforms="any",
test_suite="tests",
classifiers=[
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"Intended Audience :: End Users/Desktop",
"Intended Audience :: Information Technology",
"Intended Audience :: Science/Research",
"Intended Audience :: System Administrators",
"License :: OSI Approved :: Apache Software License",
"Operating System :: POSIX",
"Operating System :: MacOS",
"Operating System :: Unix",
"Programming Language :: Python",
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Internet",
"Topic :: Scientific/Engineering",
],
)
Loading