示例#1
0
def sphere(ndiv=6, base='icosa', equiv='max'):
    """Create a triangulated approximation of a spherical surface.

    A (possibly high quality) approximation of a spherical surface is
    constructed as follows. First a simple base triangulated surface
    is created. 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. For `ndiv=1`, the base surface is
    returned.

    Parameters:

    - `ndiv`: number of divisions along the edges of the base surface.
    - `base`: the type of base surface. One of the following:

      - 'icosa': icosahedron (20 faces): this offers the highest
        quality with triangles of almost same size ans shape.
      - 'octa': octahedron (8 faces): this model will have the same
        mesh on each of the quadrants. The coordinate planes do not
        cut any triangle. This model is this fit to be subdivided along
        coordinate planes.

    Returns a TriSurface, representing a triangulated approximation of a
    spherical surface with radius 1 and center at the origin.

    """
    from pyformex import elements
    from pyformex.trisurface import TriSurface

    base = getattr(elements, base.capitalize())
    M = TriSurface(base.vertices, base.faces)
    M = M.subdivide(ndiv).fuse()
    M = M.projectOnSphere()
    return M
示例#2
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 pyformex.gui.draw import draw, pause, undraw
        from pyformex.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)
示例#3
0
def vmtkDistanceOfPoints(self, X, tryfixsign=True, nproc=1):
    """Find the distances of points X to the TriSurface self.

    - `X` is a (nX,3) shaped array of points.
    - `nproc`: number of parallel processes to use. On multiprocessor machines
      this may be used to speed up the processing. If <= 0 , the number of
      processes will be set equal to the number of processors, to achieve
      a maximal speedup.

    Retuns a tuple of vector and signed scalar distances for all points.
    The signed distance is positive if the distance vector and the surface
    normal have negative dot product, i.e. if X is outer with respect to self.
    """
    if nproc < 1:
        nproc = multi.cpu_count()
    if nproc == 1:
        S = TriSurface(
            X,
            arange(X.shape[0]).reshape(-1, 1) *
            ones(3, dtype=int).reshape(1, -1))
        return vmtkDistanceOfSurface(self, S, tryfixsign)
    else:
        datablocks = multi.splitar(X, nproc, close=False)
        print('-----distance of %d points from %d triangles with %d proc' %
              (len(X), self.nelems(), nproc))
        tasks = [(vmtkDistanceOfPoints, (self, d, tryfixsign, 1))
                 for d in datablocks]
        ind = multi.multitask(tasks, nproc)
        vdist, sdist = list(zip(*ind))
        return concatenate(vdist), concatenate(sdist)
示例#4
0
def Cube():
    """Create the surface of a cube

    Returns a TriSurface representing the surface of a unit cube.
    Each face of the cube is represented by two triangles.
    """
    from pyformex.trisurface import TriSurface
    back = Formex('3:012934')
    fb = back.reverse() + back.translate(2, 1)
    faces = fb + fb.rollAxes(1) + fb.rollAxes(2)
    return TriSurface(faces)
示例#5
0
def vmtkDistancePointsToSegments(X, L, atol=1.e-4):
    """Find the shortest distances from points X to segments L.

    X is a (nX,3) shaped array of points.
    L is a line2 Mesh.
    atol is the height of the triangles built from lines L (use lines as non-degenerated triangles to vmtk)

    Retuns a tuple of vector and scalar distances for all points.
    Points and lines are first converted into nearly degenerate triangles
    which are then used by vmtkDistanceOfSurface.
    """
    SX = TriSurface(
        X,
        arange(X.shape[0]).reshape(-1, 1) * ones(3, dtype=int).reshape(1, -1))
    from pyformex.geomtools import anyPerpendicularVector
    Lf = L.coords[L.elems]
    L0, L1 = Lf[:, 0], Lf[:, 1]
    perp = anyPerpendicularVector(L1 - L0)
    L2 = 0.5 * (L0 + L1) + normalize(perp) * atol
    SL = TriSurface(
        concatenate([L0, L1, L2], axis=1).reshape(-1, 3, 3)
    )  #NB they should not be fully degenerate otherwise VMTK will not consider them!
    vdist, dist = vmtkDistanceOfSurface(SL, SX, tryfixsign=False)
    return vdist, abs(dist)
