Export a polyhedron into a *.stl or * .gts file

Asked by Tina Asia

Hi,

I created a polyhedron in YADE using polyhedra_utils, and exported it using export.textPolyhedra. But, the file exported from YADE is a *.txt, Can I convert this *.txt file into a *.stl one?

Thanks in advance,

Tina,
NTU

Question information

Language:
English Edit question
Status:
Solved
For:
Yade Edit question
Assignee:
No assignee Edit question
Solved by:
Jan Stránský
Solved:
Last query:
Last reply:
Revision history for this message
Jan Stránský (honzik) said :
#1

Hi Tina,
you can do it "manually" if you know how to write .stl or .gts format. It
is not (yet) implemented inside Yade.
cheers
Jan

Revision history for this message
Tina Asia (tinaatyade) said :
#2

Hi,

Thanks Jan, I tried some methods to convert *.txt to *.stl, but all failed, Can you give me a help?

Thanks in advance,

Tina

Revision history for this message
Jan Stránský (honzik) said :
#3

Hi Tina,

based on [1] and tested on [2], you can save to stl directly using the
following function. It saves all polyhedrons, if you need just some, it
should be easy to modify it.

############################
def polyhedra2stl(stl): # stl is string file name, e.g. 'test.stl'
  # get triangles
  trisCoords = []
  for b in O.bodies:
    if not isinstance(b.shape,Polyhedra): # skip non-polyhedra bodies
      continue
    vs = [b.state.pos + b.state.ori*v for v in b.shape.v] # vertices in
global coords
    trisIds = b.shape.GetSurfaceTriangulation() # "triangles" in one array
    l = len(trisIds) / 3
    trisIds = [trisIds[3*i:3*i+3] for i in range(l)] # triangles as array
of three indices
    for t in trisIds:
      trisCoords.append([vs[i] for i in t]) # triangles as coords of its
vertices
  # generate content of stl file, see [1]
  lines = ['solid polyhedra']
  for v1,v2,v3 in trisCoords:
    n = (v2-v1).cross(v3-v1)
    lines.append('facet normal {} {} {}'.format(n[0],n[1],n[2]))
    lines.append(' outer loop')
    for v in (v1,v2,v3):
      lines.append(' vertex {} {} {}'.format(v[0],v[1],v[2]))
    lines.append(' endloop')
    lines.append('endfacet')
  lines.append('endsolid polyhedra')
  # write to file
  with open(stl,'w') as f:
    f.writelines(l+'\n' for l in lines)
############################

cheers
Jan

[1] https://en.wikipedia.org/wiki/STL_(file_format)
[2] http://www.viewstl.com/

Revision history for this message
Tina Asia (tinaatyade) said :
#4

Hi,

Jan, I have tried your code, but the *.stl only contains some triangles, My code is as follows (My YADE is the latest vision):

from yade import polyhedra_utils
from yade import export
from yade import plot
from yade import qt

gravel = PolyhedraMat()
gravel.IsSplitable = True
gravel.strength = 7.9e6
gravel.density = 2678#kg/m^3
gravel.young = 5.98e7 #Pa
gravel.poisson =0.3
gravel.frictionAngle = 0.5 #rad

#create a polyhedron
poly=polyhedra_utils.polyhedra(material=gravel,size=(0.05,0.05,0.05),seed=1)
O.bodies.append(poly)
poly.state.pos=(0,0,0.025)

def polyhedra2stl(stl): # stl is string file name, e.g. 'test.stl'
    # get triangles
    trisCoords = []
    for b in O.bodies:
        if not isinstance(b.shape,Polyhedra): # skip non-polyhedra bodies
            continue
        vs = [b.state.pos + b.state.ori*v for v in b.shape.v] # vertices in global coords
        trisIds = b.shape.GetSurfaceTriangulation() # "triangles" in one array
        l = len(trisIds) / 3
        trisIds = [trisIds[3*i:3*i+3] for i in range(l)] # triangles as array of three indices
        for t in trisIds:
            trisCoords.append([vs[i] for i in t]) # triangles as coords of its vertices
    # generate content of stl file, see [1]
    lines = ['solid polyhedra']
    for v1,v2,v3 in trisCoords:
        n = (v2-v1).cross(v3-v1)
        lines.append('facet normal {} {} {}'.format(n[0],n[1],n[2]))
        lines.append(' outer loop')
        for v in (v1,v2,v3):
            lines.append(' vertex {} {} {}'.format(v[0],v[1],v[2]))
            lines.append(' endloop')
            lines.append('endfacet')
            lines.append('endsolid polyhedra')
    # write to file
    with open(stl,'w') as f:
        f.writelines(l+'\n' for l in lines)

