SolidCode/SolidPython

extrude_along_path with scaling crashes: AttributeError: 'numpy.ndarray' object has no attribute 'x'

tpchuckles opened this issue · 6 comments

consider the creation of path, profile, and scaling lists sensibly and quickly via numpy:

thetas=np.linspace(0,np.pi,N) path=list(zip(3*np.sin(thetas),3*np.cos(thetas),thetas)) profile=list(zip(np.sin(thetas),np.cos(thetas))) scalepts=list(np.linspace(1,.1,N))

vs building them element-by-element, without numpy:

path=[] ; profile=[] ; scalepts=[] for i in range(N): t=3.14/N*i path.append(Point3(3*math.sin(t),3*math.cos(t),t)) profile.append(Point3(math.sin(t),math.cos(t),0)) scalepts.append((N-i)/N)

the latter works, while the former yields error: "AttributeError: 'numpy.ndarray' object has no attribute 'x'", which is especially confusing since i carefully converted my numpy arrays into lists! even when faced with the error, the user has no idea what's wrong (printing these lists shows nothing abnormal; python prints floats and numpy floats identically)

I would think the former example (minus the need to cast them into lists) would be preferred stylistically, especially if the latter case were to be broken into 3 for loops depending on the situation. the former will certainly be faster if these lists are large.

you're correct that the first code incorrectly presents tuples rather than euclid3.Point3 objects, but even when that is corrected (for i in range(N): profile[i]=Point3(profile[i][0],profile[i][1],profile[i][2])...etc, or via euclidify (thanks for that, that's awesome)), it still fails.

they key to making it stop failing seemed to be in the building of scalepts list element-by-element. (I originally had posted a much different issue here, and since edited it, since this suggested the root of the issue was the numpy floats within the scalepts list), where the list elements are floats rather than numpy floats.

this code:

`1: path=euclidify(list(zip(3np.sin(thetas),3np.cos(thetas),thetas)))
2: profile=euclidify(list(zip(np.sin(thetas),np.cos(thetas))))
3a: scalepts=list(np.linspace(1,.1,N))

3b: scalepts=list(map(float,np.linspace(1.,.1,N)))
`

using one or the other of 3a/3b, this fails using line 3a, and works using line 3b

edit: as far as making extrude_along_path accept numpy arrays, it should be as "simple" as wrapping the args as follows:
path=euclidify(list(map(list,list(path)))) ; profile=euclidify(list(map(list,list(profile)))) ; scalepts=list(map(float,scalepts))
(to convert a 2D numpy matrix to a 2D pure python list, you first need to cast each element of the outer level (map) and then cast the outer level itself, and then pass this into euclidify (which itself doesn't appear to accept numpy matrices. that might be a better place to make this change though))

Thanks, this is great info. I'll see what I can figure out

Sorry for delay on this (I just had a baby! Little busy this week.) Looks like this is another numpy instance. Numpy has some different multiplication rules defined than standard python. Multiplying a standard python numeric by a euclid3.Vector3 | euclid3.Point3 yields a Vector3. Multiplying a Numpy float64 by that same Vector3 yields a 3-tuple instead, which caused the problem you found.

I'll push a fix for this behavior shortly, and then I'll see if I can add some more general behavior to solid.utils.euclidify that will play more nicely with Numpy.

Pushed and released as V1.0.1

Also worth noting: although you could call euclidify() on all the numpy lines like path=list(zip(3*np.sin(thetas),3*np.cos(thetas),thetas)), you don't have to. Once the issue with scalepts was fixed, this just works.