void splitEdge(Edge* e, Vertex *v, Edge *&e1, Edge *&e2) {

    cout << "  split edge: e = " << e << ", v = " << v->p << endl;
    Vertex *v1 = e->Orig();
    Vertex *v2 = e->Dest();
    Face *fl = e->Left();
    Face *fr = e->Right();
    Edge *a = e->Lprev();
    Edge *d = e->Lnext();

    cout << "    before kill: e = (" << e << ") " << endl;
    Edge::killEdge(e);

    Edge *b = Edge::makeEdge(v1, v, fl, fr);
    Edge *c = Edge::makeEdge(v, v2, fl, fr);
    cout << "               : b = (" << b << ") " << endl;
    cout << "               : c = (" << c << ") " << endl;

    Edge::splice(a->Sym(), b);
    Edge::splice(b->Sym(), c);
    Edge::splice(c->Sym(), d);

    e1 = b;
    e2 = c;
    cout << "    final: e = (" << e << ") " << endl;
}
Exemplo n.º 2
0
Edge *Subdivision::locate(const Vec2& x, Edge *start)
{
    Edge *e = start;
    double t = triArea(x, e->Dest(), e->Org());

    if (t>0) {                  // x is to the right of edge e
        t = -t;
        e = e->Sym();
    }


    while (true)
    {
        Edge *eo = e->Onext();
        Edge *ed = e->Dprev();

        double to = triArea(x, eo->Dest(), eo->Org());
        double td = triArea(x, ed->Dest(), ed->Org());

        if (td>0)                       // x is below ed
            if (to>0 || to==0 && t==0) {// x is interior, or origin endpoint
                startingEdge = e;
                return e;
            }
            else {                      // x is below ed, below eo
                t = to;
                e = eo;
            }
        else                            // x is on or above ed
            if (to>0)                   // x is above eo
                if (td==0 && t==0) {    // x is destination endpoint
                    startingEdge = e;
                    return e;
                }
                else {                  // x is on or above ed and above eo
                    t = td;
                    e = ed;
                }
            else                        // x is on or below eo
                if (t==0 && !leftOf(eo->Dest(), e))
		    // x on e but subdiv. is to right
                    e = e->Sym();
                else if (rand()&1) {  // x is on or above ed and
                    t = to;             // on or below eo; step randomly
                    e = eo;
                }
                else {
                    t = td;
                    e = ed;
                }
    }
}
Exemplo n.º 3
0
Edge *Cell::makeVertexEdge(Vertex *vertex, Face *left, Face *right) {
    assert(vertex != 0);
    assert(left != 0);
    assert(right != 0);

    // locate the edges to the right of each of the faces in the orbit of the
    // vertex
    Edge *edge  = vertex->getEdge();
    Edge *edge1 = getOrbitLeft(edge, right);
    Edge *edge2 = getOrbitLeft(edge, left);

    if (edge1 == 0) {
        (void)fprintf(stderr, "Cell::makeVertexEdge: unable to locate right face %u on vertex %u\n",
                      right->getID(), vertex->getID());
        abort();
    }

    if (edge2 == 0) {
        (void)fprintf(stderr, "Cell::makeVertexEdge: unable to locate left face %u on vertex %u\n",
                      left->getID(), vertex->getID());
        abort();
    }

    // create a new vertex and copy the position of the vertex of origin
    Vertex *vertexNew = Vertex::make(this);
    vertexNew->pos = vertex->pos;

    // create a new edge and rotate it to make a clockwise loop
    Edge *edgeNew = Edge::make()->Rot();

    // connect the origin (and destination) of the new edge to _vertex_ so that
    // the left face of the edge is _left_
    // this makes a loop on the inside of _left_
    Edge::splice(edge2, edgeNew);

    // split the origin and destination of the loop so that the right face of the
    // edge is now _right_
    // this results in a non-loop edge dividing _left_ from _right_
    Edge::splice(edge1, edgeNew->Sym());

    // initialize the secondary attributes of the new edge
    edgeNew->setOrg(edge1->Org());
    edgeNew->setLeft(edge2->Left());
    edgeNew->setRight(edge1->Left());

    // all edges leaving the destination orbit of the new edge now have the new
    // vertex as their vertex of origin
    setOrbitOrg(edgeNew->Sym(), vertexNew);

    return edgeNew;
}
Exemplo n.º 4
0
Edge *Cell::makeFaceEdge(Face *face, Vertex *org, Vertex *dest) {
    assert(face != 0);
    assert(org != 0);
    assert(dest != 0);

    // locate the edges leaving each of the vertices in the orbit of the face
    Edge *edge  = face->getEdge();
    Edge *edge1 = getOrbitOrg(edge, org);
    Edge *edge2 = getOrbitOrg(edge, dest);

    if (edge1 == 0) {
        (void)fprintf(stderr, "Cell::makeFaceEdge: unable to locate origin vertex %u on face %u\n",
                      org->getID(), face->getID());
        abort();
    }

    if (edge2 == 0) {
        (void)fprintf(stderr, "Cell::makeFaceEdge: unable to locate destination vertex %u on face %u\n",
                      dest->getID(), face->getID());
        abort();
    }

    // create a new face
    Face *faceNew = Face::make(this);

    // create a new (non-loop) edge
    Edge *edgeNew = Edge::make();

    // connect the destination of the new edge to the origin of _edge2_
    // both faces of the edge are now _face_
    Edge::splice(edge2, edgeNew->Sym());

    // connect the origin of the new edge to _edge1_
    // _face_ is split in half along the new edge, with the new face introduced
    // on the right
    Edge::splice(edge1, edgeNew);

    // initialize the secondary attributes of the new edge
    edgeNew->setOrg(edge1->Org());
    edgeNew->setDest(edge2->Org());
    edgeNew->setLeft(edge2->Left());

    // all edges in the right orbit of the new edge (i.e. the left orbit of its
    // Sym) now have the new face as their left face
    setOrbitLeft(edgeNew->Sym(), faceNew);

    return edgeNew;
}
int getVertexOrder(Edge *e, vector<Point>& pts) {
	int count = 1;
	Edge *iter = e->Onext();

	if (iter == NULL) {
		cout << "Erro: ponteiro next == NULL\n";
		return -1;
	}

	pts.clear();
	pts.push_back(e->Sym()->Orig()->p);

	while (iter != e && count < 100) {
		count++;
		iter = iter->Onext();
		pts.push_back(iter->Sym()->Orig()->p);
	}

	if (count == 100) {
		cout << "Erro: loop infinito\n";
		return -1;
	}

	return count;
}
Edge * splitFace(Face *fl, Vertex *v1, Vertex *v2) {

	Face *fr = new Face();

	//cout << "  split vertices: " << v1->p << "-" << v2->p << endl;
	Edge *a = v1->getEdge();
	Edge *b = v2->getEdge();
	Edge *c = Edge::makeEdge(v1, v2, fl, fr);

	while (a->Left() != fl) { a = a->Onext(); }
	while (b->Left() != fl) { b = b->Onext(); }

	Edge::splice(a, c);
	Edge::splice(b, c->Sym());

	Edge *cIter = c->Lnext();
	while (cIter != c) {
		cIter->setLeft(fl);
		cIter = cIter->Lnext();
	}
	cIter = c->Rnext();
	while (cIter != c) {
		cIter->setRight(fr);
		cIter = cIter->Rnext();
	}

	return c;
}
Exemplo n.º 7
0
Subdivision::Subdivision(const Point2d& a, const Point2d& b, const Point2d& c)
// Initialize a subdivision to the triangle defined by the points a, b, c.
{
	Point2d *da, *db, *dc;
	da = new Point2d(a), db = new Point2d(b), dc = new Point2d(c);
	Edge* ea = MakeEdge();
	ea->EndPoints(da, db);
	Edge* eb = MakeEdge();
	Splice(ea->Sym(), eb);
	eb->EndPoints(db, dc);
	Edge* ec = MakeEdge();
	Splice(eb->Sym(), ec);
	ec->EndPoints(dc, da);
	Splice(ec->Sym(), ea);
	startingEdge = ea;
}
Exemplo n.º 8
0
Edge *Subdivision::connect(Edge *a, Edge *b)
{
    Edge *e = makeEdge();
    splice(e, a->Lnext());
    splice(e->Sym(), b);
    e->EndPoints(a->Dest(), b->Org());

    return e;
}
Exemplo n.º 9
0
Edge* Connect(Edge* a, Edge* b)
// Add a new edge e connecting the destination of a to the
// origin of b, in such a way that all three have the same
// left face after the connection is complete.
// Additionally, the data pointers of the new edge are set.
{
	Edge* e = MakeEdge();
	Splice(e, a->Lnext());
	Splice(e->Sym(), b);
	e->EndPoints(a->Dest(), b->Org());
	return e;
}
Exemplo n.º 10
0
void Subdivision::initMesh(const Vec2& A,const Vec2& B,
			   const Vec2& C,const Vec2& D)
{
    Vec2& a = A.clone();
    Vec2& b = B.clone();
    Vec2& c = C.clone();
    Vec2& d = D.clone();

    Edge *ea = makeEdge();
    ea->EndPoints(a, b);

    Edge *eb = makeEdge();
    splice(ea->Sym(), eb);
    eb->EndPoints(b, c);

    Edge *ec = makeEdge();
    splice(eb->Sym(), ec);
    ec->EndPoints(c, d);

    Edge *ed = makeEdge();
    splice(ec->Sym(), ed);
    ed->EndPoints(d, a);
    splice(ed->Sym(), ea);

    Edge *diag = makeEdge();
    splice(ed->Sym(),diag);
    splice(eb->Sym(),diag->Sym());
    diag->EndPoints(a,c);

    startingEdge = ea;

    first_face = NULL;

    makeFace(ea->Sym()).update(*this);
    makeFace(ec->Sym()).update(*this);

}
Exemplo n.º 11
0
void Subdivision::InsertSite(const Point2d& x)
// Inserts a new point into a subdivision representing a Delaunay
// triangulation, and fixes the affected edges so that the result
// is still a Delaunay triangulation. This is based on the
// pseudocode from Guibas and Stolfi (1985) p.120, with slight
// modifications and a bug fix.
{
	Edge* e = Locate(x);
	if ((x == e->Org2d()) || (x == e->Dest2d()))  // point is already in
	    return;
	else if (OnEdge(x, e)) {
		e = e->Oprev();
		DeleteEdge(e->Onext());
	}

	// Connect the new point to the vertices of the containing
	// triangle (or quadrilateral, if the new point fell on an
	// existing edge.)
	Edge* base = MakeEdge();
	base->EndPoints(e->Org(), new Point2d(x));
	Splice(base, e);
	startingEdge = base;
	do {
		base = Connect(e, base->Sym());
		e = base->Oprev();
	} while (e->Lnext() != startingEdge);

	// Examine suspect edges to ensure that the Delaunay condition
	// is satisfied.
	do {
		Edge* t = e->Oprev();
		if (RightOf(t->Dest2d(), e) &&
			InCircle(e->Org2d(), t->Dest2d(), e->Dest2d(), x)) {
				Swap(e);
				e = e->Oprev();
		}
		else if (e->Onext() == startingEdge)  // no more suspect edges
			return;
		else  // pop a suspect edge
		    e = e->Onext()->Lprev();
	} while (TRUE);
}
Exemplo n.º 12
0
Edge* Subdivision::Locate(const Point2d& x)
// Returns an edge e, s.t. either x is on e, or e is an edge of
// a triangle containing x. The search starts from startingEdge
// and proceeds in the general direction of x. Based on the
// pseudocode in Guibas and Stolfi (1985) p.121.
{
	Edge* e = startingEdge;

	while (TRUE) {
		if (x == e->Org2d() || x == e->Dest2d())
		    return e;
		else if (RightOf(x, e))
			 e = e->Sym();
		else if (!RightOf(x, e->Onext()))
			 e = e->Onext();
		else if (!RightOf(x, e->Dprev()))
			 e = e->Dprev();
		else
		    return e;
	}
}
Exemplo n.º 13
0
int main() {
        using cgl::Point2d;
        using cgl::Edge;
        using cgl::make_edge;

        // Point2d *a = new Point2d(0.0, 0.0); // 0
        // Point2d *b = new Point2d(1.0, 0.0); // 1
        // Point2d *c = new Point2d(1.0, 1.0); // 2
        // Point2d *d = new Point2d(0.0, 1.0); // 3

        Edge *ea = make_edge(0, 1);
        Edge *eb = make_edge(ea->Dest(), 2);
        Edge *ec = make_edge(eb->Dest(), 3);
        Edge *ed = make_edge(ec->Dest(), 0);
        Edge *ee = make_edge(eb->Dest(), ea->Org());

        // Connect eb to ea->Dest()
        splice(ea->Sym(), eb);
        // Connect ec to eb->Dest()
        splice(eb->Sym(), ec);
        // Connect ed to ec->Sym() and ea
        splice(ec->Sym(), ed);
        splice(ed->Sym(), ea);
        // Connect ee to ec and ea -- this is equivalent to Delaunay::connect
        splice(ee, eb->Lnext());
        splice(ee->Sym(), ea);

        // Traverse the convex hull
        if (ea->Dnext() != eb->Sym()) {
                std::cerr << "Error: ea->Dnext() != eb->Sym()" << std::endl;
                return 1;
        }

        if (eb->Dnext() != ec->Sym()) {
                std::cerr << "Error: eb->Dnext() != ec->Sym()" << std::endl;
                return 1;
        }

        if (ec->Dnext() != ed->Sym()) {
                std::cerr << "Error: ec->Dnext() != ed->Sym()" << std::endl;
                return 1;
        }

        if (ed->Dprev() != ee) {
                std::cerr << "Error: ed->Dprev() != ee" << std::endl;
                return 1;
        }

        // Cross linkage -- test algebra
        // Rot / invRot
        if (ee->Rot()->Org() != ed->invRot()->Org()) {
                std::cerr << "Error: ee->Rot()->Org() != ed->invRot()->Org()" <<
                        std::endl;
                return 1;
        }

        // Sym
        if (ee->Sym()->Org() != 0) {
                std::cerr << "Error: ee->Sym()->Org() != a" << std::endl;
                return 1;
        }

        // Onext
        if (ee->Onext() != eb->Sym()) {
                std::cerr << "Error: ee->Onext() != eb->Sym()" << std::endl;
                return 1;
        }

        // Oprev
        if (ee->Oprev() != ec) {
                std::cerr << "Error: ee->Oprev() != ec" << std::endl;
                return 1;
        }

        // Dnext
        if (ee->Dnext() != ed) {
                std::cerr << "Error: ee->Dnext() != ed" << std::endl;
                return 1;
        }

        // Dprev
        if (ee->Dprev() != ea->Sym()) {
                std::cerr << "Error: ee->Dprev() != ea->Sym()" << std::endl;
                return 1;
        }

        // Lnext
        if (ee->Lnext() != ea) {
                std::cerr << "Error: ee->Lnext() != ea" << std::endl;
                return 1;
        }

        // Lprev
        if (ee->Lprev() != eb) {
                std::cerr << "Error: ee->Lprev() != eb" << std::endl;
                return 1;
        }

        // Rnext
        if (ee->Rnext() != ec->Sym()) {
                std::cerr << "Error: ee->Rnext() != ec->Sym()" << std::endl;
                return 1;
        }

        // Rprev
        if (ee->Rprev() != ed->Sym()) {
                std::cerr << "Error: ee->Rprev() != ed->Sym()" << std::endl;
                return 1;
        }

        // Org / Dest
        if (ed->Org() != 3) {
                std::cerr << "Error: ed->Org() != d" << std::endl;
                return 1;
        }

        if (ed->Dest() != 0) {
                std::cerr << "Error: ed->Dest() != a" << std::endl;
                return 1;
        }

        if (ee->Org()  != 2) {
                std::cerr << "Error: ee->Org() != c" << std::endl;
                return 1;
        }

        if (ee->Dest()  != 0) {
                std::cerr << "Error: ee->Dest() != a" << std::endl;
                return 1;
        }

        // Let the system clean up the memory

        return 0;
}
Exemplo n.º 14
0
/*Type of Cut Strategy is defined as:
 * (useAltCut, useVertical) = (true, true)  => Alternating Cuts
 * (useAltCut, useVertical) = (true, false) => Horizontal Cuts*
 * (useAltCut, useVertical) = (false, true)  => Vertical Cuts
 * */