polyhedra2stl('tina.stl')

Revision history for this message
Jan Stránský (honzik) said :
#5

Hi Tina,
I have tried your code and did not face any problem..
please be more specific on "the *.stl only contains some triangles".
The code is sopposed to save surface of the polyhedron as a set of
triangles. Are some triangles missing? Or did you expect some other output?
I don't use stl myself, but according to [1], "In practice, however, all
facets are simple triangles.". Probably it would be possible to save each
face as a polygon, not only triangles..
cheers
Jan

Revision history for this message
Tina Asia (tinaatyade) said :
#6

Thanks Jan,

My goal is to fill this polyhedron with some sub-particles, export *.stl may be an unwise decision. Is there any better method to finish this task??

Tina,
NTU

Revision history for this message
Jan Stránský (honzik) said :
#7

Hi Tina,
it pretty much depends on what program/method you use for filling. In my
opinion, both gts and stl are equally good, since they are relatively
easily convertible and saving basically the same information.
cheers
Jan

Revision history for this message
Tina Asia (tinaatyade) said :
#8

Hi Jan,

Thanks for your patience, but I got an error by importing the *.stl file generated by the above code. The error and my new code are as follows.

from yade import ymport
from yade import pack
from yade import qt

pred=O.bodies.append(ymport.stl('tina.stl'))
spheres=pack.randomDensePack(pred,radius=0.001,rRelFuzz=0.3,spheresInCell=500,color=(1,0,1))
O.bodies.append(spheres)

qt.View()
qt.Controller()

I got this error:

$ yade fillsphere.pyWelcome to Yade 2017.01a
TCP python prompt on localhost:9000, auth cookie `sdcaek'
XMLRPC info provider on http://localhost:21000
Running script fillsphere.py
ERROR /home/tina/YADE/trunk/pkg/dem/STLImporter.cpp:23 import: Can't open file: tina.stl
Traceback (most recent call last):
  File "/home/tina/YADE/install/bin/yade", line 182, in runScript
    execfile(script,globals())
  File "fillsphere.py", line 6, in <module>
    spheres=pack.randomDensePack(pred,radius=0.001,rRelFuzz=0.3,spheresInCell=500,color=(1,0,1))
  File "/home/tina/YADE/install/lib/x86_64-linux-gnu/yade/py/yade/pack.py", line 490, in randomDensePack
    if not dim: dim=predicate.dim()
AttributeError: 'list' object has no attribute 'dim'
[[ ^L clears screen, ^U kills line. F12 controller, F11 3d view (use h-key for showing help), F10 both, F9 generator, F8 plot. ]]

Besides, I also tried pack.inConvexPolyhedron (https://www.yade-dem.org/doc/yade.pack.html?highlight=pack.in#yade.pack.inConvexPolyhedron) to fill a polyhedron with many sub-particles, but this one got error too:

This is my code:

from yade import polyhedra_utils

rock = PolyhedraMat()
rock.IsSplitable = True
rock.strength = 6e3

poly = polyhedra_utils.polyhedra(rock, size=(0.01,0.02,0.01), seed=1)
O.bodies.append(poly)

pred=pack.inConvexPolyhedron(poly)
aabb=pred.aabb()
dim0=aabb[1][0]-aabb[0][0]
spheres=O.bodies.append(pack.randomDensePack(pred,radius=0.001,rRelFuzz=0.3,spheresInCell=300))
O.bodies.append(spheres)

from yade import qt
qt.View()
qt.Controller()

and this is the error:

~/Desktop/polyhedra$ yade poly.py
Welcome to Yade 2017.01a
TCP python prompt on localhost:9000, auth cookie `yudssc'
XMLRPC info provider on http://localhost:21000
Running script poly.py
Traceback (most recent call last):
  File "/home/tina/YADE/install/bin/yade", line 182, in runScript
    execfile(script,globals())
  File "poly.py", line 10, in <module>
    pred=pack.inConvexPolyhedron(poly)
  File "/home/tina/YADE/install/lib/x86_64-linux-gnu/yade/py/yade/pack.py", line 208, in __init__
    self._inHalfSpaces = [inHalfSpace(c,d) for c,d in planes]
