Closed ghost closed 7 years ago
If all else fails, you might be able to use the nearest point and boolean selectors to get it done. Can you post your code so that I can try some things out?
Thanks @jmwright
This is how I've built the empty box:
import cadquery as cq
from Helpers import show
result = cq.Workplane("XY").box(2, 2, 2)
result = result.faces(">Z").shell(-0.2)
show(result)
We've got a newer DirectionNth selector (largely undocumented still unfortunately) that can be used to select faces at a certain depth. I started to experiment with that, but am having trouble getting all of the edges selected at once so that FreeCAD doesn't reorder them on me.
https://github.com/dcowden/cadquery/pull/147/files
import cadquery as cq
from cadquery import selectors
from Helpers import show
result = cq.Workplane("XY").box(2, 2, 2)
result = result.faces(">Z").shell(-0.2)
result = result.faces(">Z").edges("<X[0]").chamfer(0.125)
show(result)
I tried the nearest point selector too, but that only selected one edge and is kind of a hackey way to do that selection. I feel like the DirectionNthSelector is probably the way to do what you want, I just haven't spent enough time with it yet to get it right.
@adam-urbanczyk You implemented the DirectionNthSelector. Do you have any thoughts on this?
@hackscribble The following code does get the result for me that you're looking for, but it's a horrible way to handle the selectors. FreeCAD often selects orders edges differently from execution to execution, so you may not get the same result.
import cadquery as cq
from cadquery import selectors
from Helpers import show
result = cq.Workplane("XY").box(2, 2, 2)
result = result.faces(">Z").shell(-0.2)
result = result.faces(">Z").edges("<X[1]").chamfer(0.125)
result = result.faces(">Z").edges("<Y[2]").chamfer(0.125)
result = result.faces(">Z").edges("<X[0]").chamfer(0.125)
result = result.faces(">Z").edges("<Y[0]").chamfer(0.125)
show(result)
@hackscribble I'm intrigued by this example, thanks for posting it.
CQ 2.0 will feature 'created by' and 'modifid by' queries, which makes many selections easier. In this case, it makes it easier, but not really easy. We could do:
result = result.shell(-0.2,
name="myshell").edges(createdby="myshell").edges(">Z")
I think that would work here, but its still not super-intuitive.
On Mon, Apr 10, 2017 at 11:13 AM, Jeremy Wright notifications@github.com wrote:
@hackscribble https://github.com/hackscribble The following code does get the result for me that you're looking for, but it's a horrible way to handle the selectors. FreeCAD often selects orders edges differently from execution to execution, so you may not get the same result.
import cadquery as cqfrom cadquery import selectorsfrom Helpers import show
result = cq.Workplane("XY").box(2, 2, 2) result = result.faces(">Z").shell(-0.2) result = result.faces(">Z").edges("<X[1]").chamfer(0.125) result = result.faces(">Z").edges("<Y[2]").chamfer(0.125) result = result.faces(">Z").edges("<X[0]").chamfer(0.125) result = result.faces(">Z").edges("<Y[0]").chamfer(0.125)
show(result)
[image: screenshot_2017-04-10_11-12-07] https://cloud.githubusercontent.com/assets/1015439/24868623/9d1a4c8a-1dde-11e7-8133-13eb27e36b2d.png
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/dcowden/cadquery/issues/177#issuecomment-292980386, or mute the thread https://github.com/notifications/unsubscribe-auth/ABPOA8VMsvVDZq50_JxZR1To8wsn4timks5rukcPgaJpZM4M4veI .
Many thanks @jmwright and @dcowden
I'll play with that solution.
Maybe another way entirely would be to create a separate solid, in this case an upside down square-based pyramid with the point cut off, position it upside down over the shell at the right height and do a cut?
EDIT
Cutter method seems to work ...
box_x = 12
box_y = 6
box_z = 5
thickness = 0.6
chamfer_top = 0.3
chamfer_side = 0.3
plate = cq.Workplane("XY").box(box_x, box_y, box_z).faces(">Z)") \
.shell(-thickness)
cutter = cq.Workplane("XY").workplane(offset=box_z/2.0) \
.rect(box_x-2*(thickness-chamfer_top), box_y-2*(thickness-chamfer_top)) \
.workplane(offset=-chamfer_side).rect(box_x-2*thickness, box_y-2*thickness) \
.loft(combine=True)
result = plate.cut(cutter)
show(result)
yes, another option is that you can always 'break out' of the CQ chained selectors and do things pro grammatically by hand.
for example, if you do ....edges().val(), you'll get a list of edge objects. then, you could loop through them by hand and do arbitrary logic. that'd be uglier than Jeremy's earlier suggestion though.
On Mon, Apr 10, 2017 at 11:24 AM, Hackscribble notifications@github.com wrote:
Many thanks @jmwright https://github.com/jmwright and @dcowden https://github.com/dcowden
I'll play with that solution.
Maybe another way entirely would be to create a separate solid, in this case an upside down square-based pyramid with the point cut off, position it upside down over the shell at the right height and do a cut?
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/dcowden/cadquery/issues/177#issuecomment-292983992, or mute the thread https://github.com/notifications/unsubscribe-auth/ABPOA5_e8nwaCJlFWrVJe27MrGglyQZTks5rukm_gaJpZM4M4veI .
I do think that there's probably a clever way to combine the selectors to accomplish this, but I haven't come up with it yet. I'll try different ideas as they come to me today. @dcowden is right though, if you want to you can break the edges out and manually check if they're in the right location to be chamfered.
http://dcowden.github.io/cadquery/classreference.html#cadquery.StringSyntaxSelector
I did not test it (yet), but something as follows should work too:
res_final = res_shelled.faces(">Z").edges("not(<X or >X or <Y or >Y)").chamfer(0.125)
In the end what we probably need here is an inner/outer boundry selector.
Thanks Adam. An inner/outer boundary selector does sound ideal for this use case.
I tried the code you suggested, so I now have the following.
import cadquery as cq
from cadquery import selectors
from Helpers import show
result = cq.Workplane("XY").box(2, 2, 2)
result = result.faces(">Z").shell(-0.2)
# result = result.faces(">Z").edges("<X[1]").chamfer(0.125)
# result = result.faces(">Z").edges("<Y[2]").chamfer(0.125)
# result = result.faces(">Z").edges("<X[0]").chamfer(0.125)
# result = result.faces(">Z").edges("<Y[0]").chamfer(0.125)
result_final = result.faces(">Z").edges("not(<X or >X or <Y or >Y)").chamfer(0.125)
show(result_final)
However, I get this error:
Traceback (most recent call last):
File "/usr/lib/freecad/Mod/CadQuery/Gui/Command.py", line 128, in Activated
imp.load_source('temp_module', tempFile.name)
File "/tmp/tmpufIRtg", line 12, in <module>
result_final = result.faces(">Z").edges("not(<X or >X or <Y or >Y)").chamfer(0.125)
File "/usr/lib/freecad/Mod/CadQuery/Libs/cadquery-lib/cadquery/cq.py", line 582, in edges
return self._selectObjects('Edges', selector)
File "/usr/lib/freecad/Mod/CadQuery/Libs/cadquery-lib/cadquery/cq.py", line 483, in _selectObjects
selectorObj = selectors.StringSyntaxSelector(selector)
File "/usr/lib/freecad/Mod/CadQuery/Libs/cadquery-lib/cadquery/selectors.py", line 549, in __init__
parsing_result = _grammar.parseString(selectorString)
File "/usr/lib/freecad/Mod/CadQuery/Libs/pyparsing.py", line 1228, in parseString
raise exc
Expected {{XY | XZ | X | YZ | Y | Z | Combine:({"(" Combine:({Combine:({[{"+" | "-"}] W:(0123...)}) [{"." [W:(0123...)]}]}) "," Combine:({Combine:({[{"+" | "-"}] W:(0123...)}) [{"." [W:(0123...)]}]}) "," Combine:({Combine:({[{"+" | "-"}] W:(0123...)}) [{"." [W:(0123...)]}]}) ")"})} | {"%" Plane | Cylinder | Sphere | Cone | Line | Circle | Arc} | {> | < {XY | XZ | X | YZ | Y | Z | Combine:({"(" Combine:({Combine:({[{"+" | "-"}] W:(0123...)}) [{"." [W:(0123...)]}]}) "," Combine:({Combine:({[{"+" | "-"}] W:(0123...)}) [{"." [W:(0123...)]}]}) "," Combine:({Combine:({[{"+" | "-"}] W:(0123...)}) [{"." [W:(0123...)]}]}) ")"})} [{Suppress:("[") Group:({["-"] W:(0123...)}) Suppress:("]")}]} | {| | # | + | - {XY | XZ | X | YZ | Y | Z | Combine:({"(" Combine:({Combine:({[{"+" | "-"}] W:(0123...)}) [{"." [W:(0123...)]}]}) "," Combine:({Combine:({[{"+" | "-"}] W:(0123...)}) [{"." [W:(0123...)]}]}) "," Combine:({Combine:({[{"+" | "-"}] W:(0123...)}) [{"." [W:(0123...)]}]}) ")"})}} | front | back | left | right | top | bottom} (at char 0), (line:1, col:1)
inner/ounter boundary selector is a great idea!
so like:
object.faces(">Z").outerEdges() or .innerEdges() ?
On Mon, Apr 10, 2017 at 3:21 PM, Adam Urbańczyk notifications@github.com wrote:
I did not test it (yet), but something as follows should work too:
res_final = res_shelled.faces(">Z").edges("not(
X or <Y or Y)").chamfer(0.125)
In the end what we probably need here is an inner/outer boundry selector.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/dcowden/cadquery/issues/177#issuecomment-293051792, or mute the thread https://github.com/notifications/unsubscribe-auth/ABPOA5e9hd3CPP-avBBEBdwPvGdrOlDGks5ruoE7gaJpZM4M4veI .
There are some cases where there could be multiple sets of "concentric" inner edges/wires. Maybe add an index or selector for innerEdges() and outerEdges()?
yes that's true, you can have islands-- so yes i think it would have to return a list. but i think we already have chained selectors that return the first, last, or an indexed value after one selector gives a list, right?
On Mon, Apr 10, 2017 at 5:35 PM, Jeremy Wright notifications@github.com wrote:
There are some cases where there could be multiple sets of "concentric" inner edges/wires. Maybe add an index or selector for innerEdges() and outerEdges()?
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/dcowden/cadquery/issues/177#issuecomment-293086185, or mute the thread https://github.com/notifications/unsubscribe-auth/ABPOA2EPdmQ1hk1aJXpVkhpgmDPVfJTSks5ruqCtgaJpZM4M4veI .
@dcowden Is this what you're talking about? It refers to the stack.
http://dcowden.github.io/cadquery/classreference.html?highlight=first#cadquery.CQ.first
@hackscribble We've hijacked your issue here, but please feel free to post back. It's still your issue.
yes, and CQ.item(i).
so
On Mon, Apr 10, 2017 at 7:28 PM, Jeremy Wright notifications@github.com wrote:
@dcowden https://github.com/dcowden Is this what you're talking about? It refers to the stack.
http://dcowden.github.io/cadquery/classreference.html? highlight=first#cadquery.CQ.first
@hackscribble https://github.com/hackscribble We've hijacked your issue here, but please feel free to post back. It's still your issue.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/dcowden/cadquery/issues/177#issuecomment-293107460, or mute the thread https://github.com/notifications/unsubscribe-auth/ABPOAx0PIMaQ2k7WN6vExQzADOAoPs5jks5rursqgaJpZM4M4veI .
@hackscribble We've hijacked your issue here, but please feel free to post back. It's still your issue.
Happy to be hijacked :smile:
@jmwright it works for me. Weird... the only reason I can think of is we are running on different pyParsing versions (?). If you think it will add anything, I can write a test to cover this exact selector phrase.
import cadquery as cq
from cadquery import selectors
from Helpers import show
result = cq.Workplane("XY").box(2, 2, 2)
result = result.faces(">Z").shell(-0.2)
result_final = result.faces(">Z").edges("not(<X or >X or <Y or >Y)").chamfer(0.125)
@jmwright CI says all checks pass. Could it be something on your side then?
@adam-urbanczyk What version of FreeCAD are you running? I've got the latest stable installed for Linux, which is 0.16 R6707 from the FreeCAD stable Ubuntu PPA. It looks like I also have pyparsing 2.1.5 embedded with the FreeCAD module too.
wow, awesome Adam! that ends up being pretty simple-- i wouldnt have thought of that method at all. An excellent use of the powerful logical selectors.
On Tue, Apr 11, 2017 at 2:23 PM, Adam Urbańczyk notifications@github.com wrote:
@jmwright https://github.com/jmwright it works for me. Weird... the onnly reason I can think of is we are running on different pyParsing versions (?). If you think it will add anything, I can write a test to cover this exact selector pharse.
import cadquery as cq from cadquery import selectors from Helpers import show
result = cq.Workplane("XY").box(2, 2, 2) result = result.faces(">Z").shell(-0.2)
result_final = result.faces(">Z").edges("not(
X or Y)").chamfer(0.125) [image: cq_chamfer_test] https://cloud.githubusercontent.com/assets/13981538/24924286/91723c00-1ef4-11e7-96d2-e62efc8d184c.png
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/dcowden/cadquery/issues/177#issuecomment-293355694, or mute the thread https://github.com/notifications/unsubscribe-auth/ABPOA7IIzIYCZOa4mv4Q026BRrRD6Rmtks5ru8UJgaJpZM4M4veI .
@jmwright Here is my FreeCad info:
OS: Ubuntu 15.10
Word size of OS: 64-bit
Word size of FreeCAD: 64-bit
Version: 0.16.6692 (Git)
Build type: None
Branch: master
Hash: 87293fac9bb85c5f5e0ebf563374e27d565116ae
Python version: 2.7.10
Qt version: 4.8.6
Coin version: 4.0.0a
OCC version: 6.8.0.oce-0.17
In my dev env I use the following pyparsing version:
pyparsing 2.0.3 py27_0
We're pretty close on FreeCAD versions then, and the exception I get is from pyparsing. I'm thinking something probably changed between version 2.0.3 and 2.1.5 of pyparsing that's causing the error.
@hackscribble to sum up, I think the code below solves you problem. Could you verify that it works for you? If so, I'd propose we close the issue.
import cadquery as cq
from Helpers import show
result = cq.Workplane("XY").box(2, 2, 2).\
faces(">Z").shell(-0.2).\
faces(">Z").edges("not(<X or >X or <Y or >Y)").\
chamfer(0.125)
show(result)
@jmwright Turns out that I am also using 2.1.5 (which is the version bundled with your FreeCad workbench):
>>> import pyparsing
>>> pyparsing.__version__
'2.1.5'
>>> pyparsing
<module 'pyparsing' from '/home/adam/.FreeCAD/Mod/CadQuery/Libs/pyparsing.pyc'>
>>>
Could you check if the following works in your environment:
from cadquery import selectors
selectors._expression_grammar.parseString('not(<X or >X or <Y or >Y)',parseAll=True)
Hi @adam-urbanczyk I get the same error message as Jim when I run that script.
When I try the other code, I get the error message:
Traceback (most recent call last):
File "/usr/lib/freecad/Mod/CadQuery/Gui/Command.py", line 181, in Activated
imp.load_source('temp_module', tempFile.name)
File "/tmp/tmp3C_FMV", line 2, in <module>
selectors._expression_grammar.parseString('not(<X or >X or <Y or >Y)',parseAll=True)
'module' object has no attribute '_expression_grammar'
@adam-urbanczyk When I try to run the code you posted, I get the following error.
Running the Python command 'CadQueryExecuteScript' failed:
Traceback (most recent call last):
File "/usr/lib/freecad/Mod/CadQuery/Gui/Command.py", line 128, in Activated
imp.load_source('temp_module', tempFile.name)
File "/tmp/tmpzAEiK1", line 13, in <module>
selectors._expression_grammar.parseString('not(<X or >X or <Y or >Y)',parseAll=True)
'module' object has no attribute '_expression_grammar'
I'm running Ubuntu 16.04 whereas you're running 15.10. Do we have a problem with an underlying library version?
It has to be there:
Could it be that you are not using the latest cq version in your env?
@adam-urbanczyk Thanks for the catch. I have the latest release in the workbench, but it looks like your selector changes aren't in that release (0.5.2). We've been wanting to bump CadQuery's version number up to 1.0.0 for a long time. I think now is the time to do it. I'll start prepping that and hopefully get it released tomorrow.
@hackscribble In the meantime, if you're using the FreeCAD workbench you can download the latest master of CadQuery and replace the contents of this directory: https://github.com/jmwright/cadquery-freecad-module/tree/master/CadQuery/Libs/cadquery-lib
If you've installed CadQuery using pip, you can download the CadQuery master and then manipulate sys.path to look at that directory before the one pip installed. I can explain in more detail if you want. Sorry for the hacks, but I wanted to give you a workaround until Dave or I get a chance to push 1.0.0 out.
Thanks @jmwright and @adam-urbanczyk
I have updated cadquery-lib
and the selector works :smile:
Thanks everyone for your help.
Just FYI, the new version of CadQuery and the FreeCAD module have been released: https://groups.google.com/forum/#!topic/cadquery/DOMyLCpvrjE
@dcowden @jmwright I'll open an issue about lacking documentation of the extended selector syntax. I think it would be good to spread the word.
I'm trying to script an object like the image below. I can get the "empty box" using box() and shell() but I can't work out how to select the four inner edges of the top face to apply a chamfer. Is this possible?
Thanks