Exemple #1
0
    def fill(self):
        """Fill the surface inside the polygon with triangles.

        Returns a TriSurface filling the surface inside the polygon.
        """
        print("AREA(self) %s" % self.area())
        # creating elems array at once (more efficient than appending)
        from gui.draw import draw, pause, undraw
        from geomtools import insideTriangle
        x = self.coords
        n = x.shape[0]
        tri = -ones((n - 2, 3), dtype=Int)
        # compute all internal angles
        e = arange(x.shape[0])
        c = self.internalAngles()
        # loop in order of smallest angles
        itri = 0
        while n > 3:
            #print("ANGLES",c)
            # try minimal angle
            srt = c.argsort()
            for j in srt:
                #print("ANGLE: %s" % c[j])
                if c[j] > 180.:
                    print(
                        "OOPS, I GOT STUCK!\nMaybe the curve is self-intersecting?"
                    )
                    #print("Remaining points: %s" % e)
                    #raise
                    #
                    # We could return here also the remaining part
                    #
                    return TriSurface(x, tri[:itri])
                i = (j - 1) % n
                k = (j + 1) % n
                newtri = [e[i], e[j], e[k]]
                # remove the point j of triangle i,j,k
                # recompute adjacent angles of edge i,k
                ii = (i - 1) % n
                kk = (k + 1) % n
                iq = e[[ii, i, k, kk]]
                PQ = Polygon(x[iq])
                cn = PQ.internalAngles()
                cnew = cn[1:3]
                reme = roll(e, -j)[2:-1]
                T = x[newtri].reshape(1, 3, 3)
                P = x[reme].reshape(-1, 1, 3)
                check = insideTriangle(T, P)
                if not check.any():
                    # Triangle is ok
                    break
            #draw(TriSurface(x,newtri),bbox='last',color='red')
            # accept new triangle
            tri[itri] = newtri
            c = roll(concatenate([cnew, roll(c, 1 - j)[3:]]), j - 1)
            e = roll(roll(e, -j)[1:], j)
            n -= 1
            itri += 1
        tri[itri] = e
        return TriSurface(x, tri)
Exemple #2
0
def run():
    reset()
    smooth()
    lights(True)

    S = TriSurface.read(getcfg('datadir')+'/horse.off')
    SA = draw(S)

    res = askItems([
        ('direction',[1.,0.,0.]),
        ('number of sections',20),
        ('color','red'),
        ('ontop',False),
        ('remove surface',False),
        ]) 
    if not res:
        return

    d = res['direction']
    n = res['number of sections']
    c = res['color']

    slices = S.slice(dir=d,nplanes=n)
    linewidth(2)
    draw(slices,color=c,view=None,bbox='last',nolight=True,ontop=res['ontop'])
    export({'_HorseSlice_slices':slices})
    
    if res['remove surface']:
        undraw(SA)
        
    zoomAll()
Exemple #3
0
def remesh(self, edgelen=None):
    """Remesh a TriSurface.

    edgelen is the suggested edge length
    """
    if edgelen is None:
        self.getElemEdges()
        E = Mesh(self.coords, self.edges, eltype='line2')
        edgelen = E.lengths().mean()
    tmp = utils.tempFile(suffix='.stl').name
    tmp1 = utils.tempFile(suffix='.stl').name
    pf.message("Writing temp file %s" % tmp)
    self.write(tmp, 'stl')
    pf.message("Remeshing using VMTK (edge length = %s)" % edgelen)
    cmd = "vmtk vmtksurfaceremeshing -ifile %s -ofile %s -edgelength %s" % (
        tmp, tmp1, edgelen)
    sta, out = utils.runCommand(cmd)
    os.remove(tmp)
    if sta:
        pf.message("An error occurred during the remeshing.")
        pf.message(out)
        return None
    S = TriSurface.read(tmp1)
    os.remove(tmp1)
    return S
Exemple #4
0
def run():
    global F, G
    clear()
    smooth()
    view('iso')
    F = cylinder(L=8., D=2., nt=36, nl=20, diag='u').centered()
    F = TriSurface(F).setProp(3).close(method='planar').fixNormals()
    G = F.rotate(90., 0).trl(0, 1.).setProp(1)
    export({'F': F, 'G': G})
    draw([F, G])

    res = askItems([
        _I('op',
           text='Operation',
           choices=[
               '+ (Union)',
               '- (Difference)',
               '* Intersection',
               'Intersection Curve',
           ],
           itemtype='vradio'),
        _I('verbose', False, text='Show stats'),
    ])

    if not res:
        return
    op = res['op'][0]
    verbose = res['verbose']
    if op in '+-*':
        I = F.boolean(G, op, verbose=verbose)
    else:
        I = F.intersection(G, verbose=verbose)
    clear()
    draw(I)

    if op in '+-*':
        return

    else:
        if ack('Create a surface inside the curve ?'):
            I = I.toMesh()
            e = connectedLineElems(I.elems)
            I = Mesh(I.coords, connectedLineElems(I.elems)[0])
            clear()
            draw(I, color=red, linewidth=3)
            S = fillBorder(I, method='planar')
            draw(S)
Exemple #5
0
def delaunay(X):
    """Return a Delaunay triangulation of the specified Coords.

    While the Coords are 3d, only the first 2 components are used.

    Returns a TriSurface with the Delaunay trinagulation in the x-y plane.
    """
    from voronoi import voronoi
    return TriSurface(X, voronoi(X[:, :2]).triangles)
Exemple #6
0
def run():
    global F,G
    clear()
    smooth()
    view('iso')
    F = cylinder(L=8.,D=2.,nt=36,nl=20,diag='u').centered()
    F = TriSurface(F).setProp(3).close(method='planar').fixNormals()
    G = F.rotate(90.,0).trl(0,1.).setProp(1)
    export({'F':F,'G':G})
    draw([F,G])

    res = askItems(
        [ _I('op',text='Operation',choices=[
            '+ (Union)',
            '- (Difference)',
            '* Intersection',
            'Intersection Curve',
            ],itemtype='vradio'),
          _I('verbose',False,text='Show stats'),
        ])
    
    if not res:
        return
    op = res['op'][0]
    verbose = res['verbose']
    if op in '+-*':
        I = F.boolean(G,op,verbose=verbose)
    else:
        I = F.intersection(G,verbose=verbose)
    clear()
    draw(I)

    if op in '+-*':
        return

    else:
        if ack('Create a surface inside the curve ?'):
            I = I.toMesh()
            e = connectedLineElems(I.elems)
            I = Mesh(I.coords,connectedLineElems(I.elems)[0])
            clear()
            draw(I,color=red,linewidth=3)
            S = fillBorder(I,method='planar')
            draw(S)