TypeError: 'Body' object is not iterable
[[ ^L clears screen, ^U kills line. F12 controller, F11 3d view (use h-key for showing help), F10 both, F9 generator, F8 plot. ]]

Revision history for this message
Jan Stránský (honzik) said :
#9

Hi Tina,
thanks for clarification :-) you have several options here, using existing
answers of this question:
(ymport.stl returns list of facets, it cannot be used as a predicate for
randomDensePack. For this case, you have to use pack.inGtsSurface)
###############
import gts
facets = ymport.stl('tina.stl')
surf = gts.Surface()
for f in facets:
  vs = [f.state.pos + f.state.ori*v for v in f.shape.vertices]
  gtsvs = [gts.Vertex(v[0],v[1],v[2]) for v in vs]
  es = [gts.Edge(gtsvs[i],gtsvs[j]) for i,j in ((0,1),(1,2),(2,0))]
  face = gts.Face(es[0],es[1],es[2])
  surf.add(face)
surf.cleanup(1e-6)
assert surf.is_closed()
pred = pack.inGtsSurface(surf)
sphs = pack.randomDensePack(pred,radius=0.001,spheresInCell=500)
O.bodies.append(sphs)
###############

cheers
Jan

Revision history for this message
Tina Asia (tinaatyade) said :
#10

Jan,

Thanks for your patience, but the error---"Can't open file: tina.st" still exists. I guess there is something wrong with the code---"poly2stl" ,although the *.stl can be displayed in Meshlab and looks identical with polyhedron generated in Yade.
Besides, I converted *.stl to *.gts using stl2gts, and using the following code to fill this polyhedron, but it got a cuboid package.
I replaced this *.gts with horse.coarse.gts, and this one got correct result. Thus, I guess the poly2stl outputs something wrong, please check this code---poly2stl.

Many thanks.

My code was as following:

from yade import ymport
from yade import pack
surface=gts.read(open('poly.gts'))
pred=pack.inGtsSurface(surface)
assembly=pack.randomDensePack(pred,radius=0.001,rRelFuzz=0.3,spheresInCell=500)
O.bodies.append(assembly)

Revision history for this message
Best Jan Stránský (honzik) said :
#11

Hi Tina,

> "Can't open file: tina.st"

it means that the file cannot be opened :-) could you check if it exists? I put here a complete script, that works for me:

#################################################
from yade import polyhedra_utils
from yade import export, ymport
from yade import plot
from yade import pack
from yade import qt

gravel = PolyhedraMat()
gravel.IsSplitable = True
gravel.strength = 7.9e6
gravel.density = 2678#kg/m^3
gravel.young = 5.98e7 #Pa
gravel.poisson =0.3
gravel.frictionAngle = 0.5 #rad

#create a polyhedron
poly=polyhedra_utils.polyhedra(material=gravel,size=(0.05,0.05,0.05),seed=1)
poly.shape.wire = True
O.bodies.append(poly)
poly.state.pos=(0,0,0.025)

def polyhedra2stl(stl):
  trisCoords = []
  for b in O.bodies:
    if not isinstance(b.shape,Polyhedra):
      continue
    vs = [b.state.pos + b.state.ori*v for v in b.shape.v]
    trisIds = b.shape.GetSurfaceTriangulation()
    l = len(trisIds) / 3
    trisIds = [trisIds[3*i:3*i+3] for i in range(l)]
    for t in trisIds:
      trisCoords.append([vs[i] for i in t])
  #
  lines = ['solid polyhedra']
  for v1,v2,v3 in trisCoords:
    n = (v2-v1).cross(v3-v1)
    lines.append('facet normal {} {} {}'.format(n[0],n[1],n[2]))
    lines.append(' outer loop')
    for v in (v1,v2,v3):
      lines.append(' vertex {} {} {}'.format(v[0],v[1],v[2]))
    lines.append(' endloop')
    lines.append('endfacet')
  lines.append('endsolid polyhedra')
  #
  with open(stl,'w') as f:
    f.writelines(l+'\n' for l in lines)