示例#6
0
def create_points(color=0, obj=None):
    """Create points on visible objects.
    
    Points are picked on the surface of a single object, a list of objects
    or of all visible objects (default).
    It returns all the points in the order they were picked 
    and a variable indicating how  you have exited:
    - you can escape the create_points by pushing ESC on Keyboard: None is returned
    - right mouse click or ENTER returns an empty list []
    """
    # build one surface around all visible objects or the given objects
    if obj is None:
        canvas = pf.GUI.viewports.current
        obj = [a.object for a in canvas.actors]
    if type(obj)!=list:
        obj = [obj]
    S = []
    for o in obj:
        try:
            S.append(o.toSurface())
        except:
            pass
    if len(S)==0:
        warning('there are no objects on the screen')
        return []
    S = TriSurface.concatenate(S) # the surface of all visible objects
    # now pick points on the surface
    D = []
    P = []
    while True:
        col = mycolor(color)
        drawopt = dict(color=col,bbox='last', view=None)
        p = create_point(S)
        if isNoneOrEmpty(p):
            undraw(D)
            break
        s = "*** Point 3D report ***\n"
        s += '%s'%p
        cprint (s, color=col) # print in color on pyFormex message board
        D+=[
        draw(p, **drawopt),
        drawMarks([p,], [color,], size=20, mode='smooth',**drawopt) # smooth is needed for drawMarks
        ]
        P.append(p)
        color+=1
    return Coords.concatenate(P), p
