Draft of the look and feel of a simple pythonic API for CSG

5 views
Skip to first unread message

Bryan Bishop

unread,
Feb 21, 2010, 2:28:06 PM2/21/10
to pythono...@gna.org, Open Manufacturing, open...@rocklinux.org, kan...@gmail.com
Hey Thomas,

A while ago, you and Jelle were talking about creating an API for
pythonOCC that would simplify and hide the ugly guts of OCC. I haven't
seen too much on this front in a while, but a few days ago I was
talking with Christian Siefkes, Ben Lipkowitz, and a few others about
a potential look and feel for python that would hide away OCC, or even
BRLCAD or anything else that we opt to throw under the hood.

I was wondering if I could get some thoughts on how this feels:

http://designfiles.org/~bryan/csg.py

sphere = Sphere(position=(0, 0, 0), radius="5 mm")
base = Circle(diameter="20 mm", position=(0,0,0))
cylinder = base.extrude(length="10 mm", direction=Vector(0, 0, 1))
sphere.position = (0, 0, 10)
lamp_post = cylinder.fuse(sphere)

#if you're in an X11 opengl session thingy:
display.add(lamp_post)

#or if you want to export it:
iges_data = lamp_post.to_iges()
file_handler = open("lamp_post.iges", "w")
file_handler.write(iges_data)
file_handler.close()

And maybe some parametric PAF stuff?

base = Circle("base of base plate", diameter="20 mm")
base_plate = Extrusion(base, "10mm", Vector(0,0,1))
#OR: base_plate = base.extrude("10 mm", Vector(0, 0, 1))
#OR: base_plate = extrude(base, "10 mm", Vector(0, 0, 1))
base_plate.name = "cylinder 1"

assert base_plate == Cylinder("cylinder 1", diameter="20 mm",
height="10 mm"), "Cylinder and base plate should be exactly the same"

base_plate.diameter = "25 mm"
assert base.diameter == "25 mm", "base diameter should have been updated"

base.diameter = "32 mm"
assert base_plate.diameter == "32 mm", "base plate diameter should
have been updated"

I am not entirely sure how to do this sort of feature tree. Any hints
or advice from interested bystanders would be hawt. Also, I am sending
this over to OpenSCAD because they have a similar thing going on, with
their own parser, but nevertheless would probably be interested in
python hooks and somehow influencing how those hooks feel & work.

Thanks,

- Bryan
http://heybryan.org/
1 512 203 0507

Bryan Bishop

unread,
Feb 21, 2010, 2:54:57 PM2/21/10
to Jelle Feringa, kan...@gmail.com, pythonOCC users mailing list., Open Manufacturing, open...@rocklinux.org
On Sun, Feb 21, 2010 at 1:49 PM, Jelle Feringa wrote:

> On Feb 21, 2010, at 8:28 PM, Bryan Bishop wrote:
>> Hey Thomas,
>>
>> A while ago, you and Jelle were talking about creating an API for
>> pythonOCC that would simplify and hide the ugly guts of OCC. I haven't
>> seen too much on this front in a while, but a few days ago I was
>> talking with Christian Siefkes, Ben Lipkowitz, and a few others about
>> a potential look and feel for python that would hide away OCC, or even
>> BRLCAD or anything else that we opt to throw under the hood.
>
> Not good.
> Those libs are _massively_ different, so no way.

Have you checked out libged from BRLCAD? There's CSG support going on,
believe it or not.

> When discussing the higher level API ( yes please ;'), please do so in reference to the work-in-progress: /src/addons/KBE/Level2API.py

Granted. Here's a link, then, since I can't seem to find it elsewhere
on the internets:

http://designfiles.org/lab/opencascade/Level2API.py

Bryan Bishop

unread,
Feb 21, 2010, 3:08:02 PM2/21/10
to Thomas Paviot, kan...@gmail.com, pythonOCC users mailing list., Open Manufacturing, open...@rocklinux.org
On Sun, Feb 21, 2010 at 1:59 PM, Thomas Paviot <tpa...@gmail.com> wrote:
> Hi Bryan,

Hey Thomas. Thanks for the quick reply.

> *Very* intersting thread, thank you for opening it.

had to be done

> The topic you're talking about is still an open issue in CAD and
> contributions from the open source community would certainly be a great
> thing. However, it's not only a technical problem (in terms of
> implementation), but rather a scientific one. As far as I know, a good
> starting point is the second edition of the AP203 STEP application protocol,
> especially the ISO10303-55 (‘Procedural and hybrid representation’). Being
> able to implement this part of the standard in a scripting langage would
> already be a huge step.