Exemple #7
0
def smallestDirection(x,method='inertia',return_size=False):
    """Return the direction of the smallest dimension of a Coords

    - `x`: a Coords-like array
    - `method`: one of 'inertia' or 'random'
    - return_size: if True and `method` is 'inertia', a tuple of a direction
      vector and the size  along that direction and the cross directions;
      else, only return the direction vector.
    """
    x = x.reshape(-1,3)
    if method == 'inertia':
        # The idea is to take the smallest dimension in a coordinate
        # system aligned with the global axes.
        C,r,Ip,I = x.inertia()
        X = x.trl(-C).rot(r)
        sizes = X.sizes()
        i = sizes.argmin()
        # r gives the directions as column vectors!
        # TODO: maybe we should change that
        N = r[:,i]
        if return_size:
            return N,sizes[i]
        else:
            return N
    elif method == 'random':
        # Take the mean of the normals on randomly created triangles
        from plugins.trisurface import TriSurface
        n = x.shape[0]
        m = 3 * (n // 3)
        e = arange(m)
        random.shuffle(e)
        if n > m:
            e = concatenate([e,[0,1,n-1]])
        el = e[-3:]
        S = TriSurface(x,e.reshape(-1,3))
        A,N = S.areaNormals()
        ok = where(isnan(N).sum(axis=1) == 0)[0]
        N = N[ok]
        N = N*N
        N = N.mean(axis=0)
        N = sqrt(N)
        N = normalize(N)
        return N
Exemple #8
0
def run():
    reset()
    smooth()
    lights(True)

    S = TriSurface.read(getcfg('datadir') + '/horse.off')
    SA = draw(S)

    bb = S.bbox()
    bb1 = [1.1 * bb[0] - 0.1 * bb[1], 1.1 * bb[1] - 0.1 * bb[0]]
    print(bb)
    print(bb1)

    res = askItems([
        _I('Resolution', 100),
    ])
    if not res:
        return

    nmax = res['Resolution']
    sz = bb1[1] - bb1[0]
    step = sz.max() / (nmax - 1)
    n = (sz / step).astype(Int)
    print(n)
    P = Formex(simple.regularGrid(bb1[0], bb1[0] + n * step, n).reshape(-1, 3))
    draw(P, marksize=1, color='black')
    #drawNumbers(P)
    zoomAll()
    ind = S.inside(P)
    vox = zeros(n + 1, dtype=uint8)
    print(vox.shape)
    vox1 = vox.reshape(-1)
    print(vox1.shape, ind.max())
    vox1[ind] = 1
    print(vox.max())
    P.setProp(vox1)
    draw(P, marksize=8)

    dirname = askDirname()
    chdir(dirname)
    # Create output file
    if not checkWorkdir():
        print("Could not open a directory for writing. I have to stop here")
        return

    fs = utils.NameSequence('horse', '.png')
    clear()
    flat()
    A = None
    for frame in vox:
        B = showGreyImage(frame)
        saveBinaryImage(frame * 255, fs.next())
        undraw(A)
        A = B
Exemple #9
0
def run():
    reset()
    smooth()
    lights(True)

    S = TriSurface.read(getcfg('datadir')+'/horse.off')
    SA = draw(S)

    bb = S.bbox()
    bb1 = [ 1.1*bb[0]-0.1*bb[1], 1.1*bb[1]-0.1*bb[0]]
    print(bb)
    print(bb1)

    res = askItems([
        _I('Resolution',100),
        ])
    if not res:
        return
    
    nmax = res['Resolution']
    sz = bb1[1]-bb1[0]
    step = sz.max() / (nmax-1)
    n = (sz / step).astype(Int)
    print(n)
    P = Formex(simple.regularGrid(bb1[0],bb1[0]+n*step,n).reshape(-1,3))
    draw(P, marksize=1, color='black')
    #drawNumbers(P)
    zoomAll()
    ind = S.inside(P)
    vox = zeros(n+1,dtype=uint8)
    print(vox.shape)
    vox1 = vox.reshape(-1)
    print(vox1.shape,ind.max())
    vox1[ind] = 1
    print(vox.max())
    P.setProp(vox1)
    draw(P, marksize=8)

    dirname = askDirname()
    chdir(dirname)
    # Create output file
    if not checkWorkdir():
        print("Could not open a directory for writing. I have to stop here")
        return
    
    fs = utils.NameSequence('horse','.png')
    clear()
    flat()
    A = None
    for frame in vox:
        B = showGreyImage(frame)
        saveBinaryImage(frame*255,fs.next())
        undraw(A)
        A = B
Exemple #10
0
def sphere(ndiv=6):
    """Create a triangulated spherical surface.

    A high quality approximation of a spherical surface is constructed as
    follows. First an icosahedron is constructed. Its triangular facets are
    subdivided by dividing all edges in `ndiv` parts. The resulting mesh is
    then projected on a sphere with unit radius. The higher `ndiv` is taken,
    the better the approximation. `ndiv=1` results in an icosahedron.

    Returns: 

      A TriSurface, representing a triangulated approximation of a
      spherical surface with radius 1 and center at the origin.
    """
    from elements import Icosa
    from plugins.trisurface import TriSurface

    M = TriSurface(Icosa.vertices,Icosa.faces)
    M = M.subdivide(ndiv).fuse()
    M = M.projectOnSphere()
    return M
Exemple #11
0
def run():
    reset()
    smooth()
    lights(True)
    transparent(False)
    setView('horse', [20, 20, 0])
    S = TriSurface.read(getcfg('datadir') + '/horse.off')
    bb = S.bbox()

    t = -0.3
    bb[0] = (1.0 - t) * bb[0] + t * bb[1]
    draw(S, bbox=bb, view='front')

    try:
        P, n, t = askSlices(S.bbox())
    except:
        return

    a = t / len(P)

    F = S.toFormex()
    G = []
    old = seterr(all='ignore')
    setDrawOptions({'bbox': None})

    clear()
    A = None
    for i, p in enumerate(P):
        F1, F = F.cutWithPlane(p, -n)
        if F1.nelems() > 0:
            F1.setProp(i)
        G = [g.rot(a, around=p) for g in G]
        G.append(F1)
        #clear()
        B = draw([F, G])
        if A:
            undraw(A)
        A = B

    seterr(**old)

    x = pf.canvas.width() / 2
    y = pf.canvas.height() - 40
    T = drawText("No animals got hurt during the making of this movie!",
                 x,
                 y,
                 size=18,
                 gravity='C')
    for i in range(10):
        sleep(0.3)
        undecorate(T)
        sleep(0.3)
        decorate(T)
Exemple #12
0
def run():
    reset()
    smooth()
    lights(True)
    transparent(False)
    setView('horse',[20,20,0])
    S = TriSurface.read(getcfg('datadir')+'/horse.off')
    bb = S.bbox()

    t = -0.3
    bb[0] = (1.0-t)*bb[0] + t*bb[1]
    draw(S,bbox=bb,view='front')

    try:
        P,n,t = askSlices(S.bbox())
    except:
        return

    a = t/len(P)

    F = S.toFormex()
    G = []
    old = seterr(all='ignore')
    setDrawOptions({'bbox':None})

    clear()
    A = None
    for i,p in enumerate(P):
        F1,F = F.cutWithPlane(p,-n)
        if F1.nelems() > 0:
            F1.setProp(i)
        G = [ g.rot(a,around=p) for g in G ] 
        G.append(F1)
        #clear()
        B = draw([F,G])
        if A:
            undraw(A)
        A = B
            
    seterr(**old)

    x = pf.canvas.width()/2
    y = pf.canvas.height() - 40
    T = drawText("No animals got hurt during the making of this movie!",x,y,size=18,gravity='C')
    for i in range(10):
        sleep(0.3)
        undecorate(T)
        sleep(0.3)
        decorate(T)
Exemple #13
0
def gtsset(self,
           surf,
           op,
           filt='',
           ext='.tmp',
           curve=False,
           check=False,
           verbose=False):
    """_Perform the boolean/intersection methods.

    See the boolean/intersection methods for more info.
    Parameters not explained there:

    - filt: a filter command to be executed on the gtsset output
    - ext: extension of the result file
    - curve: if True, an intersection curve is computed, else the surface.

    Returns the name of the (temporary) results file.
    """
    op = {'+': 'union', '-': 'diff', '*': 'inter'}[op]
    options = ''
    if curve:
        options += '-i'
    if check:
        options += ' -s'
    if not verbose:
        options += ' -v'
    tmp = utils.tempFile(suffix='.gts').name
    tmp1 = utils.tempFile(suffix='.gts').name
    tmp2 = utils.tempFile(suffix=ext).name
    pf.message("Writing temp file %s" % tmp)
    self.write(tmp, 'gts')
    pf.message("Writing temp file %s" % tmp1)
    surf.write(tmp1, 'gts')
    pf.message("Performing boolean operation")
    cmd = "gtsset %s %s %s %s %s > %s" % (options, op, tmp, tmp1, filt, tmp2)
    sta, out = utils.runCommand(cmd)
    os.remove(tmp)
    os.remove(tmp1)
    if sta or verbose:
        pf.message(out)
    pf.message("Reading result from %s" % tmp2)
    if curve:
        res = read_gts_intersectioncurve(tmp2)
    else:
        res = TriSurface.read(tmp2)
    os.remove(tmp2)
    return res
Exemple #14
0
def gtsset(self,surf,op,filt='',ext='.tmp',curve=False,check=False,verbose=False):
    """_Perform the boolean/intersection methods.

    See the boolean/intersection methods for more info.
    Parameters not explained there:

    - filt: a filter command to be executed on the gtsset output
    - ext: extension of the result file
    - curve: if True, an intersection curve is computed, else the surface.

    Returns the name of the (temporary) results file.
    """
    op = {'+':'union', '-':'diff', '*':'inter'}[op]
    options = ''
    if curve:
        options += '-i'
    if check:
        options += ' -s'
    if not verbose:
        options += ' -v'
    tmp = utils.tempFile(suffix='.gts').name
    tmp1 = utils.tempFile(suffix='.gts').name
    tmp2 = utils.tempFile(suffix=ext).name
    pf.message("Writing temp file %s" % tmp)
    self.write(tmp,'gts')
    pf.message("Writing temp file %s" % tmp1)
    surf.write(tmp1,'gts')
    pf.message("Performing boolean operation")
    cmd = "gtsset %s %s %s %s %s > %s" % (options,op,tmp,tmp1,filt,tmp2)
    sta,out = utils.runCommand(cmd)
    os.remove(tmp)
    os.remove(tmp1)
    if sta or verbose:
        pf.message(out)
    pf.message("Reading result from %s" % tmp2)
    if curve:
        res = read_gts_intersectioncurve(tmp2)
    else:
        res = TriSurface.read(tmp2)
    os.remove(tmp2)
    return res
Exemple #15
0
def run():
    reset()
    smooth()
    lights(True)

    S = TriSurface.read(getcfg('datadir') + '/horse.off')
    SA = draw(S)

    res = askItems([
        ('direction', [1., 0., 0.]),
        ('number of sections', 20),
        ('color', 'red'),
        ('ontop', False),
        ('remove surface', False),
    ])
    if not res:
        return

    d = res['direction']
    n = res['number of sections']
    c = res['color']

    slices = S.slice(dir=d, nplanes=n)
    linewidth(2)
    draw(slices,
         color=c,
         view=None,
         bbox='last',
         nolight=True,
         ontop=res['ontop'])
    export({'_HorseSlice_slices': slices})

    if res['remove surface']:
        undraw(SA)

    zoomAll()
Exemple #16
0
def remesh(self,edgelen=None):
    """Remesh a TriSurface.

    edgelen is the suggested edge length
    """
    if edgelen is None:
       self.getElemEdges()
       E = Mesh(self.coords,self.edges,eltype='line2')
       edgelen =  E.lengths().mean()
    tmp = utils.tempFile(suffix='.stl').name
    tmp1 = utils.tempFile(suffix='.stl').name
    pf.message("Writing temp file %s" % tmp)
    self.write(tmp,'stl')
    pf.message("Remeshing using VMTK (edge length = %s)" % edgelen)
    cmd = "vmtk vmtksurfaceremeshing -ifile %s -ofile %s -edgelength %s" % (tmp,tmp1,edgelen)
    sta,out = utils.runCommand(cmd)
    os.remove(tmp)
    if sta:
        pf.message("An error occurred during the remeshing.")
        pf.message(out)
        return None
    S = TriSurface.read(tmp1)
    os.remove(tmp1)
    return S
        totalrot = res['total rot']
        xmin,xmax = bb[:,axis]
        dx =  (xmax-xmin) / nslices
        x = arange(nslices+1) * dx
        N = unitVector(axis)
        P = [ bb[0]+N*s for s in x ]
        return P,N,totalrot
    else:
        return None

reset()
smooth()
lights(True)
transparent(False)
setView('horse',[20,20,0])
S = TriSurface.read(getcfg('datadir')+'/horse.off')
bb = S.bbox()

t = -0.3
bb[0] = (1.0-t)*bb[0] + t*bb[1]
draw(S,bbox=bb,view='front')

try:
    P,n,t = askSlices(S.bbox())
except:
    exit()

a = t/len(P)

F = S.toFormex()
G = []
Exemple #18
0
def drawSurf(F, surface=False, **kargs):
    """Draw a Formex as surface or not."""
    if surface:
        F = TriSurface(F)
    return draw(F, **kargs)
        sw=sp[w]
        return px[sw], w, itx[sw], delete( arange(len(segm)),  w)
    else:return px, ilx, itx


clear()
smooth()
lights(True)
transparent(False)
view('iso')

image = None
scaled_image = None

# read the teapot surface
T = TriSurface.read(getcfg('datadir')+'/teapot.off')
xmin,xmax = T.bbox()
T= T.trl(-T.center()).scale(4./(xmax[0]-xmin[0])).setProp(2)
draw(T)

# default image file
filename = getcfg('datadir')+'/benedict_6.jpg'
viewer = ImageView(filename)

px,py = 5,5 #control points for projection of patches
kx,ky = 60,50 #number of cells in each patch
scale = 0.8
method='projection'

res = askItems([
    I('filename',filename,text='Image file',itemtype='button',func=selectImage),
Exemple #20
0
def run():
    clear()
    linewidth(2)
    flatwire()
    setDrawOptions({'bbox': 'auto'})

    # A tapered grid of points
    F = Formex([0.]).replic2(10, 5, taper=-1)
    draw(F)

    # Split in parts by testing y-position; note use of implicit loop!
    G = [F.clip(F.test(dir=1, min=i - 0.5, max=i + 0.5)) for i in range(5)]
    print([Gi.nelems() for Gi in G])

    def annot(char):
        [
            drawText3D(G[i][0, 0] + [-0.5, 0., 0.], "%s%s" % (char, i))
            for i, Gi in enumerate(G)
        ]

    # Apply a general mapping function : x,y,x -> [ newx, newy, newz ]
    G = [
        Gi.map(lambda x, y, z: [x, y + 0.01 * float(i + 1)**1.5 * x**2, z])
        for i, Gi in enumerate(G)
    ]
    clear()
    draw(G)
    annot('G')

    setDrawOptions({'bbox': 'last'})

    # Connect G0 with G1
    H1 = connect([G[0], G[1]])
    draw(H1, color=blue)

    # Connect G1 with G2 with a 2-element bias
    H2 = connect([G[1], G[2]], bias=[0, 2])
    draw(H2, color=green)

    # Connect G3 with G4 with a 1-element bias plus loop
    H2 = connect([G[3], G[4]], bias=[1, 0], loop=True)
    draw(H2, color=red)

    # Create a triangular grid of bars
    clear()
    draw(G)
    annot('G')
    # Connect Gi[j] with Gi[j+1] to create horizontals
    K1 = [connect([i, i], bias=[0, 1]) for i in G]
    draw(K1, color=blue)

    # Connect Gi[j] with Gi+1[j] to create verticals
    K2 = [connect([i, j]) for i, j in zip(G[:-1], G[1:])]
    draw(K2, color=red)

    # Connect Gi[j+1] with Gi+1[j] to create diagonals
    K3 = [connect([i, j], bias=[1, 0]) for i, j in zip(G[:-1], G[1:])]
    draw(K3, color=green)

    # Create triangles
    clear()
    draw(G)
    annot('G')

    L1 = [connect([i, i, j], bias=[0, 1, 0]) for i, j in zip(G[:-1], G[1:])]
    draw(L1, color=red)
    L2 = [connect([i, j, j], bias=[1, 0, 1]) for i, j in zip(G[:-1], G[1:])]
    draw(L2, color=green)

    # Connecting multiplex Formices using bias
    clear()
    annot('K')
    draw(K1)
    L1 = [connect([i, i, j], bias=[0, 1, 0]) for i, j in zip(K1[:-1], K1[1:])]
    draw(L1, color=red)
    L2 = [connect([i, j, j], bias=[1, 0, 1]) for i, j in zip(K1[:-1], K1[1:])]
    draw(L2, color=green)

    # Connecting multiplex Formices using nodid
    clear()
    draw(K1)
    annot('K')
    L1 = [connect([i, i, j], nodid=[0, 1, 0]) for i, j in zip(K1[:-1], K1[1:])]
    draw(L1, color=red)
    L2 = [connect([i, j, j], nodid=[1, 0, 1]) for i, j in zip(K1[:-1], K1[1:])]
    draw(L2, color=green)

    # Add the missing end triangles
    L3 = [
        connect([i, i, j],
                nodid=[0, 1, 1],
                bias=[i.nelems() - 1,
                      i.nelems() - 1,
                      j.nelems() - 1]) for i, j in zip(K1[:-1], K1[1:])
    ]
    draw(L3, color=magenta)

    # Collect all triangles in a single Formex
    L = (Formex.concatenate(L1) +
         Formex.concatenate(L3)).setProp(1) + Formex.concatenate(L2).setProp(2)
    clear()
    draw(L)

    # Convert to a Mesh
    print("nelems = %s, nplex = %s, coords = %s" %
          (L.nelems(), L.nplex(), L.coords.shape))
    M = L.toMesh()
    print("nelems = %s, nplex = %s, coords = %s" %
          (M.nelems(), M.nplex(), M.coords.shape))
    clear()
    draw(M, color=yellow, mode='flatwire')
    drawNumbers(M)
    draw(M.getBorderMesh(), color=black, linewidth=6)

    # Convert to a surface
    from plugins.trisurface import TriSurface
    S = TriSurface(M)
    print("nelems = %s, nplex = %s, coords = %s" %
          (S.nelems(), S.nplex(), S.coords.shape))
    clear()
    draw(S)
    print("Total surface area: %s" % S.area())
    export({'surface-1': S})
    setDrawOptions({'bbox': 'auto'})
def createShellModel():
    """Create the Finite Element Model.

    It is supposed here that the Geometry has been created and is available
    as a global variable F.
    """
    
    # Turn the Formex structure into a TriSurface
    # This guarantees that element i of the Formex is element i of the TriSurface
    S = TriSurface(F)
    print "The structure has %s nodes, %s edges and %s faces" % (S.ncoords(),S.nedges(),S.nfaces())
    nodes = S.coords
    elems = S.elems  # the triangles

    clear()
    draw(F)

    # Shell section and material properties
    # VALUES SHOULD BE SET CORRECTLY

    glass_plate = { 
        'name': 'glass_plate',
        'sectiontype': 'shell',
        'thickness': 18,
        'material': 'glass',
        }
    glass = {
        'name': 'glass',
        'young_modulus': 72000,
        'shear_modulus': 26200,
        'density': 2.5e-9,        # T/mm**3
        }
    print glass_plate
    print glass
    glasssection = ElemSection(section=glass_plate,material=glass)

    PDB = PropertyDB()
    # All elements have same property:
    PDB.elemProp(set=arange(len(elems)),section=glasssection,eltype='STRI3')    

    # Calculate the nodal loads

    # Area of triangles
    area,normals = S.areaNormals()
    print "Area:\n%s" % area

    ### DEFINE LOAD CASE (ask user) ###
    res = askItems([('Glass',True),('Snow',False)])
    if not res:
        return

    step = 0
    if res['Glass']:
        step += 1
        NODLoad = zeros((S.ncoords(),3))
        # add the GLASS weight
        wgt = 450e-6 # N/mm**2
        # Or, calculate weight from density:
        # wgt = glass_plate['thickness'] * glass['density'] * 9810 
        # assemble uniform glass load
        for e,a in zip(S.elems,area):
            NODLoad[e] += [ 0., 0., - a * wgt / 3 ]
        # Put the nodal loads in the properties database
        for i,P in enumerate(NODLoad):
            PDB.nodeProp(tag=step,set=i,cload=[P[0],P[1],P[2],0.,0.,0.])

    if res['Snow']:
        step += 1
        NODLoad = zeros((S.ncoords(),3))
        # add NON UNIFORM SNOW
        fn = 'hesperia-nieve.prop'
        snowp = fromfile(fn,sep=',')
        snow_uniform = 320e-6 # N/mm**2
        snow_non_uniform = { 1:333e-6, 2:133e-6, 3:133e-6, 4:266e-6, 5:266e-6, 6:667e-6 }
        # assemble non-uniform snow load
        for e,a,p in zip(S.elems,area,snowp):
            NODLoad[e] += [ 0., 0., - a * snow_non_uniform[p] / 3]
        # Put the nodal loads in the properties database
        for i,P in enumerate(NODLoad):
            PDB.nodeProp(tag=step,set=[i],cload=[P[0],P[1],P[2],0.,0.,0.])

    # Get support nodes
    botnodes = where(isClose(nodes[:,2], 0.0))[0]
    bot = nodes[botnodes].reshape((-1,1,3))
    pf.message("There are %s support nodes." % bot.shape[0])

    botofs = bot + [ 0.,0.,-0.2]
    bbot2 = concatenate([bot,botofs],axis=1)
    print bbot2.shape
    S = Formex(bbot2)
    draw(S)
    
##     np_central_loaded = NodeProperty(3, displacement=[[1,radial_displacement]],coords='cylindrical',coordset=[0,0,0,0,0,1])
##     #np_transf = NodeProperty(0,coords='cylindrical',coordset=[0,0,0,0,0,1])

##     # Radial movement only
##     np_fixed = NodeProperty(1,bound=[0,1,1,0,0,0],coords='cylindrical',coordset=[0,0,0,0,0,1])
    
    # Since we left out the ring beam, we enforce no movement at the botnodes
    bc = PDB.nodeProp(set=botnodes,bound=[1,1,1,0,0,0],csys=CoordSystem('C',[0,0,0,0,0,1]))

    # And we record the name of the bottom nodes set
    botnodeset = Nset(bc.nr)

    fe_model = Dict(dict(nodes=nodes,elems=elems,prop=PDB,botnodeset=botnodeset,nsteps=step))
    export({'fe_model':fe_model})
    smooth()
    lights(False)
def createFrameModel():
    """Create the Finite Element Model.

    It is supposed here that the Geometry has been created and is available
    as a global variable F.
    """
    wireframe()
    lights(False)
    
    # Turn the Formex structure into a TriSurface
    # This guarantees that element i of the Formex is element i of the TriSurface
    S = TriSurface(F)
    nodes = S.coords
    elems = S.elems  # the triangles

    # Create edges and faces from edges
    print "The structure has %s nodes, %s edges and %s faces" % (S.ncoords(),S.nedges(),S.nfaces())

    # Create the steel structure
    E = Formex(nodes[S.getEdges()])
    clear()
    draw(E)
    
    # Get the tri elements that are part of a quadrilateral:
    prop = F.prop
    quadtri = S.getFaceEdges()[prop==6]
    nquadtri = quadtri.shape[0]
    print "%s triangles are part of quadrilateral faces" % nquadtri
    if nquadtri > 0:
        # Create triangle definitions of the quadtri faces
        tri = Connectivity.tangle(quadtri,S.getEdges())
        D = Formex(nodes[tri])
        clear()
        flatwire()
        draw(D,color='yellow')

    conn = connections(quadtri)
    print conn

    # Filter out the single connection edges
    internal = [ c[0] for c in conn if len(c[1]) > 1 ]
    print "Internal edges in quadrilaterals: %s" % internal
    
    E = Formex(nodes[S.getEdges()],1)
    E.prop[internal] = 6
    wireframe()
    clear()
    draw(E)

    # Remove internal edges
    tubes = S.getEdges()[E.prop != 6]

    print "Number of tube elements after removing %s internals: %s" % (len(internal),tubes.shape[0])

    D = Formex(nodes[tubes],1)
    clear()
    draw(D)

    # Beam section and material properties
    b = 60
    h = 100
    t = 4
    b1 = b-2*t
    h1 = h-2*t
    A = b*h - b1*h1
    print b*h**3
    I1 = (b*h**3 - b1*h1**3) / 12
    I2 = (h*b**3 - h1*b1**3) / 12
    I12 = 0
    J = 4 * A**2 / (2*(b+h)/t)

    tube = { 
        'name':'tube',
        'cross_section': A,
        'moment_inertia_11': I1,
        'moment_inertia_22': I2,
        'moment_inertia_12': I12,
        'torsional_constant': J
        }
    steel = {
        'name':'steel',
        'young_modulus' : 206000,
        'shear_modulus' : 81500,
        'density' : 7.85e-9,
        }
    print tube
    print steel

    tubesection = ElemSection(section=tube,material=steel)

    # Calculate the nodal loads

    # Area of triangles
    area,normals = S.areaNormals()
    print "Area:\n%s" % area
    # compute bar lengths
    bars = nodes[tubes]
    barV = bars[:,1,:] - bars[:,0,:]
    barL = sqrt((barV*barV).sum(axis=-1))
    print "Member length:\n%s" % barL


    ### DEFINE LOAD CASE (ask user) ###
    res = askItems([('Steel',True),
                    ('Glass',True),
                    ('Snow',False),
                    ('Solver',None,'select',['Calpy','Abaqus']),
                    ])
    if not res:
        return

    nlc = 0
    for lc in [ 'Steel','Glass','Snow' ]:
        if res[lc]:
            nlc += 1 
    NODLoad = zeros((nlc,S.ncoords(),3))

    nlc = 0
    if res['Steel']:
        # the STEEL weight
        lwgt = steel['density'] * tube['cross_section'] * 9810  # mm/s**2
        print "Weight per length %s" % lwgt
        # assemble steel weight load
        for e,L in zip(tubes,barL):
            NODLoad[nlc,e] += [ 0., 0., - L * lwgt / 2 ]
        nlc += 1
        
    if res['Glass']:
        # the GLASS weight
        wgt = 450e-6 # N/mm**2
        # assemble uniform glass load
        for e,a in zip(S.elems,area):
            NODLoad[nlc,e] += [ 0., 0., - a * wgt / 3 ]
        nlc += 1
        
    if res['Snow']:
        # NON UNIFORM SNOW
        fn = 'hesperia-nieve.prop'
        snowp = fromfile(fn,sep=',')
        snow_uniform = 320e-6 # N/mm**2
        snow_non_uniform = { 1:333e-6, 2:133e-6, 3:133e-6, 4:266e-6, 5:266e-6, 6:667e-6 }

        # assemble non-uniform snow load
        for e,a,p in zip(S.elems,area,snowp):
            NODLoad[nlc,e] += [ 0., 0., - a * snow_non_uniform[p] / 3]
        nlc += 1

    # For Abaqus: put the nodal loads in the properties database
    print NODLoad
    PDB = PropertyDB()
    for lc in range(nlc):
        for i,P in enumerate(NODLoad[lc]):
            PDB.nodeProp(tag=lc,set=i,cload=[P[0],P[1],P[2],0.,0.,0.])

    # Get support nodes
    botnodes = where(isClose(nodes[:,2], 0.0))[0]
    bot = nodes[botnodes]
    pf.message("There are %s support nodes." % bot.shape[0])

    # Upper structure
    nnodes = nodes.shape[0]              # node number offset
    ntubes = tubes.shape[0]              # element number offset
    
    PDB.elemProp(set=arange(ntubes),section=tubesection,eltype='FRAME3D')    
    
    # Create support systems (vertical beams)
    bot2 = bot + [ 0.,0.,-200.]         # new nodes 200mm below bot
    botnodes2 = arange(botnodes.shape[0]) + nnodes  # node numbers
    nodes = concatenate([nodes,bot2])
    supports = column_stack([botnodes,botnodes2])
    elems = concatenate([tubes,supports])
    ## !!!
    ## THIS SHOULD BE FIXED !!!
    supportsection = ElemSection(material=steel,section={ 
        'name':'support',
        'cross_section': A,
        'moment_inertia_11': I1,
        'moment_inertia_22': I2,
        'moment_inertia_12': I12,
        'torsional_constant': J
        })
    PDB.elemProp(set=arange(ntubes,elems.shape[0]),section=supportsection,eltype='FRAME3D')

    # Finally, the botnodes2 get the support conditions
    botnodes = botnodes2

##     # Radial movement only
##     np_fixed = NodeProperty(1,bound=[0,1,1,0,0,0],coords='cylindrical',coordset=[0,0,0,0,0,1])
    
##     # No movement, since we left out the ring beam
##     for i in botnodes:
##         NodeProperty(i,bound=[1,1,1,0,0,0],coords='cylindrical',coordset=[0,0,0,0,0,1])

##     np_central_loaded = NodeProperty(3, displacement=[[1,radial_displacement]],coords='cylindrical',coordset=[0,0,0,0,0,1])
##     #np_transf = NodeProperty(0,coords='cylindrical',coordset=[0,0,0,0,0,1])
    
    # Draw the supports
    S = connect([Formex(bot),Formex(bot2)])
    draw(S,color='black')

    if res['Solver'] == 'Calpy':
        fe_model = Dict(dict(solver='Calpy',nodes=nodes,elems=elems,prop=PDB,loads=NODLoad,botnodes=botnodes,nsteps=nlc))
    else:
        fe_model = Dict(dict(solver='Abaqus',nodes=nodes,elems=elems,prop=PDB,botnodes=botnodes,nsteps=nlc))
    export({'fe_model':fe_model})
    print "FE model created and exported as 'fe_model'"
# Add the missing end triangles
L3 = [ connect([i,i,j],nodid=[0,1,1],bias=[i.nelems()-1,i.nelems()-1,j.nelems()-1])  for i,j in zip(K1[:-1],K1[1:]) ]
draw(L3,color=magenta)

# Collect all triangles in a single Formex
L = (Formex.concatenate(L1)+Formex.concatenate(L3)).setProp(1) + Formex.concatenate(L2).setProp(2)
clear()
draw(L)

# Convert to a Mesh
print "nelems = %s, nplex = %s, coords = %s" % (L.nelems(),L.nplex(),L.coords.shape)
M = L.toMesh()
print "nelems = %s, nplex = %s, coords = %s" % (M.nelems(),M.nplex(),M.coords.shape)
clear()
draw(M,color=yellow,mode=flatwire)
drawNumbers(M)
draw(M.getBorderMesh(),color=black,linewidth=6)

# Convert to a surface
from plugins.trisurface import TriSurface
S = TriSurface(M)
print "nelems = %s, nplex = %s, coords = %s" % (S.nelems(),S.nplex(),S.coords.shape)
clear()
draw(S)
print "Total surface area: %s" % S.area()
export({'surface-1':S})
setDrawOptions({'bbox':'auto'})

# End
Exemple #24
0
def createFrameModel():
    """Create the Finite Element Model.

    It is supposed here that the Geometry has been created and is available
    as a global variable F.
    """
    wireframe()
    lights(False)

    # Turn the Formex structure into a TriSurface
    # This guarantees that element i of the Formex is element i of the TriSurface
    S = TriSurface(F)
    nodes = S.coords
    elems = S.elems  # the triangles

    # Create edges and faces from edges
    print("The structure has %s nodes, %s edges and %s faces" %
          (S.ncoords(), S.nedges(), S.nfaces()))

    # Remove the edges between to quad triangles
    drawNumbers(S.coords)
    quadtri = where(S.prop == 6)[0]
    nquadtri = quadtri.shape[0]
    print("%s triangles are part of quadrilateral faces" % nquadtri)
    faces = S.getElemEdges()[quadtri]
    cnt, ind, xbin = histogram2(faces.reshape(-1), arange(faces.max() + 1))
    rem = where(cnt == 2)[0]
    print("Total edges %s" % len(S.edges))
    print("Removing %s edges" % len(rem))
    edges = S.edges[complement(rem, n=len(S.edges))]
    print("Remaining edges %s" % len(edges))

    # Create the steel structure
    E = Formex(nodes[edges])
    clear()
    draw(E)

    warning("Beware! This script is currently under revision.")

    conn = connections(quadtri)
    print(conn)

    # Filter out the single connection edges
    internal = [c[0] for c in conn if len(c[1]) > 1]
    print("Internal edges in quadrilaterals: %s" % internal)

    E = Formex(nodes[edges], 1)
    E.prop[internal] = 6
    wireframe()
    clear()
    draw(E)

    # Remove internal edges
    tubes = edges[E.prop != 6]

    print("Number of tube elements after removing %s internals: %s" %
          (len(internal), tubes.shape[0]))

    D = Formex(nodes[tubes], 1)
    clear()
    draw(D)

    # Beam section and material properties
    b = 60
    h = 100
    t = 4
    b1 = b - 2 * t
    h1 = h - 2 * t
    A = b * h - b1 * h1
    print(b * h**3)
    I1 = (b * h**3 - b1 * h1**3) / 12
    I2 = (h * b**3 - h1 * b1**3) / 12
    I12 = 0
    J = 4 * A**2 / (2 * (b + h) / t)

    tube = {
        'name': 'tube',
        'cross_section': A,
        'moment_inertia_11': I1,
        'moment_inertia_22': I2,
        'moment_inertia_12': I12,
        'torsional_constant': J
    }
    steel = {
        'name': 'steel',
        'young_modulus': 206000,
        'shear_modulus': 81500,
        'density': 7.85e-9,
    }
    print(tube)
    print(steel)

    tubesection = ElemSection(section=tube, material=steel)

    # Calculate the nodal loads

    # Area of triangles
    area, normals = S.areaNormals()
    print("Area:\n%s" % area)
    # compute bar lengths
    bars = nodes[tubes]
    barV = bars[:, 1, :] - bars[:, 0, :]
    barL = sqrt((barV * barV).sum(axis=-1))
    print("Member length:\n%s" % barL)

    ### DEFINE LOAD CASE (ask user) ###
    res = askItems([
        _I('Steel', True),
        _I('Glass', True),
        _I('Snow', False),
        _I('Solver', choices=['Calpy', 'Abaqus']),
    ])
    if not res:
        return

    nlc = 0
    for lc in ['Steel', 'Glass', 'Snow']:
        if res[lc]:
            nlc += 1
    NODLoad = zeros((nlc, S.ncoords(), 3))

    nlc = 0
    if res['Steel']:
        # the STEEL weight
        lwgt = steel['density'] * tube['cross_section'] * 9810  # mm/s**2
        print("Weight per length %s" % lwgt)
        # assemble steel weight load
        for e, L in zip(tubes, barL):
            NODLoad[nlc, e] += [0., 0., -L * lwgt / 2]
        nlc += 1

    if res['Glass']:
        # the GLASS weight
        wgt = 450e-6  # N/mm**2
        # assemble uniform glass load
        for e, a in zip(S.elems, area):
            NODLoad[nlc, e] += [0., 0., -a * wgt / 3]
        nlc += 1

    if res['Snow']:
        # NON UNIFORM SNOW
        fn = '../data/hesperia-nieve.prop'
        snowp = fromfile(fn, sep=',')
        snow_uniform = 320e-6  # N/mm**2
        snow_non_uniform = {
            1: 333e-6,
            2: 133e-6,
            3: 133e-6,
            4: 266e-6,
            5: 266e-6,
            6: 667e-6
        }

        # assemble non-uniform snow load
        for e, a, p in zip(S.elems, area, snowp):
            NODLoad[nlc, e] += [0., 0., -a * snow_non_uniform[p] / 3]
        nlc += 1

    # For Abaqus: put the nodal loads in the properties database
    print(NODLoad)
    PDB = PropertyDB()
    for lc in range(nlc):
        for i, P in enumerate(NODLoad[lc]):
            PDB.nodeProp(tag=lc, set=i, cload=[P[0], P[1], P[2], 0., 0., 0.])

    # Get support nodes
    botnodes = where(isClose(nodes[:, 2], 0.0))[0]
    bot = nodes[botnodes]
    pf.message("There are %s support nodes." % bot.shape[0])

    # Upper structure
    nnodes = nodes.shape[0]  # node number offset
    ntubes = tubes.shape[0]  # element number offset

    PDB.elemProp(set=arange(ntubes), section=tubesection, eltype='FRAME3D')

    # Create support systems (vertical beams)
    bot2 = bot + [0., 0., -200.]  # new nodes 200mm below bot
    botnodes2 = arange(botnodes.shape[0]) + nnodes  # node numbers
    nodes = concatenate([nodes, bot2])
    supports = column_stack([botnodes, botnodes2])
    elems = concatenate([tubes, supports])
    ## !!!
    ## THIS SHOULD BE FIXED !!!
    supportsection = ElemSection(material=steel,
                                 section={
                                     'name': 'support',
                                     'cross_section': A,
                                     'moment_inertia_11': I1,
                                     'moment_inertia_22': I2,
                                     'moment_inertia_12': I12,
                                     'torsional_constant': J
                                 })
    PDB.elemProp(set=arange(ntubes, elems.shape[0]),
                 section=supportsection,
                 eltype='FRAME3D')

    # Finally, the botnodes2 get the support conditions
    botnodes = botnodes2

    ##     # Radial movement only
    ##     np_fixed = NodeProperty(1,bound=[0,1,1,0,0,0],coords='cylindrical',coordset=[0,0,0,0,0,1])

    ##     # No movement, since we left out the ring beam
    ##     for i in botnodes:
    ##         NodeProperty(i,bound=[1,1,1,0,0,0],coords='cylindrical',coordset=[0,0,0,0,0,1])

    ##     np_central_loaded = NodeProperty(3, displacement=[[1,radial_displacement]],coords='cylindrical',coordset=[0,0,0,0,0,1])
    ##     #np_transf = NodeProperty(0,coords='cylindrical',coordset=[0,0,0,0,0,1])

    # Draw the supports
    S = connect([Formex(bot), Formex(bot2)])
    draw(S, color='black')

    if res['Solver'] == 'Calpy':
        fe_model = Dict(
            dict(solver='Calpy',
                 nodes=nodes,
                 elems=elems,
                 prop=PDB,
                 loads=NODLoad,
                 botnodes=botnodes,
                 nsteps=nlc))
    else:
        fe_model = Dict(
            dict(solver='Abaqus',
                 nodes=nodes,
                 elems=elems,
                 prop=PDB,
                 botnodes=botnodes,
                 nsteps=nlc))
    export({'fe_model': fe_model})
    print("FE model created and exported as 'fe_model'")
Exemple #25
0
def createShellModel():
    """Create the Finite Element Model.

    It is supposed here that the Geometry has been created and is available
    as a global variable F.
    """

    # Turn the Formex structure into a TriSurface
    # This guarantees that element i of the Formex is element i of the TriSurface
    S = TriSurface(F)
    print("The structure has %s nodes, %s edges and %s faces" %
          (S.ncoords(), S.nedges(), S.nfaces()))
    nodes = S.coords
    elems = S.elems  # the triangles

    clear()
    draw(F)

    # Shell section and material properties
    # VALUES SHOULD BE SET CORRECTLY

    glass_plate = {
        'name': 'glass_plate',
        'sectiontype': 'shell',
        'thickness': 18,
        'material': 'glass',
    }
    glass = {
        'name': 'glass',
        'young_modulus': 72000,
        'shear_modulus': 26200,
        'density': 2.5e-9,  # T/mm**3
    }
    print(glass_plate)
    print(glass)
    glasssection = ElemSection(section=glass_plate, material=glass)

    PDB = PropertyDB()
    # All elements have same property:
    PDB.elemProp(set=arange(len(elems)), section=glasssection, eltype='STRI3')

    # Calculate the nodal loads

    # Area of triangles
    area, normals = S.areaNormals()
    print("Area:\n%s" % area)

    ### DEFINE LOAD CASE (ask user) ###
    res = askItems([('Glass', True), ('Snow', False)])
    if not res:
        return

    step = 0
    if res['Glass']:
        step += 1
        NODLoad = zeros((S.ncoords(), 3))
        # add the GLASS weight
        wgt = 450e-6  # N/mm**2
        # Or, calculate weight from density:
        # wgt = glass_plate['thickness'] * glass['density'] * 9810
        # assemble uniform glass load
        for e, a in zip(S.elems, area):
            NODLoad[e] += [0., 0., -a * wgt / 3]
        # Put the nodal loads in the properties database
        for i, P in enumerate(NODLoad):
            PDB.nodeProp(tag=step, set=i, cload=[P[0], P[1], P[2], 0., 0., 0.])

    if res['Snow']:
        step += 1
        NODLoad = zeros((S.ncoords(), 3))
        # add NON UNIFORM SNOW
        fn = '../data/hesperia-nieve.prop'
        snowp = fromfile(fn, sep=',')
        snow_uniform = 320e-6  # N/mm**2
        snow_non_uniform = {
            1: 333e-6,
            2: 133e-6,
            3: 133e-6,
            4: 266e-6,
            5: 266e-6,
            6: 667e-6
        }
        # assemble non-uniform snow load
        for e, a, p in zip(S.elems, area, snowp):
            NODLoad[e] += [0., 0., -a * snow_non_uniform[p] / 3]
        # Put the nodal loads in the properties database
        for i, P in enumerate(NODLoad):
            PDB.nodeProp(tag=step,
                         set=[i],
                         cload=[P[0], P[1], P[2], 0., 0., 0.])

    # Get support nodes
    botnodes = where(isClose(nodes[:, 2], 0.0))[0]
    bot = nodes[botnodes].reshape((-1, 1, 3))
    pf.message("There are %s support nodes." % bot.shape[0])

    botofs = bot + [0., 0., -0.2]
    bbot2 = concatenate([bot, botofs], axis=1)
    print(bbot2.shape)
    S = Formex(bbot2)
    draw(S)

    ##     np_central_loaded = NodeProperty(3, displacement=[[1,radial_displacement]],coords='cylindrical',coordset=[0,0,0,0,0,1])
    ##     #np_transf = NodeProperty(0,coords='cylindrical',coordset=[0,0,0,0,0,1])

    ##     # Radial movement only
    ##     np_fixed = NodeProperty(1,bound=[0,1,1,0,0,0],coords='cylindrical',coordset=[0,0,0,0,0,1])

    # Since we left out the ring beam, we enforce no movement at the botnodes
    bc = PDB.nodeProp(set=botnodes,
                      bound=[1, 1, 1, 0, 0, 0],
                      csys=CoordSystem('C', [0, 0, 0, 0, 0, 1]))

    # And we record the name of the bottom nodes set
    botnodeset = Nset(bc.nr)

    fe_model = Dict(
        dict(nodes=nodes,
             elems=elems,
             prop=PDB,
             botnodeset=botnodeset,
             nsteps=step))
    export({'fe_model': fe_model})
    smooth()
    lights(False)
Exemple #26
0
def run():
    global wviewer,pcolor,px,py
    clear()
    smooth()
    lights(True)
    transparent(False)
    view('iso')

    image = None
    scaled_image = None

    # read the teapot surface
    T = TriSurface.read(getcfg('datadir')+'/teapot.off')
    xmin,xmax = T.bbox()
    T= T.trl(-T.center()).scale(4./(xmax[0]-xmin[0])).setProp(2)
    draw(T)

    # default image file
    dfilename = getcfg('datadir')+'/benedict_6.jpg'
    wviewer = ImageView(dfilename,maxheight=200)

    res = askItems([
        _I('filename',dfilename,text='Image file',itemtype='button',func=selectImage),
        wviewer,
        _I('px',4,text='Number of patches in x-direction'),
        _I('py',6,text='Number of patches in y-direction'),
        _I('kx',30,text='Width of a patch in pixels'),
        _I('ky',30,text='Height of a patch in pixels'),
        _I('scale',1.0,text='Scale factor'),
        _I('trl',[-0.4,-0.1,2.],itemtype='point',text='Translation'),
        _I('method',choices=['projection','intersection']),
        ])

    if not res:
        return

    globals().update(res)

    nx,ny = px*kx,py*ky # pixels
    print('The image is reconstructed with %d x %d pixels'%(nx, ny))

    F = Formex('4:0123').replic2(nx,ny).centered()
    if image is None:
        print("Loading image")
        image = loadImage(filename)
        wpic,hpic = image.width(),image.height()
        print("Image size is %sx%s" % (wpic,hpic))

    if image is None:
        return

    # Create the colors
    color,colortable = image2glcolor(image.scaled(nx,ny))
    # Reorder by patch
    pcolor = color.reshape((py,ky,px,kx,3)).swapaxes(1,2).reshape(-1,kx*ky,3)
    print("Shape of the colors array: %s" % str(pcolor.shape))

    mH = makeGrid(px,py,'Quad8')

    try:
        ratioYX = float(hpic)/wpic
        mH = mH.scale(ratioYX,1) # Keep original aspect ratio
    except:
        pass

    mH0 = mH.scale(scale).translate(trl)

    dg0 = draw(mH0,mode='wireframe')
    zoomAll()
    zoom(0.5)
    print("Create %s x %s patches" % (px,py))

    # Create the transforms
    base = makeGrid(1,1,'Quad8').coords[0]
    patch = makeGrid(kx,ky,'Quad4').toMesh()
    d0 = drawImage(mH0,base,patch)

    if method == 'projection':
        pts = mH0.coords.projectOnSurface(T,[0.,0.,1.],'-f')
        dg1 = d1 = [] # to allow dummy undraw


    else:
        mH1 = mH.rotate(-30.,0).scale(0.5).translate([0.,-.7,-2.])
        dg1 = draw(mH1,mode='wireframe')
        d1 = drawImage(mH1,base,patch)

        x = connect([mH0.asPoints(),mH1.asPoints()])
        dx = draw(x)
        print("Creating intersection with surface")
        pts, il, it, mil=intersectSurfaceWithSegments2(T, x, max1xperline=True)

        if len(x) != len(pts):
            print("Some of the lines do not intersect the surface:")
            print(" %d lines, %d intersections %d missing" % (len(x),len(pts),len(mil)))
            return

    dp = draw(pts, marksize=6, color='white')
    #print pts.shape
    mH2 = Formex(pts.reshape(-1,8,3))

    if method == 'projection':
        x = connect([mH0.points(),mH2.points()])
        dx = draw(x)

    print("Create projection mapping using the grid points")
    d2 = drawImage(mH2.trl([0.,0.,0.01]),base,patch)
    # small translation to make sure the image is above the surface, not cutting it

    print("Finally show the finished image")
    undraw(dp);
    undraw(dx);
    undraw(d0);
    undraw(d1);
    undraw(dg0);
    undraw(dg1);
    view('front')
    zoomAll()
    sleep(1)
    transparent()
##
"""HorseTorse

level = 'advanced'
topics = ['geometry','surface','mesh']
techniques = ['intersection']

"""
from plugins.trisurface import TriSurface
from mesh import Mesh

reset()
smooth()
lights(True)

S = TriSurface.read(getcfg("datadir") + "/horse.off")
SA = draw(S)

res = askItems([("direction", [1.0, 0.0, 0.0]), ("number of sections", 20), ("color", "red"), ("ontop", False)])
if not res:
    exit()

d = res["direction"]
n = res["number of sections"]
c = res["color"]

slices = S.slice(dir=d, nplanes=n)
linewidth(2)
draw(slices, color=c, view=None, bbox="last", nolight=True, ontop=res["ontop"])
draw(slices[0])