polyhedra2stl('tina.stl')

facets = ymport.stl('tina.stl')
surf = gts.Surface()
for f in facets:
  vs = [f.state.pos + f.state.ori*v for v in f.shape.vertices]
  gtsvs = [gts.Vertex(v[0],v[1],v[2]) for v in vs]
  es = [gts.Edge(gtsvs[i],gtsvs[j]) for i,j in ((0,1),(1,2),(2,0))]
  face = gts.Face(es[0],es[1],es[2])
  surf.add(face)
surf.cleanup(1e-6)
assert surf.is_closed()
pred = pack.inGtsSurface(surf)
sphs = pack.randomDensePack(pred,radius=0.001,spheresInCell=500)
O.bodies.append(sphs)
#################################################

cheers
Jan

Revision history for this message
Tina Asia (tinaatyade) said :
#12

Thanks Jan,

I just ran this code in my yade2017.01a, but it still failed. I guess something wrong happens when I copy the code, please send this code in .py format to my E-mail:<email address hidden>

Thanks for your patience,
Best regards.

Tina

Revision history for this message
Tina Asia (tinaatyade) said :
#13

Thanks Jan Stránský, that solved my question.

Revision history for this message
Jan Stránský (honzik) said :
#14

Hi Tina,

> but it still failed

please always attach the error message (or be more specific why it does not work). This information alone has no value for the problem solution.

The script sent to the email

cheers
Jan

Revision history for this message
Tina Asia (tinaatyade) said :
#15

Hi Jan,

I have tried this code in Yade 1.20, and It gets a correct result. Anyway, thank you very much. The latest version (2017.01a) may contain a bug.

Best regards,

Tina

Revision history for this message
Jan Stránský (honzik) said :
#16

Hi Tina,

> latest version (2017.01a) may contain a bug

please send here (or open a bug issue) what error you get
thanks
Jan

Revision history for this message
De zhang (dzlearnyade) said :
#17

Hi Jan,
I have tried the code you provided above in version(2018.02b). It also came out the error message as following:
###
ERROR /data/trunk/pkg/dem/STLImporter.cpp:23 import: Can't open file: tina.stl

** (python:7975): CRITICAL **: pygts_vertices_merge: assertion 'vertices != NULL' failed

(python:7975): Gts-CRITICAL **: gts_bb_tree_new: assertion 'bboxes != NULL' failed
Traceback (most recent call last):
  File "/usr/bin/yadedaily", line 182, in runScript
    execfile(script,globals())
  File "stl2sphere.py", line 59, in <module>
    pred = pack.inGtsSurface(surf)
RuntimeError: Could not create GTree
###
I also tried the other stl file which produced by scanning software, the same problem happened with "Can't open file: xxx.stl"

Revision history for this message
Bruno Chareyre (bruno-chareyre) said :
#18

Hi, Please don't re-open an old question.
http://yade-dem.org/wiki/Howtoask

Revision history for this message
Jan Stránský (honzik) said :
#19

Also see [1], might be the same problem
Jan

[1] https://answers.launchpad.net/yade/+question/537735

Revision history for this message
De zhang (dzlearnyade) said :
#20

Thanks Jan,
I have tried the procedure in stl-gts folder in trunk-2018.02b and I made it by following conversion:

STL->GTS->Yade

to convert format by using "stl2gts -r <xx.stl> xx.gts"

after that, the new version of Yadedaily can open the xx.gts

#it requires libgts-bin installed
# © 2009 Václav Šmilauer <email address hidden>
# © 2013 Anton Gladky <email address hidden>

That may be a solution to solve the problem. Thanks Jan~

Dez