Yes, absolutely. I do not have access to the ISO documentation on
STEP. Maybe you can send a few files my way to help me out? There is
one other resource that I am aware of- namely, the STEP export API in
the OpenCASCADE source code. From this, it is possible to extract the
valid entities that can be found in a STEP file in the EXPRESS
language. But then you have the issue of adopting OCC's peculiar
standard or peculiar implementation of STEP. Here's a skeleton of STEP
features that I figured out by looking at the OCC source:

http://designfiles.org/~bryan/step_importer.py

> I think this approach would certainly be the easiest way to get started: the
> STEP data model has been widely discussed and validated.

Right now what we need most are the ISO 10303 documents. Otherwise I
suspect nothing will happen. Whether or not this could be used to make
an API is also another issue- I suspect so, but maybe we should forge
ahead even if we can't get the ISO docs?

Bryan Bishop

unread,
Feb 21, 2010, 3:30:05 PM2/21/10
to open...@rocklinux.org, Open Manufacturing, kan...@gmail.com
---------- Forwarded message ----------
From: Thomas Paviot <tpa...@gmail.com>
Date: Sun, Feb 21, 2010 at 2:24 PM
Subject: Re: [Pythonocc-users] Draft of the look and feel of a simple
pythonic API for CSG
To: Bryan Bishop <kan...@gmail.com>


2010/2/21 Bryan Bishop <kan...@gmail.com>

Bryan,
You'll have to buy the CD in order to have documentation. However,
there's a webpage dedicated to AP203ed2 here:
http://www.steptools.com/support/stdev_docs/express/ap203e2/index.html
The EXPRESS data model file can be browsed at:
http://www.steptools.com/support/stdev_docs/express/ap203e2/html/index.html
I learnt most of the STEP standard by reading the EXPRESS files.
Thomas


--

Bryan Bishop

unread,
Feb 21, 2010, 4:31:42 PM2/21/10
to Open Manufacturing
---------- Forwarded message ----------
From: Thomas Paviot <tpa...@gmail.com>
Date: Sun, Feb 21, 2010 at 3:27 PM
Subject: Re: [Pythonocc-users] Draft of the look and feel of a simple pythonic API for CSG
To: "pythonOCC users mailing list." <pythono...@gna.org>


2010/2/21 Bryan Bishop <kan...@gmail.com>
On Sun, Feb 21, 2010 at 1:59 PM, Thomas Paviot <tpa...@gmail.com> wrote:

> Hi Bryan,

Hey Thomas. Thanks for the quick reply.

> *Very* intersting thread, thank you for opening it.

had to be done

> The topic you're talking about is still an open issue in CAD and
> contributions from the open source community would certainly be a great
> thing. However, it's not only a technical problem (in terms of
> implementation), but rather a scientific one. As far as I know, a good
> starting point is the second edition of the AP203 STEP application protocol,
> especially the ISO10303-55 (‘Procedural and hybrid representation’). Being
> able to implement this part of the standard in a scripting langage would
> already be a huge step.

Yes, absolutely. I do not have access to the ISO documentation on
STEP. Maybe you can send a few files my way to help me out? There is
one other resource that I am aware of- namely, the STEP export API in
the OpenCASCADE source code. From this, it is possible to extract the
valid entities that can be found in a STEP file in the EXPRESS
language. But then you have the issue of adopting OCC's peculiar
standard or peculiar implementation of STEP. Here's a skeleton of STEP
features that I figured out by looking at the OCC source:

http://designfiles.org/~bryan/step_importer.py

Hi Bryan,

Just browsed your proposal. It looks like you're starting to develop a STEP parser. Please don't do that! It's a nightmare! I worked 2 full weeks on trying to write a basic STEP parser, it was a mistake. Finally I moved to Eclipse+JSDAI to parse EXPRESS files, it's much better.

When pointing out the STEP Procedural design representation, I was just meaning that it could be a perfect template for a high level API on top of pythonOCC. Let's take an example. In the AP203ed2.exp file, I can read the following description for the 'surface_of_revolution' entity:

ENTITY surface_of_revolution
  SUBTYPE OF (swept_surface);
   axis_position       : axis1_placement;
 DERIVE
   axis_line : line := representation_item('')||
                     geometric_representation_item()|| curve()||
                     line(axis_position.location, representation_item('')||
                     geometric_representation_item()||
                     vector(axis_position.z, 1.0));
 END_ENTITY;
We could have something similar in Python:

