Posted to tcl by hypnotoad at Fri Jan 22 16:28:19 GMT 2016view raw
- /*
- ** This file implements a TCL object that keeps track of the walls and
- ** bulkheads on a single deck of a ship.
- **
- ** This widget assumes a right-handed coordinate system if zoom is positive
- ** and a left-handed coordinate system is zoom is negative. The Tk canvas
- ** widget uses a left-handed coordinate system all the time. The READI
- ** database uses a right-handed coordinate system all the time. This module
- ** can be used to translate by setting zoom to +1.0 for database I/O and
- ** to -$g(zoom) for canvas I/O.
- **
- ** This module uses a purely 2-D model. It can only handle a single
- ** deck at a time. If a multi-deck model needs to be displayed then
- ** that multi-deck model should first be flattened into a stack of
- ** individual decks in the same plane using the separate "slicer" object.
- **
- ** This file implements a single new constructor tcl command named "wallset".
- ** The wallset command creates a new wallset object. Methods on this
- ** wallset object are used to manage the object.
- **
- ** The details of the various methods and what they do are provided in
- ** header comments above the implementation of each method.
- */
- #include "odielibInt.h"
- #include <tclOO.h>
- #include <stdarg.h>
- #include <stdlib.h>
- #include <assert.h>
- #include <string.h>
- #include <math.h>
- #ifndef M_PI
- # define M_PI 3.1415926535898
- #endif
- /*
- ** Remove all of the ComptBox entries from the wallset.
- */
- static void clearComptBoxCache(Wallset *pWS){
- ComptBox *p = pWS->pComptBox;
- while( p ){
- ComptBox *pNext = p->pNext;
- Odie_Free((char *)p);
- p = pNext;
- }
- pWS->pComptBox = 0;
- }
- /*
- ** This routine is invoked when the TCL command that implements a
- ** wallset is deleted. Free all memory associated with that
- ** wallset.
- */
- static void destroyWallset(void *pArg){
- Wallset *p = (Wallset*)pArg;
- Link *pLink = p->pAll;
- clearComptBoxCache(p);
- while( pLink ){
- Segment *pSeg = pLink->pLinkNode;
- pLink = pSeg->pAll.pNext;
- Odie_Free((char *) pSeg );
- }
- Odie_Free((char *) p );
- }
- /*
- ** Clear the Segment.ignore flag on all segments within a wallset.
- */
- static void ignoreNone(Wallset *p){
- #if 0
- Link *pLink;
- for(pLink=p->pAll; pLink; pLink=pLink->pNext){
- pLink->pSeg->ignore = 0;
- }
- #endif
- }
- /*
- ** Return a pointer to the segment with the given ID. Return NULL
- ** if there is no such segment.
- */
- static Segment *findSegment(Wallset *p, int id){
- int h;
- Link *pLink;
- h = hashInt(id);
- for(pLink = p->hashId[h]; pLink; pLink=pLink->pNext){
- Segment *pSeg=pLink->pLinkNode;
- if( pSeg->id==id ) return pSeg;
- }
- return 0;
- }
- /*
- ** Scan all segments looking for the vertex or vertices that are nearest
- ** to x,y. Return a pointer to a Segment.set that is the list of matching
- ** segments. Also write the nearest point into *pX,*pY.
- **
- ** The returned list uses the Segment.set link.
- */
- static Link *nearestVertex(
- Wallset *p, /* The wallset to be scanned */
- double x, double y, /* Search for points near to this point */
- double *pX, double *pY /* Write nearest vertex here */
- ){
- double nx, ny;
- double min = -1.0;
- Link *pList = 0;
- Link *pI;
- x = roundCoord(x);
- y = roundCoord(y);
- for(pI=p->pAll; pI; pI=pI->pNext){
- double dx, dy, dist;
- Segment *pSeg = pI->pLinkNode;
- dx = x - pSeg->from[X_IDX];
- dy = y - pSeg->from[Y_IDX];
- dist = dx*dx + dy*dy;
- if( min<0.0 || dist<=min ){
- if( min<0.0 || nx!=pSeg->from[X_IDX] || ny!=pSeg->from[Y_IDX] ){
- pList = 0;
- nx = pSeg->from[X_IDX];
- ny = pSeg->from[Y_IDX];
- min = dist;
- }
- LinkInit(pSeg->pSet, pSeg);
- LinkInsert(&pList, &pSeg->pSet);
- }
- dx = x - pSeg->to[X_IDX];
- dy = y - pSeg->to[Y_IDX];
- dist = dx*dx + dy*dy;
- if( dist<=min ){
- if( nx!=pSeg->to[X_IDX] || ny!=pSeg->to[Y_IDX] ){
- pList = 0;
- nx = pSeg->to[X_IDX];
- ny = pSeg->to[Y_IDX];
- min = dist;
- }
- LinkInit(pSeg->pSet, pSeg);
- LinkInsert(&pList, &pSeg->pSet);
- }
- }
- *pX = nx;
- *pY = ny;
- return pList;
- }
- /*
- ** Scan all segments looking for the point on a segment that is nearest
- ** to x,y. Return a pointer to a Segment.set that is the list of matching
- ** segments. This set might contain multiple members if the nearest point
- ** is actually a vertex shared by two or more segments. Write the nearest
- ** point into *pX, *pY.
- **
- ** /// Ignore any segment that has its Segment.ignore flag set. -- removed
- **
- ** The returned list uses the Segment.set list.
- */
- static Link *nearestPoint(
- Wallset *p, /* The wallset to be scanned */
- double x, double y, /* Search for points near to this point */
- double *pX, double *pY /* Write nearest vertex here */
- ){
- double nx, ny;
- double min = -1.0;
- Link *pList = 0;
- Link *pI;
- x = roundCoord(x);
- y = roundCoord(y);
- for(pI=p->pAll; pI; pI=pI->pNext){
- double dx, dy, dist;
- Segment *pSeg;
- double acx, acy; /* Vector from x0,y0 to x,y */
- double abx, aby; /* Vector from x0,y0 to x1,y1 */
- double rx, ry; /* Nearest point on x0,y0->to[X_IDX],y1 to x,y */
- double r;
- pSeg = pI->pLinkNode;
- /* if( pSeg->ignore ) continue; */
- acx = x - pSeg->from[X_IDX];
- acy = y - pSeg->from[Y_IDX];
- abx = pSeg->to[X_IDX] - pSeg->from[X_IDX];
- aby = pSeg->to[Y_IDX] - pSeg->from[Y_IDX];
- r = (acx*abx + acy*aby)/(abx*abx + aby*aby);
- if( r<=0 ){
- rx = pSeg->from[X_IDX];
- ry = pSeg->from[Y_IDX];
- }else if( r>=1 ){
- rx = pSeg->to[X_IDX];
- ry = pSeg->to[Y_IDX];
- }else{
- rx = pSeg->from[X_IDX] + abx*r;
- ry = pSeg->from[Y_IDX] + aby*r;
- }
- rx = roundCoord(rx);
- ry = roundCoord(ry);
- dx = x - rx;
- dy = y - ry;
- dist = dx*dx + dy*dy;
- if( min<0.0 || dist<=min ){
- if( min<0.0 || nx!=rx || ny!=ry ){
- pList = 0;
- nx = rx;
- ny = ry;
- min = dist;
- }
- LinkInit(pSeg->pSet, pSeg);
- LinkInsert(&pList, &pSeg->pSet);
- }
- }
- *pX = nx;
- *pY = ny;
- return pList;
- }
- /*
- ** Return TRUE if the value x is in between x1 and x2.
- */
- static int between(double x, double x1, double x2){
- if( x1<x2 ){
- return x>=x1 && x<=x2;
- }else{
- return x>=x2 && x<=x1;
- }
- }
- /*
- ** Return TRUE if the given segment is on the given list
- */
- static int segmentOnList(Segment *pSeg, Link *pList){
- while( pList ){
- if( pList->pLinkNode==pSeg ) return 1;
- pList = pList->pNext;
- }
- return 0;
- }
- /*
- ** Return a list of all segments which have an end at the given vertex.
- ** The returned list uses Segment.set
- */
- static Link *segmentsAtVertex(Wallset *p, double x, double y){
- Link *pList = 0;
- Link *pI;
- int h;
- x = roundCoord(x);
- y = roundCoord(y);
- h = hashCoord(x, y);
- for(pI=p->hashFrom[h]; pI; pI=pI->pNext){
- Segment *pSeg = pI->pLinkNode;
- /* if( pSeg->ignore ) continue; */
- if( floatCompare(x, pSeg->from[X_IDX])==0 && floatCompare(y, pSeg->from[Y_IDX])==0 ){
- assert( !segmentOnList(pSeg, pList) );
- LinkInit(pSeg->pSet, pSeg);
- LinkInsert(&pList, &pSeg->pSet);
- }
- }
- for(pI=p->hashTo[h]; pI; pI=pI->pNext){
- Segment *pSeg = pI->pLinkNode;
- /* if( pSeg->ignore ) continue; */
- if( floatCompare(x, pSeg->to[X_IDX])==0 && floatCompare(y, pSeg->to[Y_IDX])==0 ){
- assert( !segmentOnList(pSeg, pList) );
- LinkInit(pSeg->pSet, pSeg);
- LinkInsert(&pList, &pSeg->pSet);
- }
- }
- return pList;
- }
- /*
- ** The point xV,yV is a vertex in the wallset. This routine locates
- ** a segment connected to that vertex which is the first segment in
- ** a clockwise direction from xR,yR->xV,yV. A pointer to the segment
- ** is written into *ppSeg. If the output segment moves backwards
- ** (in other words if x1,y1 of the segment is connected at xV,yV)
- ** then *pfBack is true.
- **
- ** If a suitable segment is found, 0 is returned. Non-zero is returned
- ** if no suitable segment could be found.
- **
- ** This routine uses the Segment.set list internally.
- */
- static int nextCwSegment(
- Wallset *p, /* The wallset */
- double xR, double yR, /* Remote end of input segment */
- double xV, double yV, /* Vertex (near end of input segment) */
- Segment **ppSeg, /* OUT: First segment clockwise from xR,yR->xV,yV */
- int *pfBack /* OUT: True if output segment goes backwards */
- ){
- Link *pList, *pI;
- double rRef, rBest;
- int i, nSeg, iBest;
- Segment *pSeg;
- struct {
- Segment *pSeg;
- int isBack;
- double rAngle;
- } *aSeg, aSegStatic[20];
- /* Find all segments at xV,yV */
- pList = segmentsAtVertex(p, xV, yV);
- for(pI=pList, nSeg=0; pI; nSeg++, pI=pI->pNext){}
- if( nSeg==0 ) return 1;
- if( nSeg<=sizeof(aSegStatic)/sizeof(aSegStatic[0]) ){
- aSeg = aSegStatic;
- }else{
- aSeg = (void *)Odie_Alloc( nSeg*sizeof(*aSeg) );
- }
- for(pI=pList, i=0; pI; i++, pI=pI->pNext){
- aSeg[i].pSeg = pSeg = pI->pLinkNode;
- aSeg[i].isBack = floatCompare(xV, pSeg->to[X_IDX])==0
- && floatCompare(yV, pSeg->to[Y_IDX])==0;
- }
- /* Find the reference angle */
- rRef = atan2(yR-yV, xR-xV)*180.0/M_PI;
- /* Find angles on all segments */
- for(i=0; i<nSeg; i++){
- pSeg = aSeg[i].pSeg;
- if( aSeg[i].isBack ){
- aSeg[i].rAngle = atan2(pSeg->from[Y_IDX]-pSeg->to[Y_IDX], pSeg->from[X_IDX]-pSeg->to[X_IDX])*180.0/M_PI;
- }else{
- aSeg[i].rAngle = atan2(pSeg->to[Y_IDX]-pSeg->from[Y_IDX], pSeg->to[X_IDX]-pSeg->from[X_IDX])*180.0/M_PI;
- }
- }
- /* Subtract 360 to any segment angle that is less than the reference angle */
- for(i=0; i<nSeg; i++){
- if( aSeg[i].rAngle<rRef ) aSeg[i].rAngle += 360;
- }
- /* Choose the segment with the largest angle */
- rBest = aSeg[0].rAngle;
- iBest = 0;
- for(i=1; i<nSeg; i++){
- if( aSeg[i].rAngle>rBest ){
- iBest = i;
- rBest = aSeg[i].rAngle;
- }
- }
- *ppSeg = aSeg[iBest].pSeg;
- *pfBack = aSeg[iBest].isBack;
- if( aSeg!=aSegStatic ){
- Odie_Free((char *) aSeg );
- }
- return 0;
- }
- /*
- ** Consider a line beginning at x0,y0 then going from x1,y1 to x2,y2.
- ** x1,y1 is an elbow in the line. This routine returns -1 if the
- ** elbow bends to the right, and +1 if it bends to the left. zero is
- ** returned if the elbow does not bend at all.
- */
- static int bendDirection(
- double x0, double y0,
- double x1, double y1,
- double x2, double y2
- ){
- /* Algorithm: Rotate x0,y0->to[X_IDX],y1 90 degrees counter-clockwise. Take
- ** the dot product with x1,y1->x2,y2. The dot produce will be the product
- ** of two (non-negative) magnitudes and the cosine of the angle. So if
- ** the dot product is positive, the bend is to the left, or to the right if
- ** the dot product is negative.
- */
- double r = (y0-y1)*(x2-x1) + (x1-x0)*(y2-y1);
- return r<0.0 ? +1 : (r>0.0 ? -1 : 0);
- }
- /*
- ** Given an interior point xI,yI, this routine finds a segment on the
- ** boundary that contains the interior point. That segment is returned
- ** in *ppSeg. *pfLeft is set to true if the interior point is to the left
- ** of the segment and false if it is to the right.
- **
- ** Zero is returned on success. Non-zero is returned if no suitable
- ** boundary could be located. Non-zero might be returned, for example,
- ** if xI,yI is positioned directly on top of a wall or if there are no
- ** walls in the wallset.
- **
- ** // Any segment marked with Segment.ignore is ignored for purposes of
- ** // this routine. -- removed
- **
- ** This routine uses the Segment.set list internally.
- */
- static int firstBoundarySegment(
- Wallset *p, /* The wallset */
- double xI, double yI, /* An interior point */
- Segment **ppSeg, /* OUT: A segment on the boundary containing xI,yI */
- int *pfLeft /* OUT: True if xI,yI is to the left side *ppSeg */
- ){
- Link *pList;
- double xN, yN;
- /* Find nearest point, xN,yN */
- pList = nearestPoint(p, xI, yI, &xN, &yN);
- if( pList==0 ) return 1;
- if( pList->pNext ){
- /* xN,yN is a vertex...
- ** Locate the first segment clockwise from xI,yI->xN,yN and return
- */
- return nextCwSegment(p, xI, yI, xN, yN, ppSeg, pfLeft);
- }else{
- /* xN,yN is a point on single line segment...
- */
- Segment *pSeg;
- pSeg = *ppSeg = pList->pLinkNode;
- *pfLeft = bendDirection(pSeg->from[X_IDX], pSeg->from[Y_IDX], xN, yN, xI, yI)>0;
- }
- return 0;
- }
- /*
- ** Fill the given Boundary array with a list of segments (with
- ** Segment.ignore set to false) that form a closed circuit. The
- ** first entry in aBound[] has already been filled in by the
- ** calling function and is used to seed the search.
- **
- ** At most nBound slots in aBound[] will be used. The return value
- ** is the number of slots in aBound[] that would have been used if those
- ** slots had been available. A return of 0 indicates that no boundary
- ** is available.
- **
- ** If the checkIsPrimary flag is true and the aBound[0] entry is not
- ** the primary segment for the compartment, then the aBound[] is not
- ** completely filled in and the routine returns 0;
- */
- static int completeBoundary(
- Wallset *p, /* The wallset */
- int checkIsPrimary, /* Abort if aBound[0] is not the primary segment */
- int nBound, /* Number of slots available in aBound[] */
- Boundary *aBound /* IN-OUT: Write results into aBound[1...] */
- ){
- int cnt = 1;
- Segment *pSeg, *pS;
- int isLeft;
- int isBack;
- double xR, yR, xV, yV;
- pS = pSeg = aBound[0].pSeg;
- isLeft = aBound[0].backwards;
- if( !isLeft ){
- xR = pSeg->from[X_IDX];
- yR = pSeg->from[Y_IDX];
- xV = pSeg->to[X_IDX];
- yV = pSeg->to[Y_IDX];
- }else{
- xV = pSeg->from[X_IDX];
- yV = pSeg->from[Y_IDX];
- xR = pSeg->to[X_IDX];
- yR = pSeg->to[Y_IDX];
- }
- while( nextCwSegment(p,xR,yR,xV,yV,&pS,&isBack)==0 &&
- (isBack!=isLeft || pS!=pSeg) ){
- if( checkIsPrimary ){
- if( pS->id<pSeg->id ) return 0;
- if( pS->id==pSeg->id && !isLeft ) return 0;
- }
- if( isBack ){
- xV = pS->from[X_IDX];
- yV = pS->from[Y_IDX];
- xR = pS->to[X_IDX];
- yR = pS->to[Y_IDX];
- }else{
- xR = pS->from[X_IDX];
- yR = pS->from[Y_IDX];
- xV = pS->to[X_IDX];
- yV = pS->to[Y_IDX];
- }
- if( nBound>cnt ){
- aBound[cnt].pSeg = pS;
- aBound[cnt].backwards = isBack;
- }
- cnt++;
- if( cnt>1000 /* 00 */ ) return -cnt; /* Avoid an infinite loop */
- }
- return cnt;
- }
- /*
- ** Compute the "spin" on a boundary. A positive value means the
- ** circulation is to counter-clockwise and a negative value means the
- ** circulation is clockwise. For boundaries, a positive
- ** value means the region is internal and a negative value means
- ** the region is external.
- */
- static double spin(Boundary *aBound, int nBound){
- double sum = 0;
- int i;
- for(i=0; i<nBound; i++){
- double x0, y0, x1, y1;
- double dx, dy;
- Segment *pSeg = aBound->pSeg;
- if( aBound->backwards ){
- x0 = pSeg->to[X_IDX];
- y0 = pSeg->to[Y_IDX];
- x1 = pSeg->from[X_IDX];
- y1 = pSeg->from[Y_IDX];
- }else{
- x0 = pSeg->from[X_IDX];
- y0 = pSeg->from[Y_IDX];
- x1 = pSeg->to[X_IDX];
- y1 = pSeg->to[Y_IDX];
- }
- aBound++;
- dx = x1-x0;
- dy = y1-y0;
- sum += x0*dy - y0*dx;
- }
- return sum;
- }
- /*
- ** The input is two linked lists of ComptBox structures where each
- ** list is sorted by increasing area. Combine these two lists into
- ** a single sorted linked list.
- */
- static ComptBox *mergeComptBox(ComptBox *p1, ComptBox *p2){
- ComptBox head;
- ComptBox *pTail = &head;
- ComptBox *p;
- while( p1 && p2 ){
- if( p1->area<=p2->area ){
- p = p1->pNext;
- pTail->pNext = p1;
- pTail = p1;
- p1 = p;
- }else{
- p = p2->pNext;
- pTail->pNext = p2;
- pTail = p2;
- p2 = p;
- }
- }
- if( p1 ){
- pTail->pNext = p1;
- }else{
- pTail->pNext = p2;
- }
- return head.pNext;
- }
- /*
- ** Construct the ComptBox cache. For each compartment (where a compartment
- ** is a closed circuit of Segments) make an entry on the Wallset.pComptBox
- ** list.
- **
- ** If the ComptBox cache already exists, this routine is a no-op.
- */
- static void buildComptBoxCache(Wallset *p){
- Link *pI;
- int i;
- ComptBox *aSort[30];
- /* Return immediately if the cache already exists */
- if( p->pComptBox ) return;
- /* Compute a linked list of all compartment boxes */
- for(pI=p->pAll; pI; pI=pI->pNext){
- int i, j, n;
- Boundary aBound[1000];
- aBound[0].pSeg = pI->pLinkNode;
- for(j=0; j<2; j++){
- aBound[0].backwards = j;
- n = completeBoundary(p, 1, sizeof(aBound)/sizeof(aBound[0]), aBound);
- if( n>0 && spin(aBound,n)>0.0 ){
- double dx, dy;
- Segment *pSeg = pI->pLinkNode;
- ComptBox *pNew = (ComptBox *)Odie_Alloc( sizeof(*pNew) );
- pNew->pNext = p->pComptBox;
- pNew->bbox.l = pNew->bbox.r = pSeg->from[X_IDX];
- pNew->bbox.t = pNew->bbox.b = pSeg->from[Y_IDX];
- pNew->prim = aBound[0];
- for(i=1; i<n; i++){
- Segment *pSeg = aBound[i].pSeg;
- if( pSeg->from[X_IDX]<pNew->bbox.l ) pNew->bbox.l = pSeg->from[X_IDX];
- if( pSeg->from[X_IDX]>pNew->bbox.r ) pNew->bbox.r = pSeg->from[X_IDX];
- if( pSeg->from[Y_IDX]<pNew->bbox.b ) pNew->bbox.b = pSeg->from[Y_IDX];
- if( pSeg->from[Y_IDX]>pNew->bbox.t ) pNew->bbox.t = pSeg->from[Y_IDX];
- if( pSeg->to[X_IDX]<pNew->bbox.l ) pNew->bbox.l = pSeg->to[X_IDX];
- if( pSeg->to[X_IDX]>pNew->bbox.r ) pNew->bbox.r = pSeg->to[X_IDX];
- if( pSeg->to[Y_IDX]<pNew->bbox.b ) pNew->bbox.b = pSeg->to[Y_IDX];
- if( pSeg->to[Y_IDX]>pNew->bbox.t ) pNew->bbox.t = pSeg->to[Y_IDX];
- }
- dx = pNew->bbox.r - pNew->bbox.l;
- dy = pNew->bbox.t - pNew->bbox.b;
- pNew->area = sqrt(dx*dx+dy*dy);
- p->pComptBox = pNew;
- }
- }
- }
- /* Sort the list into order of increasing area */
- for(i=0; i<sizeof(aSort)/sizeof(aSort[0]); i++) aSort[i] = 0;
- while( p->pComptBox ){
- ComptBox *pBox = p->pComptBox;
- p->pComptBox = pBox->pNext;
- pBox->pNext = 0;
- for(i=0; i<sizeof(aSort)/sizeof(aSort[0])-1 && aSort[i]!=0; i++){
- pBox = mergeComptBox(aSort[i], pBox);
- aSort[i] = 0;
- }
- aSort[i] = mergeComptBox(aSort[i], pBox);
- }
- for(i=0; i<sizeof(aSort)/sizeof(aSort[0]); i++){
- p->pComptBox = mergeComptBox(aSort[i], p->pComptBox);
- }
- }
- /*
- ** Test to see if the point x,y is contained within the given
- ** boundary or is on the outside of the boundary.
- */
- static int pointWithinBoundary(
- Boundary *aBound, /* The boundary */
- int nBound, /* Number of segments in the boundary */
- double x, double y /* The point to test */
- ){
- int inside = 0;
- int i;
- for(i=0; i<nBound; i++){
- double x0, y0, x1, y1;
- Segment *p = aBound[i].pSeg;
- x0 = p->from[X_IDX];
- y0 = p->from[Y_IDX];
- x1 = p->to[X_IDX];
- y1 = p->to[Y_IDX];
- if( x0==x1 ) continue;
- if( (x0>x && x1>x) || (x0<x && x1<x) ) continue;
- if( y1 - (x1-x)*(y1-y0)/(x1-x0) >= y ) inside = !inside;
- }
- return inside;
- }
- /*
- ** Find a boundary which contains xI, yI. If the size of the boundary
- ** is set to 0, that means no such boundary exists.
- */
- static int findBoundary(
- Wallset *p, /* The wallset */
- double xI, double yI, /* A point that the boundary should be near */
- int nBound, /* Number of slots available in aBound[] */
- Boundary *aBound /* OUT: Write results here */
- ){
- int n = 0;
- ComptBox *pBox;
- buildComptBoxCache(p);
- for(pBox=p->pComptBox; pBox; pBox=pBox->pNext){
- if( xI<pBox->bbox.l || xI>pBox->bbox.r || yI<pBox->bbox.b || yI>pBox->bbox.t ) continue;
- aBound[0] = pBox->prim;
- n = completeBoundary(p, 0, nBound, aBound);
- if( n>0 && pointWithinBoundary(aBound, n, xI, yI) ) break;
- n = 0;
- }
- return n;
- }
- /*
- ** Do an check of the integrity of the internal data structures. If
- ** a problem is found, leave an error message in interp->result and
- ** return TCL_ERROR. Return TCL_OK if everything is OK.
- */
- static int selfCheck(Tcl_Interp *interp, Wallset *p){
- Link *pLink;
- Segment *pSeg;
- int h;
- char zErr[200];
- for(pLink=p->pAll; pLink; pLink=pLink->pNext){
- pSeg = pLink->pLinkNode;
- h = hashInt(pSeg->id);
- if(!segmentOnList(pSeg, p->hashId[h]) ){
- sprintf(zErr, "segment %d missing from hashId[%d]", pSeg->id, h);
- Tcl_SetResult(interp, zErr, TCL_VOLATILE);
- return TCL_ERROR;
- }
- h = hashCoord(pSeg->from[X_IDX], pSeg->from[Y_IDX]);
- if(!segmentOnList(pSeg, p->hashFrom[h]) ){
- sprintf(zErr, "segment %d missing from hashFrom[%d]", pSeg->id, h);
- Tcl_SetResult(interp, zErr, TCL_VOLATILE);
- return TCL_ERROR;
- }
- h = hashCoord(pSeg->to[X_IDX], pSeg->to[Y_IDX]);
- if(!segmentOnList(pSeg, p->hashTo[h]) ){
- sprintf(zErr, "segment %d missing from hashTo[%d]", pSeg->id, h);
- Tcl_SetResult(interp, zErr, TCL_VOLATILE);
- return TCL_ERROR;
- }
- }
- return TCL_OK;
- }
- static void Wallset_Delete(ClientData clientData) {
- Wallset *p = (Wallset *)clientData;
- Link *pLink = p->pAll;
- clearComptBoxCache(p);
- while( pLink ){
- Segment *pSeg = pLink->pLinkNode;
- pLink = pSeg->pAll.pNext;
- Odie_Free((char *) pSeg );
- }
- Odie_Free((char *) p );
- }
- static int Wallset_Clone(
- Tcl_Interp* interp, /* Tcl interpreter for error reporting */
- ClientData metadata, /* Metadata to be cloned */
- ClientData* newMetaData /* Where to put the cloned metadata */
- ) {
- Tcl_SetObjResult(interp,
- Tcl_NewStringObj("WALLSETs are not clonable", -1));
- /* For now... */
- return TCL_ERROR;
- }
- const static Tcl_ObjectMetadataType WallsetDataType = {
- TCL_OO_METADATA_VERSION_CURRENT,
- "Wallset",
- Wallset_Delete,
- Wallset_Clone
- };
- /*
- ** The maximum number of segments in a boundary
- */
- #define MX_BOUND 1000
- /*
- ** This routine runs when a method is executed against a wallset.
- */
- #define GETWALLSET(OBJCONTEXT) (Wallset *) Tcl_ObjectGetMetadata(Tcl_ObjectContextObject(objectContext),&WallsetDataType)
- static int Wallset_Method_atvertex(ClientData clientData ,Tcl_Interp *interp ,Tcl_ObjectContext objectContext ,int objc ,Tcl_Obj *const *objv)
- {
- /*
- ** tclmethod: WALLSET atvertex X Y
- ** title: Return a list of wall IDs that connect to vertex X,Y
- */
- Wallset *p = GETWALLSET(objectContext);
- Link *pList;
- double x, y;
- Tcl_Obj *pResult;
- if( objc!=4 ){
- Tcl_WrongNumArgs(interp, 1, objv, "X Y");
- return TCL_ERROR;
- }
- if( Tcl_GetDoubleFromObj(interp, objv[1], &x) ) return TCL_ERROR;
- if( Tcl_GetDoubleFromObj(interp, objv[2], &y) ) return TCL_ERROR;
- pResult = Tcl_NewObj();
- ignoreNone(p);
- pList = segmentsAtVertex(p, x*p->rXZoom, y*p->rYZoom);
- while( pList ){
- Segment *pSeg=pList->pLinkNode;
- Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(pSeg->id));
- pList = pList->pNext;
- }
- Tcl_SetObjResult(interp, pResult);
- return TCL_OK;
- }
- static int Wallset_Method_boundary(ClientData clientData ,Tcl_Interp *interp ,Tcl_ObjectContext objectContext ,int objc ,Tcl_Obj *const *objv)
- {
- /*
- ** tclmethod: WALLSET boundary X Y
- ** title: Return indices of segments forming a boundary around X Y
- */
- Wallset *p = GETWALLSET(objectContext);
- Boundary aBound[MX_BOUND];
- int nBound;
- double x, y;
- Tcl_Obj *pResult;
- int i;
- int showDetail = 0;
- if( objc==5 && strcmp(Tcl_GetStringFromObj(objv[1],0),"-detail")==0 ){
- showDetail = 1;
- objc--;
- objv++;
- }
- if( objc!=4 ){
- Tcl_WrongNumArgs(interp, 1, objv, "?-detail? X Y");
- return TCL_ERROR;
- }
- if( Tcl_GetDoubleFromObj(interp, objv[1], &x) ) return TCL_ERROR;
- if( Tcl_GetDoubleFromObj(interp, objv[2], &y) ) return TCL_ERROR;
- nBound = findBoundary(p, x*p->rXZoom, y*p->rYZoom, MX_BOUND, aBound);
- if( nBound>MX_BOUND ) nBound = 0;
- pResult = Tcl_NewObj();
- for(i=0; i<nBound; i++){
- Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(aBound[i].pSeg->id));
- if( showDetail ){
- Tcl_ListObjAppendElement(0, pResult,
- ODIE_CONSTANT_STRING(aBound[i].backwards ? "right" : "left"));
- }
- }
- Tcl_SetObjResult(interp, pResult);
- return TCL_OK;
- }
- static int Wallset_Method_closure(ClientData clientData ,Tcl_Interp *interp ,Tcl_ObjectContext objectContext ,int objc ,Tcl_Obj *const *objv)
- {
- /*
- ** tclmethod: WALLSET closure WALLID BACKWARDS CHECKPRIMARY ?-coords?
- ** title: Return the closure of a wall
- **
- ** The closure is a path of walls going clockwise from the wall given.
- ** The return value is a list consisting of wall IDs alternating with
- ** keywords "left" or "right" indicating which side of the wall applies.
- ** If the CHECKPRIMARY flag is true and the WALLID/BACKWARDS is not the
- ** primary wall id for the closure, then return an empty string. The
- ** primary wall id is the wall id with the lowest id number, or if
- ** two walls in the closure have the same id, then the one that goes
- ** on the right side of the wall.
- */
- Wallset *p = GETWALLSET(objectContext);
- Boundary aBound[MX_BOUND];
- int id;
- int nBound, i, checkPrim;
- Tcl_Obj *pResult;
- int coordsFlag = 0;
- int noerrFlag = 0;
- if( objc!=4 && objc!=5 ){
- Tcl_WrongNumArgs(interp, 1, objv,
- "WALLID BACKWARDS CHECKPRIMARY ?-coords? ?-noerr?");
- return TCL_ERROR;
- }
- if( objc==5 ){
- const char *zOpt = Tcl_GetStringFromObj(objv[4],0);
- if( strcmp(zOpt,"-coords")==0 ){
- coordsFlag = 1;
- }else if( strcmp(zOpt,"-noerr")==0 ){
- noerrFlag = 1;
- }else{
- Tcl_AppendResult(interp, "unknown option: ", zOpt, 0);
- return TCL_ERROR;
- }
- }
- if( Tcl_GetIntFromObj(interp, objv[1], &id) ) return TCL_ERROR;
- if( (aBound[0].pSeg = findSegment(p, id))==0 ){
- Tcl_AppendResult(interp, "segment ",
- Tcl_GetStringFromObj(objv[1],0), " does not exist", 0);
- return TCL_ERROR;
- }
- if( Tcl_GetBooleanFromObj(interp, objv[2], &aBound[0].backwards) ){
- return TCL_ERROR;
- }
- if( Tcl_GetBooleanFromObj(interp, objv[3], &checkPrim) ){
- return TCL_ERROR;
- }
- ignoreNone(p);
- nBound = completeBoundary(p, checkPrim, MX_BOUND, aBound);
- pResult = Tcl_NewObj();
- if( nBound<0 && noerrFlag ) nBound = -nBound;
- for(i=0; i<nBound; i++){
- if( coordsFlag ){
- double x, y;
- Segment *pSeg = aBound[i].pSeg;
- if( aBound[i].backwards ){
- x = pSeg->to[X_IDX];
- y = pSeg->to[Y_IDX];
- }else{
- x = pSeg->from[X_IDX];
- y = pSeg->from[Y_IDX];
- }
- Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(x/p->rXZoom));
- Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(y/p->rYZoom));
- }else{
- Tcl_ListObjAppendElement(0, pResult,
- Tcl_NewIntObj(aBound[i].pSeg->id));
- Tcl_ListObjAppendElement(0, pResult,
- ODIE_CONSTANT_STRING(aBound[i].backwards ? "right" : "left"));
- }
- }
- Tcl_SetObjResult(interp, pResult);
- return TCL_OK;
- }
- static int Wallset_Method_comptlist(ClientData clientData ,Tcl_Interp *interp ,Tcl_ObjectContext objectContext ,int objc ,Tcl_Obj *const *objv)
- {
- /*
- ** tclmethod: WALLSET comptlist
- ** title: Return a list of all compartments
- **
- ** A compartment is a closed circuit of walls. This routine returns
- ** a list of all compartments. Each element of the list consists of
- ** the primary wall for the compartment followed by a bounding box
- ** for the compartment.
- */
- Wallset *p = GETWALLSET(objectContext);
- ComptBox *pBox;
- Tcl_Obj *pResult = Tcl_NewObj();
- buildComptBoxCache(p);
- for(pBox=p->pComptBox; pBox; pBox=pBox->pNext){
- Tcl_Obj *pElem = Tcl_NewObj();
- Tcl_ListObjAppendElement(0, pElem,Tcl_NewIntObj(pBox->prim.pSeg->id));
- Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pBox->prim.backwards));
- Tcl_ListObjAppendElement(0, pElem, Tcl_NewDoubleObj(pBox->bbox.l/p->rXZoom));
- Tcl_ListObjAppendElement(0, pElem, Tcl_NewDoubleObj(pBox->bbox.b/p->rYZoom));
- Tcl_ListObjAppendElement(0, pElem, Tcl_NewDoubleObj(pBox->bbox.r/p->rXZoom));
- Tcl_ListObjAppendElement(0, pElem, Tcl_NewDoubleObj(pBox->bbox.t/p->rYZoom));
- Tcl_ListObjAppendElement(0, pResult, pElem);
- }
- Tcl_SetObjResult(interp, pResult);
- return TCL_OK;
- }
- static int Wallset_Method_corners(ClientData clientData ,Tcl_Interp *interp ,Tcl_ObjectContext objectContext ,int objc ,Tcl_Obj *const *objv)
- {
- /*
- ** tclmethod: WALLSET corners X Y
- ** title: Return vertices of compartment containing X,Y
- */
- Wallset *p = GETWALLSET(objectContext);
- Boundary aBound[MX_BOUND];
- int nBound, i;
- double x, y;
- Tcl_Obj *pResult;
- if( objc!=4 ){
- Tcl_WrongNumArgs(interp, 1, objv, "X Y");
- return TCL_ERROR;
- }
- if( Tcl_GetDoubleFromObj(interp, objv[1], &x) ) return TCL_ERROR;
- if( Tcl_GetDoubleFromObj(interp, objv[2], &y) ) return TCL_ERROR;
- nBound = findBoundary(p, x*p->rXZoom, y*p->rYZoom, MX_BOUND, aBound);
- if( nBound>MX_BOUND ) nBound = 0;
- pResult = Tcl_NewObj();
- for(i=0; i<nBound; i++){
- Segment *pSeg = aBound[i].pSeg;
- if( aBound[i].backwards ){
- x = pSeg->to[X_IDX];
- y = pSeg->to[Y_IDX];
- }else{
- x = pSeg->from[X_IDX];
- y = pSeg->from[Y_IDX];
- }
- Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(x/p->rXZoom));
- Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(y/p->rYZoom));
- }
- Tcl_SetObjResult(interp, pResult);
- return TCL_OK;
- }
- static int Wallset_Method_delete(ClientData clientData ,Tcl_Interp *interp ,Tcl_ObjectContext objectContext ,int objc ,Tcl_Obj *const *objv)
- {
- /*
- ** tclmethod: WALLSET delete ID
- ** title: Delete a single segment of a wall given by ID
- */
- Wallset *p = GETWALLSET(objectContext);
- int id;
- Segment *pSeg;
- if( objc!=3 ){
- Tcl_WrongNumArgs(interp, 1, objv, "ID");
- return TCL_ERROR;
- }
- if( p->busy ){
- Tcl_AppendResult(interp, "cannot \"delete\" from within a \"foreach\"",0);
- return TCL_ERROR;
- }
- if( Tcl_GetIntFromObj(interp, objv[1], &id) ) return TCL_ERROR;
- if( (pSeg = findSegment(p, id))==0 ){
- Tcl_AppendResult(interp, "segment ",
- Tcl_GetStringFromObj(objv[1],0), " does not exist", 0);
- return TCL_ERROR;
- }
- clearComptBoxCache(p);
- LinkRemove(&pSeg->pAll);
- /* We intentionally do not remove pSeg->pSet because it might not be
- ** a well-formed list */
- LinkRemove(&pSeg->pHash);
- LinkRemove(&pSeg->pFrom);
- LinkRemove(&pSeg->pTo);
- Odie_Free((char *)pSeg);
- return TCL_OK;
- }
- static int Wallset_Method_firstboundary(ClientData clientData ,Tcl_Interp *interp ,Tcl_ObjectContext objectContext ,int objc ,Tcl_Obj *const *objv)
- {
- /*
- ** tclmethod: WALLSET firstboundary X Y
- ** title: Find a wall on the boundary of compartment containing X Y
- **
- ** Returns a list of two elements on success or an empty list if no
- ** suitable boundary could be found. The first element is the ID of a
- ** wall that forms part of the boundary for the compartment containing
- ** point X,Y. The second element is TRUE if X,Y is to the right of the
- ** wall and false if it is to the left.
- **
- ** The right/left designation assumes a right-handed coordinate system.
- */
- Wallset *p = GETWALLSET(objectContext);
- int isBack;
- Segment *pSeg;
- double x, y;
- int rc;
- if( objc!=4 ){
- Tcl_WrongNumArgs(interp, 1, objv, "X Y");
- return TCL_ERROR;
- }
- if( Tcl_GetDoubleFromObj(interp, objv[2], &x) ) return TCL_ERROR;
- if( Tcl_GetDoubleFromObj(interp, objv[3], &y) ) return TCL_ERROR;
- ignoreNone(p);
- rc = firstBoundarySegment(p, x*p->rXZoom, y*p->rYZoom, &pSeg, &isBack);
- if( rc==0 ){
- Tcl_Obj *pResult = Tcl_NewObj();
- Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(pSeg->id));
- Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(isBack));
- Tcl_SetObjResult(interp, pResult);
- }
- return TCL_OK;
- }
- static int Wallset_Method_foreach(ClientData clientData ,Tcl_Interp *interp ,Tcl_ObjectContext objectContext ,int objc ,Tcl_Obj *const *objv)
- {
- /*
- ** tclmethod: WALLSET foreach CODE
- ** title: Run CODE for each segment of the wallset
- */
- Wallset *p = GETWALLSET(objectContext);
- Link *pLink;
- int rc = TCL_OK;
- if( objc!=2 ){
- Tcl_WrongNumArgs(interp, 1, objv, "CODE");
- return TCL_ERROR;
- }
- p->busy++;
- for(pLink=p->pAll; pLink && rc==TCL_OK; pLink=pLink->pNext){
- Segment *pSeg = pLink->pLinkNode;
- Tcl_SetVar2Ex(interp, "x0", 0, Tcl_NewDoubleObj(pSeg->from[X_IDX]/p->rXZoom), 0);
- Tcl_SetVar2Ex(interp, "y0", 0, Tcl_NewDoubleObj(pSeg->from[Y_IDX]/p->rYZoom), 0);
- Tcl_SetVar2Ex(interp, "x1", 0, Tcl_NewDoubleObj(pSeg->to[X_IDX]/p->rXZoom), 0);
- Tcl_SetVar2Ex(interp, "y1", 0, Tcl_NewDoubleObj(pSeg->to[Y_IDX]/p->rYZoom), 0);
- Tcl_SetVar2Ex(interp, "id", 0, Tcl_NewIntObj(pSeg->id), 0);
- Tcl_SetVar2Ex(interp, "lc", 0, Tcl_NewIntObj(pSeg->idLC), 0);
- Tcl_SetVar2Ex(interp, "rc", 0, Tcl_NewIntObj(pSeg->idRC), 0);
- Tcl_SetVar2Ex(interp, "virtual", 0, Tcl_NewIntObj(pSeg->isBoundary), 0);
- rc = Tcl_EvalObjEx(interp, objv[1], 0);
- }
- if( rc==TCL_BREAK ) rc = TCL_OK;
- p->busy--;
- return rc;
- }
- static int Wallset_Method_info(ClientData clientData ,Tcl_Interp *interp ,Tcl_ObjectContext objectContext ,int objc ,Tcl_Obj *const *objv)
- {
- /*
- ** tclmethod: WALLSET info ID
- ** title: Return information about a single wall segment
- */
- Wallset *p = GETWALLSET(objectContext);
- int id;
- Segment *pSeg;
- Tcl_Obj *pResult;
- if( objc!=3 ){
- Tcl_WrongNumArgs(interp, 2, objv, "ID");
- return TCL_ERROR;
- }
- if( Tcl_GetIntFromObj(interp, objv[1], &id) ) return TCL_ERROR;
- if( (pSeg = findSegment(p, id))==0 ){
- Tcl_AppendResult(interp, "segment ",
- Tcl_GetStringFromObj(objv[1],0), " does not exist", 0);
- return TCL_ERROR;
- }
- pResult = Tcl_NewObj();
- Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(pSeg->from[X_IDX]/p->rXZoom));
- Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(pSeg->from[Y_IDX]/p->rYZoom));
- Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(pSeg->to[X_IDX]/p->rXZoom));
- Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(pSeg->to[Y_IDX]/p->rYZoom));
- Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(pSeg->id));
- Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(pSeg->idLC));
- Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(pSeg->idRC));
- Tcl_SetObjResult(interp, pResult);
- return TCL_OK;
- }
- static int Wallset_Method_insert(ClientData clientData ,Tcl_Interp *interp ,Tcl_ObjectContext objectContext ,int objc ,Tcl_Obj *const *objv)
- {
- /*
- ** tclmethod: WALLSET insert X0 Y0 X1 Y1 ID LC RC VIRTUAL
- ** title: Create a new wall within the wallset
- */
- Wallset *p = GETWALLSET(objectContext);
- int id;
- int h,virtual=0;
- double x0, y0, x1, y1;
- int idLC, idRC;
- Segment *pSeg;
- if( objc!=8 && objc!=9){
- Tcl_WrongNumArgs(interp, 1, objv, "X0 Y0 X1 Y1 ID LC RC ?1|0?");
- return TCL_ERROR;
- }
- if( p->busy ){
- Tcl_AppendResult(interp, "cannot \"insert\" from within a \"foreach\"",0);
- return TCL_ERROR;
- }
- if( Tcl_GetDoubleFromObj(interp, objv[1], &x0) ) return TCL_ERROR;
- if( Tcl_GetDoubleFromObj(interp, objv[2], &y0) ) return TCL_ERROR;
- if( Tcl_GetDoubleFromObj(interp, objv[3], &x1) ) return TCL_ERROR;
- if( Tcl_GetDoubleFromObj(interp, objv[4], &y1) ) return TCL_ERROR;
- if( Tcl_GetIntFromObj(interp, objv[5], &id) ) return TCL_ERROR;
- if( Tcl_GetIntFromObj(interp, objv[6], &idLC) ) {
- Tcl_ResetResult(interp);
- idLC=0;
- }
- if( Tcl_GetIntFromObj(interp, objv[7], &idRC) ) {
- Tcl_ResetResult(interp);
- idRC=0;
- }
- if(objc==10) {
- if( Tcl_GetIntFromObj(interp, objv[8], &virtual) ) {
- Tcl_ResetResult(interp);
- virtual=0;
- }
- }
- x0 = roundCoord(x0*p->rXZoom);
- y0 = roundCoord(y0*p->rYZoom);
- x1 = roundCoord(x1*p->rXZoom);
- y1 = roundCoord(y1*p->rYZoom);
- if( findSegment(p, id) ){
- Tcl_AppendResult(interp, "segment ",
- Tcl_GetStringFromObj(objv[6],0), " already exists", 0);
- return TCL_ERROR;
- }
- if( floatCompare(x0,x1)==0 && floatCompare(y0,y1)==0 ){
- /* Tcl_AppendResult(interp, "endpoints must be distinct", 0); */
- /* return TCL_ERROR; */
- return TCL_OK; /* Not an error. Just a no-op. */
- }
- clearComptBoxCache(p);
- pSeg = (Segment *)Odie_Alloc( sizeof(*pSeg) );
- if( pSeg==0 ) return TCL_ERROR;
- pSeg->id = id;
- pSeg->idLC = idLC;
- pSeg->idRC = idRC;
- pSeg->from[X_IDX] = x0;
- pSeg->from[Y_IDX] = y0;
- pSeg->to[X_IDX] = x1;
- pSeg->to[Y_IDX] = y1;
- pSeg->isBoundary=virtual;
- LinkInit(pSeg->pAll, pSeg);
- LinkInit(pSeg->pSet, pSeg);
- LinkInit(pSeg->pHash, pSeg);
- LinkInit(pSeg->pFrom, pSeg);
- LinkInit(pSeg->pTo, pSeg);
- LinkInsert(&p->pAll, &pSeg->pAll);
- h = hashInt(id);
- LinkInsert(&p->hashId[h], &pSeg->pHash);
- h = hashCoord(pSeg->from[X_IDX], pSeg->from[Y_IDX]);
- LinkInsert(&p->hashFrom[h], &pSeg->pFrom);
- h = hashCoord(pSeg->to[X_IDX], pSeg->to[Y_IDX]);
- LinkInsert(&p->hashTo[h], &pSeg->pTo);
- return TCL_OK;
- }
- static int Wallset_Method_primary(ClientData clientData ,Tcl_Interp *interp ,Tcl_ObjectContext objectContext ,int objc ,Tcl_Obj *const *objv)
- {
- /*
- ** tclmethod: WALLSET primary X Y
- ** title: Return the primary segment of the compartment enclosing X,Y
- **
- ** The primary segment is the segment with the smallest ID. If the
- ** same segment occurs twice on the list (in other words, if the
- ** same compartment is on both sides of a wall), then the right side
- ** (as measured facing the direction of travel from x0,y0 -> x1,y1)
- ** is used.
- */
- Wallset *p = GETWALLSET(objectContext);
- Boundary aBound[MX_BOUND];
- int nBound;
- double x, y;
- int i, sideSmallest;
- int idSmallest;
- Tcl_Obj *pResult;
- if( objc!=4 ){
- Tcl_WrongNumArgs(interp, 1, objv, "X Y");
- return TCL_ERROR;
- }
- if( Tcl_GetDoubleFromObj(interp, objv[2], &x) ) return TCL_ERROR;
- if( Tcl_GetDoubleFromObj(interp, objv[3], &y) ) return TCL_ERROR;
- nBound = findBoundary(p, x*p->rXZoom, y*p->rYZoom, MX_BOUND, aBound);
- if( nBound>0 && nBound<MX_BOUND ){
- idSmallest = aBound[0].pSeg->id;
- sideSmallest = aBound[0].backwards;
- for(i=1; i<nBound; i++){
- if( aBound[i].pSeg->id>idSmallest ) continue;
- if( aBound[i].pSeg->id<idSmallest || !sideSmallest ){
- idSmallest = aBound[i].pSeg->id;
- sideSmallest = aBound[i].backwards;
- }
- }
- pResult = Tcl_NewObj();
- Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(idSmallest));
- Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(sideSmallest));
- Tcl_SetObjResult(interp, pResult);
- }
- return TCL_OK;
- }
- static int Wallset_Method_intersect(ClientData clientData ,Tcl_Interp *interp ,Tcl_ObjectContext objectContext ,int objc ,Tcl_Obj *const *objv)
- {
- /*
- ** tclmethod: WALLSET intersect X0 Y0 X1 Y1
- ** title: Find the intersection of X0,Y0->X1,Y1 with a segment
- **
- ** Scan all segments in the wallset looking for one that intersects with
- ** a line from X0,Y0 to X1,Y1. If the intersection occurs at x0,y0, it
- ** is ignored, but intersections at x1,y1 count. If no such intersection
- ** exists, return the empty string. If there are one or more intersections,
- ** return the ID of the segment and the X and Y coordinates of the nearest
- ** intersection to X0,Y0.
- */
- Wallset *p = GETWALLSET(objectContext);
- double x0,y0,x1,y1;
- double adx, ady;
- Link *pI;
- int id;
- double nx, ny;
- double mindist2 = -1.0;
- if( objc!=5 ){
- Tcl_WrongNumArgs(interp, 1, objv, "X0 Y0 X1 Y1");
- return TCL_ERROR;
- }
- if( Tcl_GetDoubleFromObj(interp, objv[1], &x0) ) return TCL_ERROR;
- if( Tcl_GetDoubleFromObj(interp, objv[2], &y0) ) return TCL_ERROR;
- if( Tcl_GetDoubleFromObj(interp, objv[3], &x1) ) return TCL_ERROR;
- if( Tcl_GetDoubleFromObj(interp, objv[4], &y1) ) return TCL_ERROR;
- x0 = roundCoord(x0*p->rXZoom);
- y0 = roundCoord(y0*p->rYZoom);
- x1 = roundCoord(x1*p->rXZoom);
- y1 = roundCoord(y1*p->rYZoom);
- adx = x1-x0;
- ady = y1-y0;
- if( adx==0.0 && ady==0.0 ) {
- return TCL_OK;
- }
- for(pI=p->pAll; pI; pI=pI->pNext){
- double bdx, bdy, denom, num1;
- Segment *pSeg;
- pSeg = pI->pLinkNode;
- bdx = pSeg->to[X_IDX] - pSeg->from[X_IDX];
- bdy = pSeg->to[Y_IDX] - pSeg->from[Y_IDX];
- denom = adx*bdy - ady*bdx;
- num1 = (y0-pSeg->from[Y_IDX])*bdx - (x0-pSeg->from[X_IDX])*bdy;
- if( denom==0.0 ){
- /* The reference line and segment are parallel */
- if( num1==0.0 ){
- /* The reference line and segment are colinear */
- if( samePoint(x0,y0,pSeg->from[X_IDX],pSeg->from[Y_IDX])
- && adx*bdx<=0.0 && ady*bdy<=0.0 ){
- continue;
- }
- if( samePoint(x0,y0,pSeg->to[X_IDX],pSeg->to[Y_IDX])
- && adx*bdx>=0.0 && ady*bdy>=0.0 ){
- continue;
- }
- if( between(pSeg->from[Y_IDX],y0,y1) && between(pSeg->from[X_IDX],x0,x1) ){
- double dx, dy, dist2;
- dx = pSeg->from[X_IDX] - x0;
- dy = pSeg->from[Y_IDX] - y0;
- dist2 = dx*dx + dy*dy;
- if( mindist2<0 || mindist2>dist2 ){
- mindist2 = dist2;
- nx = pSeg->from[X_IDX];
- ny = pSeg->from[Y_IDX];
- id = pSeg->id;
- }
- }
- if( between(pSeg->to[Y_IDX],y0,y1) && between(pSeg->to[X_IDX],x0,x1) ){
- double dx, dy, dist2;
- dx = pSeg->to[X_IDX] - x0;
- dy = pSeg->to[Y_IDX] - y0;
- dist2 = dx*dx + dy*dy;
- if( mindist2<0 || mindist2>dist2 ){
- mindist2 = dist2;
- nx = pSeg->to[X_IDX];
- ny = pSeg->to[Y_IDX];
- id = pSeg->id;
- }
- }
- if( between(y0,pSeg->from[Y_IDX],pSeg->to[Y_IDX]) && between(x0,pSeg->from[X_IDX],pSeg->to[X_IDX]) ){
- if( mindist2<0 || mindist2>0.0 ){
- mindist2 = 0.0;
- nx = x0;
- ny = y0;
- id = pSeg->id;
- }
- }
- }
- }else{
- /* The reference line and segment are not parallel */
- double r, s;
- r = num1/denom;
- s = ((y0-pSeg->from[Y_IDX])*adx - (x0-pSeg->from[X_IDX])*ady)/denom;
- if( r>0 && r<=1.0 && s>=0.0 && s<=1.0 ){
- double dx, dy, dist2;
- dx = r*adx;
- dy = r*ady;
- dist2 = dx*dx + dy*dy;
- if( dist2>=GRAIN && (mindist2<0 || mindist2>dist2) ){
- mindist2 = dist2;
- nx = x0 + dx;
- ny = y0 + dy;
- id = pSeg->id;
- }
- }
- }
- }
- if( mindist2>=0.0 ){
- Tcl_Obj *pResult;
- pResult = Tcl_NewObj();
- nx = roundCoord(nx);
- ny = roundCoord(ny);
- Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(nx/p->rXZoom));
- Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(ny/p->rYZoom));
- Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(id));
- Tcl_SetObjResult(interp, pResult);
- }
- return TCL_OK;
- }
- static int Wallset_Method_left(ClientData clientData ,Tcl_Interp *interp ,Tcl_ObjectContext objectContext ,int objc ,Tcl_Obj *const *objv)
- {
- /*
- ** tclmethod: WALLSET left ID LC
- ** title: Change the left compartment of a line segment
- */
- Wallset *p = GETWALLSET(objectContext);
- int id, idLC;
- Segment *pSeg;
- if( objc!=3 ){
- Tcl_WrongNumArgs(interp, 1, objv, "ID LC");
- return TCL_ERROR;
- }
- if( Tcl_GetIntFromObj(interp, objv[1], &id) ) return TCL_ERROR;
- if( Tcl_GetIntFromObj(interp, objv[2], &idLC) ) return TCL_ERROR;
- if( (pSeg = findSegment(p, id))==0 ){
- Tcl_AppendResult(interp, "segment ",
- Tcl_GetStringFromObj(objv[2],0), " does not exist", 0);
- return TCL_ERROR;
- }
- pSeg->idLC = idLC;
- return TCL_OK;
- }
- static int Wallset_Method_list(ClientData clientData ,Tcl_Interp *interp ,Tcl_ObjectContext objectContext ,int objc ,Tcl_Obj *const *objv)
- {
- /*
- ** tclmethod: WALLSET list
- ** title: Return a list of all wall segment identifiers
- */
- Wallset *p = GETWALLSET(objectContext);
- Link *pLink;
- Tcl_Obj *pResult;
- pResult = Tcl_NewObj();
- for(pLink=p->pAll; pLink; pLink=pLink->pNext){
- Segment *pSeg=pLink->pLinkNode;
- Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(pSeg->id));
- }
- Tcl_SetObjResult(interp, pResult);
- return TCL_OK;
- }
- static int Wallset_Method_looseends(ClientData clientData ,Tcl_Interp *interp ,Tcl_ObjectContext objectContext ,int objc ,Tcl_Obj *const *objv)
- {
- /*
- ** tclmethod: WALLSET looseends
- ** title: Return a list of walls that are have unconnected ends
- **
- ** For each unconnected end, the list contains four elements:
- ** 1. The wallid
- ** 2. 0 for the "from" end, "1" for the "to" end
- ** 3. The X coordinate of the loose end
- ** 4. The Y coordinate of the loose end
- */
- Wallset *p = GETWALLSET(objectContext);
- Segment *pSeg;
- Link *pAll, *pList;
- Tcl_Obj *pRes = Tcl_NewObj();
- for(pAll=p->pAll; pAll; pAll=pAll->pNext){
- pSeg = pAll->pLinkNode;
- pList = segmentsAtVertex(p, pSeg->from[X_IDX], pSeg->from[Y_IDX]);
- if( LinkCount(pList)==1 ){
- Tcl_ListObjAppendElement(0, pRes, Tcl_NewIntObj(pSeg->id));
- Tcl_ListObjAppendElement(0, pRes, ODIE_INT_ZERO());
- Tcl_ListObjAppendElement(0, pRes, Tcl_NewDoubleObj(pSeg->from[X_IDX]/p->rXZoom));
- Tcl_ListObjAppendElement(0, pRes, Tcl_NewDoubleObj(pSeg->from[Y_IDX]/p->rYZoom));
- }
- pList = segmentsAtVertex(p, pSeg->to[X_IDX], pSeg->to[Y_IDX]);
- if( LinkCount(pList)==1 ){
- Tcl_ListObjAppendElement(0, pRes, Tcl_NewIntObj(pSeg->id));
- Tcl_ListObjAppendElement(0, pRes, ODIE_INT_ONE());
- Tcl_ListObjAppendElement(0, pRes, Tcl_NewDoubleObj(pSeg->to[X_IDX]/p->rXZoom));
- Tcl_ListObjAppendElement(0, pRes, Tcl_NewDoubleObj(pSeg->to[Y_IDX]/p->rYZoom));
- }
- }
- Tcl_SetObjResult(interp, pRes);
- return TCL_OK;
- }
- static int Wallset_Method_nearest(ClientData clientData ,Tcl_Interp *interp ,Tcl_ObjectContext objectContext ,int objc ,Tcl_Obj *const *objv)
- {
- /*
- ** tclmethod: WALLSET nearest vertex|point X Y
- ** title: Find the nearest vertex or point to a point in the plan
- */
- Wallset *p = GETWALLSET(objectContext);
- int type;
- double x, y, near_x, near_y;
- static const char *NEAR_strs[] = { "point", "vertex", 0 };
- enum NEAR_enum { NEAR_POINT, NEAR_VERTEX, };
- Link *pLink;
- Tcl_Obj *pResult;
- double dx, dy, dist;
- if( objc!=5 ){
- Tcl_WrongNumArgs(interp, 1, objv, "point|vertex X Y");
- return TCL_ERROR;
- }
- if( Tcl_GetIndexFromObj(interp, objv[1], NEAR_strs, "option", 0, &type) ){
- return TCL_ERROR;
- }
- if( Tcl_GetDoubleFromObj(interp, objv[2], &x) ) return TCL_ERROR;
- if( Tcl_GetDoubleFromObj(interp, objv[3], &y) ) return TCL_ERROR;
- x *= p->rXZoom;
- y *= p->rYZoom;
- ignoreNone(p);
- if( type==NEAR_POINT ){
- pLink = nearestPoint(p, x, y, &near_x, &near_y);
- }else if( type==NEAR_VERTEX ){
- pLink = nearestVertex(p, x, y, &near_x, &near_y);
- }else{
- /* Cannot happen */ return TCL_ERROR;
- }
- if( pLink==0 ) return TCL_OK; /* There are not segments in the wallset */
- pResult = Tcl_NewObj();
- Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(near_x/p->rXZoom));
- Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(near_y/p->rYZoom));
- dx = x - near_x;
- dy = y - near_y;
- dist = sqrt(dx*dx + dy*dy);
- Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(dist/p->rXZoom));
- Tcl_ListObjAppendElement(0, pResult, Tcl_NewObj());
- while( pLink ){
- Segment *pSeg=pLink->pLinkNode;
- Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(pSeg->id));
- pLink = pLink->pNext;
- }
- Tcl_SetObjResult(interp, pResult);
- return TCL_OK;
- }
- static int Wallset_Method_nextcwwall(ClientData clientData ,Tcl_Interp *interp ,Tcl_ObjectContext objectContext ,int objc ,Tcl_Obj *const *objv)
- {
- /*
- ** tclmethod: WALLSET nextcwwall X0 Y0 X1 Y1
- ** title: Find a wall on X1,Y1 clockwise from X0,Y0->X1,Y1
- */
- Wallset *p = GETWALLSET(objectContext);
- int isBack;
- Segment *pSeg;
- double x0, y0, x1, y1;
- int rc;
- if( objc!=5 ){
- Tcl_WrongNumArgs(interp, 1, objv, "X0 Y0 X1 Y1");
- return TCL_ERROR;
- }
- if( Tcl_GetDoubleFromObj(interp, objv[1], &x0) ) return TCL_ERROR;
- if( Tcl_GetDoubleFromObj(interp, objv[2], &y0) ) return TCL_ERROR;
- if( Tcl_GetDoubleFromObj(interp, objv[3], &x1) ) return TCL_ERROR;
- if( Tcl_GetDoubleFromObj(interp, objv[4], &y1) ) return TCL_ERROR;
- x0 = roundCoord(x0*p->rXZoom);
- y0 = roundCoord(y0*p->rYZoom);
- x1 = roundCoord(x1*p->rXZoom);
- y1 = roundCoord(y1*p->rYZoom);
- rc = nextCwSegment(p, x0, y0, x1, y1, &pSeg, &isBack);
- if( rc==0 ){
- Tcl_Obj *pResult = Tcl_NewObj();
- Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(pSeg->id));
- Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(isBack));
- Tcl_SetObjResult(interp, pResult);
- }
- return TCL_OK;
- }
- static int Wallset_Method_right(ClientData clientData ,Tcl_Interp *interp ,Tcl_ObjectContext objectContext ,int objc ,Tcl_Obj *const *objv)
- {
- /*
- ** tclmethod: WALLSET right ID RC
- ** title: Change the right compartment of a line segment
- */
- Wallset *p = GETWALLSET(objectContext);
- int id, idRC;
- Segment *pSeg;
- if( objc!=3 ){
- Tcl_WrongNumArgs(interp, 1, objv, "ID RC");
- return TCL_ERROR;
- }
- if( Tcl_GetIntFromObj(interp, objv[1], &id) ) return TCL_ERROR;
- if( Tcl_GetIntFromObj(interp, objv[2], &idRC) ) return TCL_ERROR;
- if( (pSeg = findSegment(p, id))==0 ){
- Tcl_AppendResult(interp, "segment ",
- Tcl_GetStringFromObj(objv[2],0), " does not exist", 0);
- return TCL_ERROR;
- }
- pSeg->idRC = idRC;
- return TCL_OK;
- }
- static int Wallset_Method_selfcheck(ClientData clientData ,Tcl_Interp *interp ,Tcl_ObjectContext objectContext ,int objc ,Tcl_Obj *const *objv)
- {
- /*
- ** tclmethod: WALLSET selfcheck
- ** title: Verify the integrity of internal data structures
- */
- Wallset *p = GETWALLSET(objectContext);
- return selfCheck(interp, p);
- }
- static int Wallset_Method_zoom(ClientData clientData ,Tcl_Interp *interp ,Tcl_ObjectContext objectContext ,int objc ,Tcl_Obj *const *objv)
- {
- /*
- ** tclmethod: WALLSET zoom ?ZOOM?
- ** title: Query or change the zoom factor.
- */
- Wallset *p = GETWALLSET(objectContext);
- Tcl_Obj *pResult;
- if( objc!=2 && objc!=3 ){
- Tcl_WrongNumArgs(interp, 2, objv, "?ZOOM?");
- return TCL_ERROR;
- }
- if( objc==3 ){
- double r;
- if( Tcl_GetDoubleFromObj(interp, objv[2], &r) ) return TCL_ERROR;
- if( r==0.0 ){
- Tcl_AppendResult(interp, "zoom must be non-zero", 0);
- return TCL_ERROR;
- }
- p->rYZoom = r;
- p->rXZoom = fabs(r);
- }
- pResult = Tcl_NewDoubleObj(p->rYZoom);
- Tcl_SetObjResult(interp, pResult);
- return TCL_OK;
- }
- static int Wallset_Method_constructor(ClientData clientData ,Tcl_Interp *interp ,Tcl_ObjectContext objectContext ,int objc ,Tcl_Obj *const *objv)
- {
- /*
- ** tclcmd: wallset WALLSET
- ** title: Create a new wallset object
- ** This routine runs when the "wallset" command is invoked to create a
- ** new wallset.
- */
- Wallset *p;
- Tcl_Object thisObject = Tcl_ObjectContextObject(objectContext);
- /* The current connection object */
- /*
- ** if( objc!=1 ){
- ** Tcl_WrongNumArgs(interp, 1, objv, "WALLSET");
- ** return TCL_ERROR;
- **}
- **zCmd = Tcl_GetStringFromObj(objv[1], 0);
- */
- p = (Wallset *)Odie_Alloc( sizeof(*p) );
- p->rXZoom = 100.0;
- p->rYZoom = -100.0;
- Tcl_ObjectSetMetadata(thisObject, &WallsetDataType, (ClientData) p);
- return TCL_OK;
- }
- const static Tcl_MethodType WallsetMethodType_atvertex = {
- TCL_OO_METADATA_VERSION_CURRENT,
- "atvertex",
- NULL,
- NULL
- };
- const static Tcl_MethodType WallsetMethodType_boundary = {
- TCL_OO_METADATA_VERSION_CURRENT,
- "boundary",
- NULL,
- NULL
- };
- const static Tcl_MethodType WallsetMethodType_closure = {
- TCL_OO_METADATA_VERSION_CURRENT,
- "closure",
- NULL,
- NULL
- };
- const static Tcl_MethodType WallsetMethodType_comptlist = {
- TCL_OO_METADATA_VERSION_CURRENT,
- "comptlist",
- NULL,
- NULL
- };
- const static Tcl_MethodType WallsetMethodType_corners = {
- TCL_OO_METADATA_VERSION_CURRENT,
- "corners",
- NULL,
- NULL
- };
- const static Tcl_MethodType WallsetMethodType_delete = {
- TCL_OO_METADATA_VERSION_CURRENT,
- "delete",
- NULL,
- NULL
- };
- const static Tcl_MethodType WallsetMethodType_firstboundary = {
- TCL_OO_METADATA_VERSION_CURRENT,
- "firstboundary",
- NULL,
- NULL
- };
- const static Tcl_MethodType WallsetMethodType_foreach = {
- TCL_OO_METADATA_VERSION_CURRENT,
- "foreach",
- NULL,
- NULL
- };
- const static Tcl_MethodType WallsetMethodType_info = {
- TCL_OO_METADATA_VERSION_CURRENT,
- "info",
- NULL,
- NULL
- };
- const static Tcl_MethodType WallsetMethodType_insert = {
- TCL_OO_METADATA_VERSION_CURRENT,
- "insert",
- NULL,
- NULL
- };
- const static Tcl_MethodType WallsetMethodType_primary = {
- TCL_OO_METADATA_VERSION_CURRENT,
- "primary",
- NULL,
- NULL
- };
- const static Tcl_MethodType WallsetMethodType_intersect = {
- TCL_OO_METADATA_VERSION_CURRENT,
- "intersect",
- NULL,
- NULL
- };
- const static Tcl_MethodType WallsetMethodType_left = {
- TCL_OO_METADATA_VERSION_CURRENT,
- "left",
- NULL,
- NULL
- };
- const static Tcl_MethodType WallsetMethodType_list = {
- TCL_OO_METADATA_VERSION_CURRENT,
- "list",
- NULL,
- NULL
- };
- const static Tcl_MethodType WallsetMethodType_looseends = {
- TCL_OO_METADATA_VERSION_CURRENT,
- "looseends",
- NULL,
- NULL
- };
- const static Tcl_MethodType WallsetMethodType_nearest = {
- TCL_OO_METADATA_VERSION_CURRENT,
- "nearest",
- NULL,
- NULL
- };
- const static Tcl_MethodType WallsetMethodType_nextcwwall = {
- TCL_OO_METADATA_VERSION_CURRENT,
- "nextcwwall",
- NULL,
- NULL
- };
- const static Tcl_MethodType WallsetMethodType_right = {
- TCL_OO_METADATA_VERSION_CURRENT,
- "right",
- NULL,
- NULL
- };
- const static Tcl_MethodType WallsetMethodType_selfcheck = {
- TCL_OO_METADATA_VERSION_CURRENT,
- "selfcheck",
- NULL,
- NULL
- };
- const static Tcl_MethodType WallsetMethodType_zoom = {
- TCL_OO_METADATA_VERSION_CURRENT,
- "zoom",
- NULL,
- NULL
- };
- const static Tcl_MethodType WallsetMethodType_constructor = {
- TCL_OO_METADATA_VERSION_CURRENT,
- "constructor",
- NULL,
- NULL
- };
- int Odie_Wallset_Init(
- Tcl_Interp *interp
- )
- {
- /*
- ** Build the "wallset" class
- */
- Tcl_Obj* nameObj; /* Name of a class or method being looked up */
- Tcl_Object curClassObject; /* Tcl_Object representing the current class */
- Tcl_Class curClass; /* Tcl_Class representing the current class */
- /*
- * Find the wallset class, and attach an 'init' method to it.
- */
- nameObj = Tcl_NewStringObj("::wallset", -1);
- Tcl_IncrRefCount(nameObj);
- if ((curClassObject = Tcl_GetObjectFromObj(interp, nameObj)) == NULL) {
- Tcl_DecrRefCount(nameObj);
- return TCL_ERROR;
- }
- Tcl_DecrRefCount(nameObj);
- curClass = Tcl_GetObjectAsClass(curClassObject);
- /* Attach the constructor to the 'connection' class */
- Tcl_ClassSetConstructor(interp, curClass,
- Tcl_NewMethod(interp, curClass, NULL, 1,
- &WallsetMethodType_constructor, NULL));
- nameObj=Tcl_NewStringObj("atvertex",-1);
- Tcl_NewMethod(interp, curClass, nameObj, 1, &WallsetMethodType_atvertex, (ClientData) NULL);
- Tcl_DecrRefCount(nameObj);
- nameObj=Tcl_NewStringObj("boundary",-1);
- Tcl_NewMethod(interp, curClass, nameObj, 1, &WallsetMethodType_boundary, (ClientData) NULL);
- Tcl_DecrRefCount(nameObj);
- nameObj=Tcl_NewStringObj("closure",-1);
- Tcl_NewMethod(interp, curClass, nameObj, 1, &WallsetMethodType_closure, (ClientData) NULL);
- Tcl_DecrRefCount(nameObj);
- nameObj=Tcl_NewStringObj("comptlist",-1);
- Tcl_NewMethod(interp, curClass, nameObj, 1, &WallsetMethodType_comptlist, (ClientData) NULL);
- Tcl_DecrRefCount(nameObj);
- nameObj=Tcl_NewStringObj("corners",-1);
- Tcl_NewMethod(interp, curClass, nameObj, 1, &WallsetMethodType_corners, (ClientData) NULL);
- Tcl_DecrRefCount(nameObj);
- nameObj=Tcl_NewStringObj("delete",-1);
- Tcl_NewMethod(interp, curClass, nameObj, 1, &WallsetMethodType_delete, (ClientData) NULL);
- Tcl_DecrRefCount(nameObj);
- nameObj=Tcl_NewStringObj("firstboundary",-1);
- Tcl_NewMethod(interp, curClass, nameObj, 1, &WallsetMethodType_firstboundary, (ClientData) NULL);
- Tcl_DecrRefCount(nameObj);
- nameObj=Tcl_NewStringObj("foreach",-1);
- Tcl_NewMethod(interp, curClass, nameObj, 1, &WallsetMethodType_foreach, (ClientData) NULL);
- Tcl_DecrRefCount(nameObj);
- nameObj=Tcl_NewStringObj("info",-1);
- Tcl_NewMethod(interp, curClass, nameObj, 1, &WallsetMethodType_info, (ClientData) NULL);
- Tcl_DecrRefCount(nameObj);
- nameObj=Tcl_NewStringObj("insert",-1);
- Tcl_NewMethod(interp, curClass, nameObj, 1, &WallsetMethodType_insert, (ClientData) NULL);
- Tcl_DecrRefCount(nameObj);
- nameObj=Tcl_NewStringObj("primary",-1);
- Tcl_NewMethod(interp, curClass, nameObj, 1, &WallsetMethodType_primary, (ClientData) NULL);
- Tcl_DecrRefCount(nameObj);
- nameObj=Tcl_NewStringObj("intersect",-1);
- Tcl_NewMethod(interp, curClass, nameObj, 1, &WallsetMethodType_intersect, (ClientData) NULL);
- Tcl_DecrRefCount(nameObj);
- nameObj=Tcl_NewStringObj("left",-1);
- Tcl_NewMethod(interp, curClass, nameObj, 1, &WallsetMethodType_left, (ClientData) NULL);
- Tcl_DecrRefCount(nameObj);
- nameObj=Tcl_NewStringObj("list",-1);
- Tcl_NewMethod(interp, curClass, nameObj, 1, &WallsetMethodType_list, (ClientData) NULL);
- Tcl_DecrRefCount(nameObj);
- nameObj=Tcl_NewStringObj("looseends",-1);
- Tcl_NewMethod(interp, curClass, nameObj, 1, &WallsetMethodType_looseends, (ClientData) NULL);
- Tcl_DecrRefCount(nameObj);
- nameObj=Tcl_NewStringObj("nearest",-1);
- Tcl_NewMethod(interp, curClass, nameObj, 1, &WallsetMethodType_nearest, (ClientData) NULL);
- Tcl_DecrRefCount(nameObj);
- nameObj=Tcl_NewStringObj("nextcwwall",-1);
- Tcl_NewMethod(interp, curClass, nameObj, 1, &WallsetMethodType_nextcwwall, (ClientData) NULL);
- Tcl_DecrRefCount(nameObj);
- nameObj=Tcl_NewStringObj("right",-1);
- Tcl_NewMethod(interp, curClass, nameObj, 1, &WallsetMethodType_right, (ClientData) NULL);
- Tcl_DecrRefCount(nameObj);
- nameObj=Tcl_NewStringObj("selfcheck",-1);
- Tcl_NewMethod(interp, curClass, nameObj, 1, &WallsetMethodType_selfcheck, (ClientData) NULL);
- Tcl_DecrRefCount(nameObj);
- nameObj=Tcl_NewStringObj("zoom",-1);
- Tcl_NewMethod(interp, curClass, nameObj, 1, &WallsetMethodType_zoom, (ClientData) NULL);
- Tcl_DecrRefCount(nameObj);
- return TCL_OK;
- }