示例#7
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:

    - curve: if True, an intersection curve is computed, else the surface.

    Returns the resulting TriSurface (curve=False), or a plex-2 Formex
    (curve=True), or None if the input surfaces do not intersect.
    """
    # import here to avoid circular import
    from pyformex.trisurface import TriSurface
    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
    print("Writing temp file %s" % tmp)
    self.write(tmp, 'gts')
    print("Writing temp file %s" % tmp1)
    surf.write(tmp1, 'gts')
    print("Performing boolean operation")
    cmd = "gtsset %s %s %s %s %s" % (options, op, tmp, tmp1, filt)
    P = utils.system(cmd, stdout=open(tmp2, 'w'))
    os.remove(tmp)
    os.remove(tmp1)
    if P.sta or verbose:
        print(P.out)
    if P.sta:
        print(P.err)
        return None
    print("Reading result from %s" % tmp2)
    if curve:
        res = read_gts_intersectioncurve(tmp2)
    else:
        res = TriSurface.read(tmp2)
    os.remove(tmp2)
    return res
示例#8
0
def voronoi(fn):
    """Determine the voronoi diagram corresponding with a triangulated surface.

    fn is the file name of a surface, including the extension (.off, .stl, .gts, .neu or .smesh)
    The voronoi diagram is determined by Tetgen.
    The output are the voronoi nodes and the corresponding radii of the voronoi spheres.
    """
    S = TriSurface.read(fn)
    fn, ftype = os.path.splitext(fn)
    ftype = ftype.strip('.').lower()
    if ftype != 'smesh':
        S.write('%s.smesh' % fn)
    P = utils.command('tetgen -zpv %s.smesh' % fn)
    #information tetrahedra
    elems = tetgen.readElems('%s.1.ele' % fn)[0]
    nodes = tetgen.readNodes('%s.1.node' % fn)[0]
    #voronoi information
    nodesVor = tetgen.readNodes('%s.1.v.node' % fn)[0]
    #calculate the radii of the voronoi spheres
    vec = nodesVor[:] - nodes[elems[:, 0]]
    radii = sqrt((vec * vec).sum(axis=-1))
    return nodesVor, radii
示例#9
0
def voronoiInner(fn):
    """Determine the inner voronoi diagram corresponding with a triangulated surface.

    fn is the file name of a surface, including the extension (.off, .stl, .gts, .neu or .smesh)
    The output are the voronoi nodes and the corresponding radii of the voronoi spheres.
    """
    S = TriSurface.read(fn)
    fn, ftype = os.path.splitext(fn)
    ftype = ftype.strip('.').lower()
    if ftype != 'smesh':
        S.write('%s.smesh' % fn)
    P = utils.command('tetgen -zp %s.smesh' % fn)
    #information tetrahedra
    elems = tetgen.readElems('%s.1.ele' % fn)[0]
    nodes = tetgen.readNodes('%s.1.node' % fn)[0].astype(float64)
    #calculate surface normal for each point
    elemsS = array(S.elems)
    NT = S.areaNormals()[1]
    NP = zeros([nodes.shape[0], 3])
    for i in [0, 1, 2]:
        NP[elemsS[:, i]] = NT
    #calculate centrum circumsphere of each tetrahedron
    centers = circumcenter(nodes, elems)[0]
    #check if circumcenter falls within the geomety described by the surface
    ie = column_stack([
        ((nodes[elems[:, j]] - centers[:]) * NP[elems[:, j]]).sum(axis=-1)
        for j in [0, 1, 2, 3]
    ])
    ie = ie[:, :] >= 0
    w = where(ie.all(1))[0]
    elemsInner = elems[w]
    nodesVorInner = centers[w]
    #calculate the radii of the voronoi spheres
    vec = nodesVorInner[:] - nodes[elemsInner[:, 0]]
    radii = sqrt((vec * vec).sum(axis=-1))
    return nodesVorInner, radii
示例#10
0
def remesh(self,
           elementsizemode='edgelength',
           edgelength=None,
           area=None,
           areaarray=None,
           aspectratio=None,
           excludeprop=None,
           includeprop=None,
           preserveboundary=False,
           conformal='border',
           options=''):
    """Remesh a TriSurface.

    Returns the remeshed TriSurface. If the TriSurface has property numbers
    the property numbers will be inherited by the remeshed surface.

    Parameters:

    - `elementsizemode`: str: metric that is used for remeshing.
      `edgelength`, `area` and `areaarray` allow to specify a global
      target edgelength, area and areaarray (area at each node), respectively.
    - `edgelength`: float: global target triangle edgelength
    - `area`: float: global target triangle area
    - `areaarray`: array of float: nodal target triangle area
    - `aspectratio`: float: upper threshold for aspect ratio (default=1.2)
    - `includeprop`: either a single integer, or a list/array of integers.
      Only the regions with these property number(s) will be remeshed.
      This option is not compatible with `exludeprop`.
    - `excludeprop`: either a single integer, or a list/array of integers.
      The regions with these property number(s) will not be remeshed.
      This option is not compatible with `includeprop`.
    - `preserveboundary`: if True vmtk tries to keep the shape of the border.
    - `conformal`: None, `border` or `regionsborder`.
      If there is a border (ie. the surface is open) conformal=`border`
      preserves the border line (both points and connectivity); conformal=`regionsborder`
      preserves both border line and lines between regions with different property numbers.
    
    Detected BUGS:
    
    - preserveboundary = True
    
        With VMTK 1.3 and VTK6 there is a bug: if surface has a boundary (is not closed) and you want preserveboundary=False you can an error:
        python: /build/vtk6-E5SYwm/vtk6-6.3.0+dfsg1/Common/Core/vtkDataArrayTemplate.h:191: T vtkDataArrayTemplate<T>::GetValue(vtkIdType) [with T = int; vtkIdType = long long int]: Assertion `id >= 0 && id < this->Size' failed.
        Aborted
    
        As a workaround, you can
        - either close the surface (so there is no boundary, and preserveboundary will be ignored) 
        - or add some extensions (with different property number) to the borders and use preserveboundary=True
        After remeshing you can clip the added parts using their property number.
    """
    if includeprop is not None:
        if excludeprop is not None:
            raise ValueError('you cannot use both excludeprop and includeprop')
        else:
            ps = self.propSet()
            mask = in1d(ar1=ps, ar2=checkArray1D(includeprop))
            if sum(mask) == 0:
                utils.warn("warn_vmtk_includeprop", data=(includeprop, ps))
                return self
            excludeprop = ps[~mask]
    if conformal == 'border' or conformal == 'regionsborder':
        if elementsizemode == 'areaarray':
            raise ValueError(
                'conformal (regions)border and areaarray cannot be used together (yet)!'
            )  #conformalBorder alters the node list. Afterwards, the nodes do not correspond with pointdata.
        if self.isClosedManifold() == False:
            if conformal == 'regionsborder':
                if self.propSet() is not None:
                    if len(self.propSet()) > 1:
                        return TriSurface.concatenate([
                            remesh(s2,
                                   elementsizemode=elementsizemode,
                                   edgelength=edgelength,
                                   area=area,
                                   areaarray=None,
                                   aspectratio=aspectratio,
                                   excludeprop=excludeprop,
                                   preserveboundary=preserveboundary,
                                   conformal='border')
                            for s2 in self.splitProp()
                        ])
            added = TriSurface(
                self.getBorderMesh().convert('line3').setType('tri3'))
            s1 = self + added.setProp(-1) + added.setProp(
                -2)  #add triangles on the border
            s1 = s1.fuse().compact().renumber(
            )  #this would mix up the areaarray!!
            excludeprop1 = array([-1, -2])
            if excludeprop is not None:
                excludeprop1 = append(excludeprop1,
                                      asarray(excludeprop).reshape(-1))
            return remesh(s1,
                          elementsizemode=elementsizemode,
                          edgelength=edgelength,
                          area=area,
                          areaarray=None,
                          aspectratio=aspectratio,
                          excludeprop=excludeprop1,
                          preserveboundary=preserveboundary,
                          conformal=None).cselectProp([-1, -2]).compact()
    else:
        if conformal is not None:
            raise ValueError(
                'conformal should be either None, border or regionsborder')

    from pyformex.plugins.vtk_itf import writeVTP, checkClean, readVTKObject
    tmp = utils.tempFile(suffix='.vtp').name
    tmp1 = utils.tempFile(suffix='.vtp').name
    fielddata, celldata, pointdata = {}, {}, {}
    cmd = 'vmtk vmtksurfaceremeshing -ifile %s -ofile %s' % (tmp, tmp1)
    if elementsizemode == 'edgelength':
        if edgelength is None:
            self.getElemEdges()
            E = Mesh(self.coords, self.edges, eltype='line2')
            edgelength = E.lengths().mean()
        cmd += ' -elementsizemode edgelength -edgelength %f' % edgelength
    elif elementsizemode == 'area':
        if area is None:
            area = self.areas().mean()
        cmd += ' -elementsizemode area -area %f' % area
    elif elementsizemode == 'areaarray':
        if not checkClean(self):
            raise ValueError(
                "Mesh is not clean: vtk will alter the node numbering and the areaarray will not correspond to the node numbering. To clean: mesh.fuse().compact().renumber()"
            )
        cmd += ' -elementsizemode areaarray -areaarray nodalareas '
        pointdata['nodalareas'] = areaarray
    if aspectratio is not None:
        cmd += ' -aspectratio %f' % aspectratio
    if excludeprop is not None:
        cmd += ' -exclude ' + ' '.join([
            '%d' % i for i in checkArray1D(excludeprop, kind='i', allow=None)
        ])
    if preserveboundary:
        cmd += ' -preserveboundary 1'
    if self.prop is not None:
        cmd += ' -entityidsarray prop'
    print("Writing temp file %s" % tmp)
    cmd += ' %s' % options

    writeVTP(mesh=self, fn=tmp, pointdata=pointdata)
    print("Remeshing with command\n %s" % cmd)
    P = utils.command(cmd)
    os.remove(tmp)
    if P.sta:
        print("An error occurred during the remeshing.")
        print(P.out)
        return None
    [coords, cells, polys, lines,
     verts], fielddata, celldata, pointdata = readVTKObject(tmp1)
    S = TriSurface(coords, polys)
    if self.prop is not None:
        S = S.setProp(celldata['prop'])
    os.remove(tmp1)
    return S
示例#11
0
def Horse():
    return TriSurface.read(getcfg('datadir') + '/horse.off')