class surface_of_revolution(swept_surface):
    def blabla(self) etc.

The issue is then the mapping between AP203ed2 entities and OCC classes. The AP203ed2 standard however doesn't specify any method about how to generate the entities (ie the functions or class methods to use).

I see two ways to get done with implementation:
- use a set of functions, eg. create_surface_of_revolution(), create_line(), create_vector() etc.
- use a 'Compute' method of each python class, this method being called over all instances.

BTW, I'm not sure the link to the AP230ed2.exp is the 'official' one. It may be the latest draft before the release. The only way to have the official one is to buy the CD (the price is about 300€ in France).

Thomas



> I think this approach would certainly be the easiest way to get started: the
> STEP data model has been widely discussed and validated.

Right now what we need most are the ISO 10303 documents. Otherwise I
suspect nothing will happen. Whether or not this could be used to make
an API is also another issue- I suspect so, but maybe we should forge
ahead even if we can't get the ISO docs?


_______________________________________________
Pythonocc-users mailing list
Pythono...@gna.org
https://mail.gna.org/listinfo/pythonocc-users




--

Paul D. Fernhout

unread,
Feb 22, 2010, 12:32:25 AM2/22/10
to openmanu...@googlegroups.com

I don't really know what you are talking about, and this is not really what
you are asking about, but here are some comments on units in Python. :-)

If you are willing to standardize you units for each function and variable,
we used underscores in our Garden Simulator, like so:
sphere = Sphere(position=(0, 0, 0), radius_mm=5)
But that is not so flexible as strings. Still, you could maybe just go with
a standard metric set? :-)

You could also try this:
sphere = Sphere(position=(0, 0, 0), radius = unit_mm(5))
where unit_mm is some function returning some sort of object that has a
value and a unit and can do related math. Or you could define a class,
something like this UnitFloat here:
http://stackoverflow.com/questions/1852720/python-values-with-units/1852777
So:
sphere = Sphere(position=(0, 0, 0), radius = UnitFloat(5, "mm"))

I'm not saying your string approach will not work. Just providing some
alternatives.

Another idea is to define conversion constants:
http://mail.python.org/pipermail/python-dev/2009-June/090189.html

In another language like Smalltalk, you might have units like this:
sphere := Sphere position: (Point x: 0 y: 0 z: 0) radius: 5 mm.

The mm there is a message to an object representing 5 to convert itself to
an object representing a number with units attached to it, where you have
essentially modified the number class to understand that. There is another
approach here also for Smalltalk which changes less:
http://www.cincomsmalltalk.com/publicRepository/Measurements.html

You could also do something funky with overriding __mul__ on a Unit object,
to get effects when you multiply things:
http://docs.python.org/reference/datamodel.html
"object.__mul__(self, other)"

So then you could do at least the reverse of what most people expect:
sphere = Sphere(position=(0, 0, 0), radius = mm * 5)

Here is an example related to parsing to show how to do funky things with
cascading references:
http://pyparsing.wikispaces.com/
Of course, you could use a library like that to parse strings with complex
units, too, like "cm/s".

We chose the underscore naming convention because it was fastest. We were
using compiled Delphi. Python might be so slow as to not notice a difference
from unit objects or even strings. Also, we tended to just use a few
standard units.

SciPy implies using tuples is more Pythonic in a way:
http://sourceforge.net/project/shownotes.php?release_id=655674
http://docs.scipy.org/doc/scipy/reference/constants.html
So:
sphere = Sphere(position=(0, 0, 0), radius = (5, "mm"))

I kind of like the class approach best though, now that I think about it,
where the unit class knows how to do calculations and conversions in a way
that tuples can't.

If you go with that approach, then you might do it for other things like
Point3D, like you do for the item with a Vector:
sphere = Sphere(position=Point3D(0, 0, 0), radius=UnitFloat(5, "mm"))

Then you might have:
assert base.diameter == UnitFloat(25, "mm")
assert base.diameter == UnitFloat(2.5, "cm")
assert base.diameter == UnitFloat(0.000025, "km")
# imagine comparisons to only two digits of precision
assert base.diameter == UnitFloatWithPrecision(0.98, "in", 2)
# imagine comparisons to some degree of tolerance
assert base.diameter == UnitFloatWithTolerancePercent(1, "in", 0.02)

Where behind the scenes you've overridden math and comparison operators and
maybe do double dispatching and so on.
http://en.wikipedia.org/wiki/Double_dispatch