void delaunay(Vertex **ppVertices, long numVertices, Edge **ppLe, Edge **ppRe,
		bool useAltCuts, bool useVertical) {
    NOT_NULL(ppVertices);
    
    if(numVertices == 2) {
        Edge *pA = Edge::makeEdge();
        if(*ppVertices[0] > *ppVertices[1]) {
            ELEM_SWAP(ppVertices[0], ppVertices[1]);
        }
        pA->setOrg(ppVertices[0]);
        pA->setDest(ppVertices[1]);
        *ppLe = pA;
        *ppRe = pA->Sym();
    }
    else if(numVertices == 3) {
        Edge *pA = Edge::makeEdge(),
             *pB = Edge::makeEdge(),
             *pC = 0;

        if(*ppVertices[0] > *ppVertices[1]) {
            ELEM_SWAP(ppVertices[0], ppVertices[1]);
        }
        if(*ppVertices[1] > *ppVertices[2]) {
            ELEM_SWAP(ppVertices[1], ppVertices[2]);
        }

        Edge::splice(pA->Sym(), pB);
        pA->setOrg(ppVertices[0]);
        pA->setDest(ppVertices[1]);
        pB->setOrg(ppVertices[1]);
        pB->setDest(ppVertices[2]);
        
        REAL ccw = orient2d(ppVertices[0]->Pos(), ppVertices[1]->Pos(), ppVertices[2]->Pos());
        if(ccw > 0) {
            pC = Edge::connect(pB, pA);
            *ppLe = pA;
            *ppRe = pB->Sym();
        }
        else if(ccw < 0) {
            pC = Edge::connect(pB, pA);
            *ppLe = pC->Sym();
            *ppRe = pC;
        }
        else {
            *ppLe = pA;
            *ppRe = pB->Sym();
        }
    }
    else {
        long middle = std::floor(numVertices/2);
        Edge *pLdo = 0, 
             *pLdi = 0, 
             *pRdi = 0, 
             *pRdo = 0;

        //These vertices are used for merging a horizontal cut
        Vertex *pBotMax = 0, //highest vertex of bottom half
               *pTopMin = 0, //lowest vertex of top half
               *pMin    = 0, //Lexicographically max vertex
               *pMax    = 0; //Lexicographically min vertex
        
        //Find median partition by X or Y, depending on whether we're using a vertical cut
        std::nth_element(ppVertices, ppVertices+middle, ppVertices+numVertices, useVertical ? Vertex::lessX : Vertex::lessY);

        //Recursive calls
        delaunay(ppVertices, middle, &pLdo, &pLdi, useAltCuts, useAltCuts ? !useVertical : useVertical);
        delaunay(ppVertices+middle, numVertices - middle, &pRdi, &pRdo, useAltCuts, useAltCuts ? !useVertical : useVertical);
        
        //Modify ldi be highest in bottom half and rdi to be lowest in top half
        if(!useVertical) {
            pBotMax = ppVertices[0];
            pTopMin = ppVertices[middle];
            pMin    = (*ppVertices[0] < *ppVertices[middle]) ? ppVertices[0] : ppVertices[middle];
            pMax    = (*ppVertices[0] > *ppVertices[middle]) ? ppVertices[0] : ppVertices[middle];;
            for(long i=1; i < middle; i++) {
                if(*ppVertices[i] < *pMin) {
                    pMin = ppVertices[i];
                }
                else if(*ppVertices[i] > *pMax) {
                    pMax = ppVertices[i];
                }
                
                if(ppVertices[i]->gtY(*pBotMax)) {
                    pBotMax = ppVertices[i];
                }
            }
            for(long i=middle+1; i < numVertices; i++) {
                if(*ppVertices[i] < *pMin) {
                    pMin = ppVertices[i];
                }
                else if(*ppVertices[i] > *pMax) {
                    pMax = ppVertices[i];
                }
                
                if(ppVertices[i]->ltY(*pTopMin)) {
                    pTopMin = ppVertices[i];
                }
            }
            
            pLdi = pBotMax->getCWHullEdge();
            pRdi = pTopMin->getCCWHullEdge();
        }
        
        
        //Compute the lower common tangent of two sets of vertices
        while (1) {
            if(pLdi->leftOf(pRdi->Org())) {
                pLdi = pLdi->Lnext();
            }
            else if(pRdi->rightOf(pLdi->Org())) {
                pRdi = pRdi->Rprev();
            }
            else {
                break;
            }
        }

        // Create a first cross edge pBasel from pRdi.origin to pLdi.origin
        Edge *pBasel = Edge::connect(pRdi->Sym(), pLdi);

        if(pLdi->Org() == pLdo->Org()) {
            pLdo = pBasel->Sym();
        }
        if(pRdi->Org() == pRdo->Org()) {
            pRdo = pBasel;
        }

        //Merging two halves
        while(1) {
            //Locate the first Left point pLcand to be encou
        	Edge *pLcand = pBasel->Sym()->Onext();
            bool leftFinished  = !pLcand->valid(pBasel);
            if(!leftFinished) {
                while(incircle(pBasel->Dest()->Pos(), 
                               pBasel->Org()->Pos(), 
                               pLcand->Dest()->Pos(),  
                               pLcand->Onext()->Dest()->Pos()) > 0) {
                
                    Edge *pT = pLcand->Onext();
                    Edge::deleteEdge(pLcand);
                    pLcand = pT;
                }
            }
            
            //Symmetrically locate the first R point to be hit
            Edge *pRcand = pBasel->Oprev();
            bool rightFinished = !pRcand->valid(pBasel);
            if(!rightFinished) {
                while(incircle(pBasel->Dest()->Pos(), 
                               pBasel->Org()->Pos(), 
                               pRcand->Dest()->Pos(),  
                               pRcand->Oprev()->Dest()->Pos()) > 0) {
                
                    Edge *pT = pRcand->Oprev();
                    Edge::deleteEdge(pRcand);
                    pRcand = pT;
                }
            }

            //both are invalid, pBasel is upper common tangent
            if(leftFinished && rightFinished) {
                break;
            }
            //the next cross edge is to be connected to either pLcand.dest or pRcand.dest
            if(leftFinished || 
               (!rightFinished && incircle(pLcand->Dest()->Pos(), 
                                                  pLcand->Org()->Pos(), 
                                                  pRcand->Org()->Pos(), 
                                                  pRcand->Dest()->Pos()) > 0)) {
                pBasel = Edge::connect(pRcand, pBasel->Sym());
            }
            else {
                pBasel = Edge::connect(pBasel->Sym(), pLcand->Sym());
            }
        }

        //Modify pLdo and pRdo if we merging a horizontal cut
        if(!useVertical) {
            pLdo = pMin->getCCWHullEdge();
            pRdo = pMax->getCWHullEdge();
        }

        *ppLe = pLdo;
        *ppRe = pRdo;
    }
    return;
}
Exemplo n.º 15
0
Edge *Subdivision::spoke(Vec2& x, Edge *e)
{
    Triangle *new_faces[4];
    int facedex = 0;

    //
    // NOTE: e is the edge returned by locate(x)
    //

    if ( (x == e->Org()) || (x == e->Dest()) ) {
        // point is already in the mesh
	//
#ifdef _INC_IOSTREAM
        std::cerr << "WARNING: Tried to reinsert point: " << x << std::endl;
	std::cerr << "         org: " << e->Org() << std::endl;
	std::cerr << "        dest: " << e->Dest() << std::endl;
#endif
        return NULL;
    }

    Edge *boundary_edge = NULL;

    Triangle *lface = e->Lface();
    lface->dontAnchor(e);
    new_faces[facedex++] = lface;

    if( onEdge(x,e) )
    {
	if( ccwBoundary(e) ) {
	    //
	    // e lies on the boundary
	    // Defer deletion until after new edges are added.
	    boundary_edge = e;
	}
	else {
	    Triangle *sym_lface = e->Sym()->Lface();
	    new_faces[facedex++] = sym_lface;
	    sym_lface->dontAnchor(e->Sym());


	    e = e->Oprev();
	    deleteEdge(e->Onext());
	}
    }
    else
    {
	// x lies within the Lface of e
    }

    Edge *base = makeEdge(e->Org(), x.clone());

    splice(base, e);

    startingEdge = base;
    do {
	base = connect(e, base->Sym());
	e = base->Oprev();
    } while( e->Lnext() != startingEdge );

    if( boundary_edge )
	deleteEdge(boundary_edge);

    // Update all the faces in our new spoked polygon.
    // If point x on perimeter, then don't add an exterior face


    base = boundary_edge ? startingEdge->Rprev() : startingEdge->Sym();
    do {
	if( facedex )
	    new_faces[--facedex]->reshape(base);
	else
	    makeFace(base);

	base = base->Onext();

    } while( base != startingEdge->Sym() );

    return startingEdge;
}