No doubt there is a library for this somewhere in Python. :-)
Here's some code as I look now:
http://code.activestate.com/recipes/270589/
And they mention an even better one:
http://pypi.python.org/pypi/Unum/4.1.0
And they figured out how to do times the other way. :-)
>>> from unum.units import * # Load a number of common units.
>>> distance = 100*m
>>> time = 9.683*s
>>> speed = distance / time
>>> speed
10.3273778788 [m/s]
>>> speed.asUnit(mile/h)
23.1017437978 [mile/h]
>>> KE = 86*kg * speed / 2 # Should be speed squared!
>>> KE.asUnit(J)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "unum\__init__.py", line 171, in asUnit
s, o = self.matchUnits(other)
File "unum\__init__.py", line 258, in matchUnits
raise IncompatibleUnitsError(self, other)
unum.IncompatibleUnitsError: [kg.m/s] can't be used with [J]

For fun, here is one in Lisp talked about using prime numbers multiplied in
unexpected ways to represent combined units: :-)
http://www.isi.edu/isd/LOOM/documentation/measures-implementation.text

And probably other ways to do it I have not seen or thought of.

Anyway, I know you talked about unit stuff a year or more ago, so a bit
delayed in responding. :-)

And then stuff like this could be done as you got fancier: :-)
diameter == UnitFloatWithRandomDefectUsingMeanAndSD(1, "in", -0.01, 0.005)
That's from a drill bit that almost always is generally a little smaller
than you would like, but sometimes just a bit bigger. :-)

But you still need to convert this all back to plain numbers at some point
with overhead, which is why we just stuck the units in the names. But your
needs and bottlenecks, using Python, may be quite different.

--Paul Fernhout
http://www.pdfernhout.net/
====
The biggest challenge of the 21st century is the irony of technologies of
abundance in the hands of those thinking in terms of scarcity.

Bryan Bishop

unread,
Feb 22, 2010, 12:38:02 AM2/22/10
to openmanu...@googlegroups.com
On Sun, Feb 21, 2010 at 11:32 PM, Paul D. Fernhout wrote:
> I don't really know what you are talking about, and this is not really what
> you are asking about, but here are some comments on units in Python. :-)
>
> If you are willing to standardize you units for each function and variable,
> we used underscores in our Garden Simulator, like so:
>  sphere = Sphere(position=(0, 0, 0), radius_mm=5)

Oh my god. That is the most terrible idea ever. Paul, where have you been? :-(

http://designfiles.org/skdb/core/units.py
http://juanreyero.com/open/magnitude/
and there's at least one other.

> But that is not so flexible as strings. Still, you could maybe just go with
> a standard metric set? :-)

The reason I was thinking of a string was so that it could be passed
to the skdb Unit class (which also interoperates nicely with the
'magnitude' module, as well as sympy, etc.). I added in those nice
conversions between the different libraries that people define units
with, and I think I caught most of them.

Paul D. Fernhout

unread,
Feb 22, 2010, 8:44:40 AM2/22/10
to openmanu...@googlegroups.com
Bryan Bishop wrote:
> On Sun, Feb 21, 2010 at 11:32 PM, Paul D. Fernhout wrote:
>> I don't really know what you are talking about, and this is not really what
>> you are asking about, but here are some comments on units in Python. :-)
>>
>> If you are willing to standardize you units for each function and variable,
>> we used underscores in our Garden Simulator, like so:
>> sphere = Sphere(position=(0, 0, 0), radius_mm=5)
>
> Oh my god. That is the most terrible idea ever. Paul, where have you been? :-(

There is probably someplace in most code where raw values will be
manipulated, and in those cases, at least, a convention with units at the
end of a variable name after an underscore can still be useful sometimes.
The same goes for writing underlying C code for performance for various
tasks. As I said, that was something implemented fifteen years ago in a
compiled language. But the basic idea for creating self-documenting code can
still useful in some settings. For another example, even in Python, if you
use numpy, how are you going to name arrays of raw numbers, like in part of
a simulation? That is somewhere that such a convention could be useful as well.

> http://designfiles.org/skdb/core/units.py
> http://juanreyero.com/open/magnitude/
> and there's at least one other.
>
>> But that is not so flexible as strings. Still, you could maybe just go with
>> a standard metric set? :-)
>
> The reason I was thinking of a string was so that it could be passed
> to the skdb Unit class (which also interoperates nicely with the
> 'magnitude' module, as well as sympy, etc.). I added in those nice
> conversions between the different libraries that people define units
> with, and I think I caught most of them.

Thanks for the links and reference to SymPy:
http://code.google.com/p/sympy/
"SymPy is a Python library for symbolic mathematics. It aims to become a
full-featured computer algebra system (CAS) while keeping the code as simple
as possible in order to be comprehensible and easily extensible. SymPy is
written entirely in Python and does not require any external libraries."

The magnitude packages seems to be like one of the options I outlined.

Note that the Unum package (is that the one you are thinking of) seems more
sophisticated and more Pythonic in terms of syntactic sugar like 5*mm in
Unum versus mg(5, "mm") in magnitude. But there are mentions of it and two
others (Scalar and PhysicalQuantities) at the bottom of the magnitude page:
http://juanreyero.com/open/magnitude/
And they point out the clutter factor in the namespace for Unum. I can agree
that is an important issue a lot of the time.

I still like the Smalltalk way best for clarity. :-) "diameter := 5 mm."

As for using strings in one part with the expectation they will be converted
in another part, sometimes it's hard to work through what interdependencies
between software modules makes the most sense from different points of view.
For example, how much do you anticipate wanting to use (or at least test)
one package without another in various ways? That's not always an easy
question either to ask or answer, and often there is no right answer, only
tradeoffs. Still, occasionally there is an elegant choice with no serious
tradeoffs.

For example, the units.py class you link to creates a dependency on yaml and
FennObject. That may be fine for your code, but it can make it more brittle
in some ways for other users. There are different perspectives on whether
that is a good or bad thing relative to other priorities. Extreme
programming would say it is good, because you could refactor later easily if
you wanted to (if you have the right tools and tests) and it saves you time
now. Someone more focused on modularity or simplicity might prefer another
approach, which might or might not be possible or make sense in other ways.

The same can go for naming conventions (the use of the term Unit
unfortunately has a common meaning in computer stuff as module, thus making
it hard to search on the useage of it).

Looking at the code now I see how there is a command line OS shell call for
every non-trivial use of simplify in Unit:
rval = os.popen(self.__class__.units_call + "'" + self.sanitize(string) +
"'").read().rstrip('\n')

Again, from an Extreme Programming point of view, that is terrific. Why
waste time optimizing something or removing an external dependency when
either the code or the dependency may not be important down the road? From a
code reuse point of view it is great, because you are reusing a big well
designed module -- although from a code reusability point of view it is
problematical because the install dependency and general speed and memory
issues of an external call in a core part of the module makes your code less
reuseable to many possible users. From a simplicity of code declarations
point of view, it is also great -- one line does a lot. From a simplicity
of code interdependencies point of view (or modularity point of view), it is
problematic and may create a platform dependency (so, you need both Python
and GNU units on your platform) or even a license dependency. I'd expect
that GNU Units is under the GPL, potentially creating a licensing dependency
depending on how you read the GPL and what are considered derived works?
But, even if you read things the FSF way, because there is a more general
implementation of units that you could substitute, likely you could argue
the derived works aspect does not apply, even from a FSF point of view:
http://en.wikipedia.org/wiki/Units_%28software%29
But even if it did, if your stuff was under the GPL it would not matter.
Unless of course you wanted to use some other module under a license
incompatible with the GPL and depending on how you read the dependency. And
so it goes, lots of little issues and they depend on priorities and values
(like valuing your time now vs. the time of some hypothetical other user in
the future or even the time of some future self who has to deal with either
an expanded set of options or a reduced set of options). And then there are
more purely aesthetic preferences which can go one way or the other, from,
"a cool hack" to "an ugly special case".

I think the magnitude class look pretty nifty-galifty. :-) Isn't all
programming just living in the Neighborhood of Make Believe? :-)
http://en.wikipedia.org/wiki/Neighborhood_of_Make-Believe
"""
X the Owl - He lives in an old oak tree. He is eager and cheerful and has a
strong desire to learn new things. Many of X's stories involve assignments
from the Owl Correspondence School, and he idolizes Benjamin Franklin. He
briefly works as a salesman for Cornflake S. Pecially, but causes problems
by offering to sell things that the company doesn't make. This is one
example of X's tendencies to be flaky & dense, but like King Friday, he is
quick to apologize when told he is wrong. He also has trouble making
decisions. One of his redeeming qualities, however, is his friendliness. In
one week of episodes, he is the first one to figure out that something is
bothering his neighbor. This character's voice, more than any other heard,
is similar to Fred Rogers's own voice, although X speaks in a Southern drawl
and often uses his catch-phrase of nifty-galifty if excited.
"""

Anyway, I'm learning more about values with units in Python. Thanks again
for the links.

Reply all
Reply to author
Forward
0 new messages