/* * mzEstimate.c -- * * Management of tile plane for estimation of remaining cost to completion. * * Contains code for building estimation plane (just prior to route), and * routine for computing estimates (using the estimation plane) during routing. * * ********************************************************************* * * Copyright (C) 1988, 1990 Michael H. Arnold and the Regents of the * * * University of California. * * * Permission to use, copy, modify, and distribute this * * * software and its documentation for any purpose and without * * * fee is hereby granted, provided that the above copyright * * * notice appear in all copies. The University of California * * * makes no representations about the suitability of this * * * software for any purpose. It is provided "as is" without * * * express or implied warranty. Export of this software outside * * * of the United States of America may require an export license. * * ********************************************************************* * * EXPLANATION OF ESTIMATION CODE: * The purpose of the estimation code is to estimate cost to completion * from any point to the destintation taking into account the need to * detour around major obstactles such as subcells and fences. * * The estimation plane permits multiple destination area (routes are * made to what ever area is easiest to reach). The mzrouter also permits * multiple start points of course. * * To achieve this purpose, prior to beginning the search for a route an * estimation plane is generated with info that allows quick estimation * of cost to completion during routing. * * The estimation plane contains * ``solid'' tiles for major obstacles (subcells and fences) and space * tiles. The space tiles are split on the extensions of solid tile edges * so that one can always get from a solid tile corner straight out to * the next blocking solid tile along tile edges. Space tiles are also split * outward in each direction from the destination point. * * Estimators are generated for each tile in the estimation plane that allow * estimates to be made for points in that tile. An estimator consists * of five numbers: * (x0,y0,hcost,vcost,cost0) * that are used in the following formula: * EstCost = (x - x0)*hcost + (y -y0)*vcost + cost0. * An estimator represents the approximate cost of a path beginning at any * point (x,y) in the current tile and proceedings horizontally and then * vertically * (or vice versa) to the tile corner (x0,y0) and then following the cheapest * path along tile-edges to a destination area. The cheapest path from * (x0,y0) is precomputed and has cost cost0. Currently only * tile corners (x0,y0) of the tile the estimator is attached to are used * for estimators. Estimators are also generated for paths from edges of * a tile straight across to the nearest dest area (with out jogs). These * "straight shot" estimators have zero hcost or vcost. * Several estimators are attached to each * tile, and the least cost one is used for any given (x,y). * * The estimation plane is generated in the following steps: * * 1. Generate solid tiles for unexpanded subcells (if subcells can not be routed across on any active layer) and fences. * * 2. Split space tiles along extended edges of solid tiles and * destination areas. * * 3. Assign a horizontal and vertical cost for routing in each tile. * Cost for space tiles is cost of min active route-layer, cost for * solid tiles is INFINITY. (could be more sophisticated - e.g. * if routing is allowed over subcells on an expensive layer.) * * 4. Apply djikstra's shortest path algorithm to graph whose vertices * are tile-corners and whose edges are tile edges. Weight on * hor edges is min hor cost of the two adjacent tiles, weight on * vertical edges is min of vert costs of adjacent tiles. Djikstra's * algor. computes least cost path along tile-edges to * destination for all tile corners * (e.g. it generates all the cost0's for the estimators). * * 5. Build estimators for each tile - currently one estimator is built * for each corner of the tile. * * 6. Trim away redundant estimators - currently if an estimator e0 is * always cheaper than a second e1, e1 is thrown away. */ #ifndef lint static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/mzrouter/mzEstimate.c,v 1.2 2008/06/01 18:37:44 tim Exp $"; #endif /* not lint */ #include #include #include "utils/magic.h" #include "utils/geometry.h" #include "utils/geofast.h" #include "tiles/tile.h" #include "utils/hash.h" #include "database/database.h" #include "utils/signals.h" #include "textio/textio.h" #include "windows/windows.h" #include "dbwind/dbwind.h" #include "utils/malloc.h" #include "utils/list.h" #include "debug/debug.h" #include "textio/textio.h" #include "utils/heap.h" #include "mzrouter/mzrouter.h" #include "mzrouter/mzInternal.h" /* largest finite tile plane coordinates, need to reduce INFINITY and MINFINITY * defined in tile.h because of some funny buisness off at infinity? */ /* Special case mips machines because of a compiler bug, as of 8/9/89 */ #ifdef mips static int MAX_FINITE_COORDINATE = (INFINITY-10); static int MIN_FINITE_COORDINATE = (MINFINITY+10); #else #define MAX_FINITE_COORDINATE (INFINITY-10) #define MIN_FINITE_COORDINATE (MINFINITY+10) #endif /*----------- Vertex structure for shortest path algorithm ---------------*/ typedef struct vertex { int vx_status; /* which corner in tile, IN set when min distance * to vertex determined (shortest path algor) */ Tile *vx_tile; /* tile vertex is attached to */ dlong vx_cost; /* Min cost from here to destination point */ } Vertex; #define VX_CORNER 7 #define VX_NONE 0 #define VX_L_LEFT 1 #define VX_U_LEFT 2 #define VX_L_RIGHT 4 #define VX_IN 8 /* Estimators for estimating cost from point within tile, (x,y), along a * certain path to destination. * * EstCost = (x - x0) * hCost + (y - y0) * vCost + cost0 */ typedef struct estimate { int e_x0; int e_y0; dlong e_cost0; int e_hCost; int e_vCost; struct estimate *e_next; } Estimate; /* --------------- tileCosts structure --------------------------------------*/ /* One of these is associated with each tile in the mzEstimate plane. They * are pointed to by the client fields of the tiles. */ typedef struct tileCosts { int tc_hCost; /* horizontal cost / unit distance */ int tc_vCost; /* vertical cost / unit distance */ Vertex tc_vxLLeft; /* Vertices corresponding to 3 corners of this tile */ Vertex tc_vxULeft; Vertex tc_vxLRight; Estimate *tc_estimates; /* path estimates for point in this tile */ } TileCosts; /*---------------- static data, local to this file --------------------------*/ bool mzEstimateExists = FALSE; /* Set on first call to mzBuildEstimate */ /* Forward declarations */ extern void mzCleanEstimate(); extern void mzBuildCornerEstimators(); extern void mzBuildStraightShotEstimators(); extern void mzSplitTiles(); extern void mzAssignVertexCosts(); extern void mzAddVertex(); /* * ---------------------------------------------------------------------------- * * mzBuildEstimate -- * Setup contents of Estimation plane. * * Results: * None * * Side effects: * Add tiles to mzEstimate plane, create a tileCost struc for each tile * complete with estimates. * * ---------------------------------------------------------------------------- */ void mzBuildEstimate() { /* Clear estimation plane, reclaiming storage */ if(mzEstimateExists) { mzCleanEstimate(); } mzEstimateExists = TRUE; /* Set flag, so we know to clean next time */ /* Now build the estimate plane from scratch */ if(mzEstimate) { bool subcellsOpaque; /* determine whether subcells can be crossed on any active layer */ { RouteLayer *rL; subcellsOpaque = TRUE; for(rL = mzActiveRLs; rL!=NULL && subcellsOpaque; rL=rL->rl_nextActive) { if(rL->rl_routeType.rt_spacing[TT_SUBCELL] < 0) { subcellsOpaque = FALSE; } } } /* If over-the-cell routing is not possible, * add a tile to the estimation plane for each unexpanded subcell. * * NOTE: A 0 expansion mask is special cased since * the mzrouter interpets a 0 mask to mean all subcells are * expanded, * while DBTreeSrCells() takes a 0 mask to mean all subcells are * unexpanded. */ if(mzCellExpansionMask != 0 && subcellsOpaque) { int mzAddSubcellEstFunc(); SearchContext scx; scx.scx_area = mzBoundingRect; scx.scx_trans = GeoIdentityTransform; scx.scx_use = mzRouteUse; /* clip area to bounding box to avoid overflow during transfroms */ GEOCLIP(&(scx.scx_area),&(mzRouteUse->cu_def->cd_bbox)); DBTreeSrCells( &scx, mzCellExpansionMask, mzAddSubcellEstFunc, (ClientData) &mzBoundingRect); } /* If route is OUTSIDE fence, add ``fence'' tiles to estimation plane * for each FENCE tile on fence plane. * If route is INSIDE fence, add ``fence'' tiles for each SPACE tile on * the fence plane. */ { int mzAddFenceEstFunc(); if(mzInsideFence) { DBSrPaintArea(NULL, /* no hint tile */ mzHFencePlane, &mzBoundingRect, &DBSpaceBits, mzAddFenceEstFunc, (ClientData) &mzBoundingRect); } else { DBSrPaintArea(NULL, /* no hint tile */ mzHFencePlane, &mzBoundingRect, &DBAllButSpaceBits, mzAddFenceEstFunc, (ClientData) &mzBoundingRect); } } } /* Add a tile to the estimation plane for each dest area, and cut * holes at the walks leading to the dest areas */ { int mzProcessDestEstFunc(); SearchContext scx; scx.scx_area = mzBoundingRect; scx.scx_trans = GeoIdentityTransform; scx.scx_use = mzDestAreasUse; /* clip area to bounding box to avoid overflow during transforms */ GEOCLIP(&(scx.scx_area),&(mzDestAreasUse->cu_def->cd_bbox)); (void) DBTreeSrTiles( &scx, &DBAllButSpaceAndDRCBits, 0, mzProcessDestEstFunc, (ClientData) NULL); } /*--- Split space tiles at edges of solid tiles. ---*/ { int mzBuildSolidsListFunc(); List *solidsList = NULL; List *l; /* Build list of all solid, * i.e. nonspace, tiles on estimation plane. */ DBSrPaintArea(NULL, /* no hint tile */ mzEstimatePlane, &TiPlaneRect, /* max paintable rect. */ &DBAllButSpaceBits, mzBuildSolidsListFunc, (ClientData) &solidsList); /* Split tiles along perpendiculars of solid tile corners. */ for(l=solidsList; l!=NULL; l = LIST_TAIL(l)) { Tile *solid = (Tile *) LIST_FIRST(l); Point p; /* lower left corner */ mzSplitTiles(mzEstimatePlane,&(solid->ti_ll)); /* top left corner */ p.p_x = LEFT(solid); p.p_y = TOP(solid); mzSplitTiles(mzEstimatePlane,&p); /* top right corner */ p.p_x = RIGHT(solid); mzSplitTiles(mzEstimatePlane,&p); /* bottom right corner */ p.p_y = BOTTOM(solid); mzSplitTiles(mzEstimatePlane,&p); } /* Free up solids list */ ListDealloc(solidsList); } /* Assign costs to tiles in estimation plane: * Dest tiles - 0 cost. * Space tiles - min active costs. * Fence tiles - infinite cost. * Subcell tiles - infinite cost. */ { int mzAssignCostsFunc(); TileCosts spaceCosts; RouteLayer *rL; /* set space costs to min costs of active layers */ spaceCosts.tc_hCost = INT_MAX; spaceCosts.tc_vCost = INT_MAX; for(rL = mzRouteLayers; rL!=NULL; rL=rL->rl_next) { if(rL->rl_routeType.rt_active) { if(rL->rl_hCost < spaceCosts.tc_hCost) spaceCosts.tc_hCost = rL->rl_hCost; if(rL->rl_vCost < spaceCosts.tc_vCost) spaceCosts.tc_vCost = rL->rl_vCost; } } /* visit all tiles in estimate plane attaching cost structures * (including vertices) to the client fields. Horizontal and * vertical costs are assigned. */ DBSrPaintArea(NULL, /* no hint tile */ mzEstimatePlane, &TiPlaneRect, &DBAllTypeBits, mzAssignCostsFunc, (ClientData) &spaceCosts); } /* Apply djikstra shortest path algorithm to determine minimum costs * to vertices in tile edge graph. */ mzAssignVertexCosts(); /* Build cost estimates for each tile in estimate plane */ { int mzBuildEstimatesFunc(); DBSrPaintArea(NULL, /* no hint tile */ mzEstimatePlane, &TiPlaneRect, &DBAllTypeBits, mzBuildEstimatesFunc, (ClientData) NULL); } /* Trim away redundant cost estimates on tiles */ { int mzTrimEstimatesFunc(); DBSrPaintArea(NULL, /* no hint tile */ mzEstimatePlane, &TiPlaneRect, &DBAllTypeBits, mzTrimEstimatesFunc, (ClientData) NULL); } return; } /* * ---------------------------------------------------------------------------- * * mzCleanEstimate -- * * Clear estimate plane and reclaim storage. * * Results: * None * * Side effects: * See Above. * * ---------------------------------------------------------------------------- */ void mzCleanEstimate() { if (mzEstimateExists) { int mzReclaimTCFunc(); SigDisableInterrupts(); /* Make atomic so we don't forget to reclaim * anything nor reclaim it twice. */ /* visit all tiles in estimate plane reclaiming attached * cost structures. */ DBSrPaintArea(NULL, /* no hint tile */ mzEstimatePlane, &TiPlaneRect, /* max paintable rect */ &DBAllTypeBits, mzReclaimTCFunc, (ClientData) NULL); DBClearPaintPlane(mzEstimatePlane); mzEstimateExists = FALSE; SigEnableInterrupts(); } return; } /* * ---------------------------------------------------------------------------- * * mzReclaimTCFunc -- * * Free TileCost struc (prior to erasing old estimate plane.) * * Results: * Returns 0 always. * * Side effects: * Frees TileCost pointed to by client field in tile struc. Clears * client field pointer - just a precaution. * * ---------------------------------------------------------------------------- */ int mzReclaimTCFunc(tile, notUsed) Tile *tile; ClientData notUsed; { if (tile->ti_client != (ClientData)CLIENTDEFAULT) { TileCosts *tc = ((TileCosts *) (tile->ti_client)); Estimate *e; /* free estimates attached to tilecosts struc */ for(e=tc->tc_estimates; e!=NULL; e=e->e_next) { freeMagic((char *) e); } /* free tilecosts struc */ freeMagic((char *) (tile->ti_client)); /* reset client field in tile */ tile->ti_client = ((ClientData) CLIENTDEFAULT); } /* return 0 - to continue traversal of old estimate plane */ return 0; } /* * ---------------------------------------------------------------------------- * * mzProcessDestEstFunc -- * * Filter function called via DBTreeSrTiles on behalf of mzBuildEstimate() * above, for each dest area in the area of interest. Searches blockage * planes for dest tiles and walks under dest area and paints corresponding * tiles in estimation plane. * * Results: * Returns 0 always. * * Side effects: * Paints into mzEstimatePlane * * ---------------------------------------------------------------------------- */ int mzProcessDestEstFunc(tile, cxp) Tile *tile; TreeContext *cxp; { SearchContext *scx = cxp->tc_scx; TileType type = TiGetType(tile); RouteType *rT; Rect r, rect; /* Transform to result coordinates */ TITORECT(tile, &r); GEOTRANSRECT(&scx->scx_trans, &r, &rect); /* Grow rect by max walk size in all directions so we find walks as * well as the dest area. */ rect.r_xbot -= mzMaxWalkLength; rect.r_ybot -= mzMaxWalkLength; rect.r_xtop += mzMaxWalkLength; rect.r_ytop += mzMaxWalkLength; /* find route type for this dest area */ { rT = mzActiveRTs; while ((rT->rt_tileType != type) && (rT!=NULL)) { rT = rT->rt_nextActive; } ASSERT(rT!=NULL,"mzAddDestTileEstFunc"); } /* process dest and walk tiles below dest area */ { int mzDestTileEstFunc(); TileTypeBitMask destMask; TTMaskSetOnlyType(&destMask, TT_DEST_AREA); TTMaskSetType(&destMask, TT_LEFT_WALK); TTMaskSetType(&destMask, TT_RIGHT_WALK); TTMaskSetType(&destMask, TT_TOP_WALK); TTMaskSetType(&destMask, TT_BOTTOM_WALK); DBSrPaintArea(NULL, /* no hint tile */ rT->rt_hBlock, &rect, &destMask, mzDestTileEstFunc, (ClientData) NULL); } return 0; } /* * ---------------------------------------------------------------------------- * * mzDestTileEstFunc -- * * Paint dest area into estimate plane, or cut whole over walks. * * Results: * Returns 0 always. * * Side effects: * Modifies estimate plane. * * ---------------------------------------------------------------------------- */ int mzDestTileEstFunc(tile, cdarg) Tile *tile; ClientData cdarg; { Rect rect; /* set rect to bounding box of tile */ TITORECT(tile, &rect); if(TiGetType(tile)==TT_DEST_AREA) /* paint dest area into estimate plane */ { DBPaintPlane(mzEstimatePlane, &rect, mzEstimatePaintTbl[TT_EST_DEST], (PaintUndoInfo *) NULL); } else /* cut hole for walk in estimate plane */ { DBPaintPlane(mzEstimatePlane, &rect, mzEstimatePaintTbl[TT_SPACE], (PaintUndoInfo *) NULL); } return 0; } /* * ---------------------------------------------------------------------------- * * mzAddSubcellEstFunc -- * * Filter function called via DBTreeSrTiles on behalf of mzBuildEstimate() * above, for each unexpanded subcell in the area of interest, * a TT_EST_SUBCELL tile is painted on each estimate plane for * the bounding box of the subcell. * * Results: * Returns 0 always. * * Side effects: * Paints into mzEstimatePlane * * ---------------------------------------------------------------------------- */ int mzAddSubcellEstFunc(scx, cdarg) SearchContext *scx; ClientData cdarg; { Rect r, rDest; /* Transform bounding box to result coords */ r = scx->scx_use->cu_def->cd_bbox; GEOTRANSRECT(&scx->scx_trans, &r, &rDest); /* paint subcell block onto estimate plane */ DBPaintPlane(mzEstimatePlane, &rDest, mzEstimatePaintTbl[TT_EST_SUBCELL], (PaintUndoInfo *) NULL); /* continue search */ return (0); } /* * ---------------------------------------------------------------------------- * * mzAddFenceEstFunc -- * * Filter function called via DBSrPaintArea on behalf of mzBuildEstimate() * above, for each fence tile in the area of interest, * a TT_EST_FENCE tile is painted on the estimate plane for * each nonspace tile on the fence plane. * * Results: * Returns 0 always. * * Side effects: * Paints into mzEstimatePlane * * ---------------------------------------------------------------------------- */ int mzAddFenceEstFunc(tile, buildArea) Tile *tile; Rect *buildArea; /* currently ignored */ { Rect r; /* Get boundary of tile */ TITORECT(tile, &r); /* paint fence into estimate plane */ DBPaintPlane(mzEstimatePlane, &r, mzEstimatePaintTbl[TT_EST_FENCE], (PaintUndoInfo *) NULL); /* continue search */ return (0); } /* * ---------------------------------------------------------------------------- * * mzBuildSolidsList -- * * Called by DBSrPaintArea for each solid tile in estimation plane * Creates list of these tiles. * * Results: * Returns 0 always. * * Side effects: * Adds to list passed as arg. * * ---------------------------------------------------------------------------- */ int mzBuildSolidsListFunc(tile, listPtr) Tile *tile; List **listPtr; /* pointer to list to add tile to */ { LIST_ADD(tile,*listPtr); /* return 0 - to continue search */ return(0); } /* * ---------------------------------------------------------------------------- * * mzAssignCostsFunc -- * * Assigns horizontal and vertical costs to tiles in estimate plane. * * Results: * Returns 0 always. * * Side effects: * Adds to list passed as arg. * * ---------------------------------------------------------------------------- */ int mzAssignCostsFunc(tile, spaceCosts) Tile *tile; TileCosts *spaceCosts; /* costs to assign to space tiles */ { Tile *tRight, *tUp; TileCosts *newCosts; Vertex *v; /* Alloc TileCosts struc for this tile, and attach it */ newCosts = (TileCosts *) mallocMagic((unsigned) (sizeof(TileCosts))); tile->ti_client = (ClientData) newCosts; /* Assign hor and vert costs for tile */ switch(TiGetType(tile)) { case TT_EST_DEST: newCosts->tc_hCost = 0; newCosts->tc_vCost = 0; break; case TT_SPACE: *newCosts = *spaceCosts; break; case TT_EST_FENCE: case TT_EST_SUBCELL: newCosts->tc_hCost = INT_MAX; newCosts->tc_vCost = INT_MAX; break; default: /* unrecognized tile type */ ASSERT(FALSE,"mzAssignCostsFunc"); } /* add lower-left vertex */ v = &(newCosts->tc_vxLLeft); v->vx_status = VX_L_LEFT; v->vx_tile = tile; v->vx_cost = COST_MAX; /* add lower-right vertex, if at 'T' */ NEXT_TILE_RIGHT(tRight, tile, BOTTOM(tile)); if (BOTTOM(tRight)!=BOTTOM(tile)) { /* lower-right vertex at 'T', so add it */ v = &(newCosts->tc_vxLRight); v->vx_status = VX_L_RIGHT; v->vx_tile = tile; v->vx_cost = COST_MAX; } else { /* no 'T' */ newCosts->tc_vxLRight.vx_status = VX_NONE; } /* add upper-left vertex, if at 'T' */ NEXT_TILE_UP(tUp, tile, LEFT(tile)); if (LEFT(tUp)!=LEFT(tile)) { /* upper-left vertex at 'T', so add it */ v = &(newCosts->tc_vxULeft); v->vx_status = VX_U_LEFT; v->vx_tile = tile; v->vx_cost = COST_MAX; } else { /* no 'T' */ newCosts->tc_vxULeft.vx_status = VX_NONE; } /* initial estimates to NULL list */ newCosts->tc_estimates = NULL; /* return 0 - to continue traversal of estimate plane */ return(0); } /* * ---------------------------------------------------------------------------- * * mzDestInitialAssignFunc * * Add one vertex of dest tile to adjacency heap to initialize graph for * Djikstra's shortest path computation. * * Results: * Returns 0 always. * * Side effects: * Initials cost to zero and adds a vertex to adjacency heap. * * ---------------------------------------------------------------------------- */ int mzDestInitialAssignFunc(tile, cdarg) Tile *tile; ClientData cdarg; { Heap *adjHeap = (Heap *) cdarg; Vertex *v; /* get lower left vertex */ v = &(((TileCosts *)(tile->ti_client))->tc_vxLLeft); /* cost from dest is zero */ v->vx_cost = 0; /* add vertex to adjHeap */ HeapAddDLong(adjHeap, 0, (char *) v); /* return 0 - to continue search */ return(0); } /* * ---------------------------------------------------------------------------- * * mzBuildEstimatesFunc -- * * Build path estimates for this tile. * (For now builds * + one estimate for each corner of the tile. * + one estimate for no jog path to destination in each direction where * this is possible.) * * Results: * Returns 0 always. * * Side effects: * Adds estimates to TileCost struc attached to tile. * * ---------------------------------------------------------------------------- */ int mzBuildEstimatesFunc(tile, notUsed) Tile *tile; ClientData notUsed; { mzBuildCornerEstimators(tile); mzBuildStraightShotEstimators(tile); /* return 0 - to continue traversal of estimate plane */ return(0); } /* * ---------------------------------------------------------------------------- * * mzBuildCornerEstimators -- * * Build path estimates for paths via corners of this tile. * * Results: * None. * * Side effects: * Adds estimates to TileCost struc attached to tile. * * ---------------------------------------------------------------------------- */ void mzBuildCornerEstimators(tile) Tile *tile; { TileCosts *tc = (TileCosts *) (tile->ti_client); Vertex *vLLeft = NULL; Vertex *vULeft = NULL; Vertex *vLRight = NULL; Vertex *vURight = NULL; /* find vertex strucs corresponding to four corners. * (NULL, for corners at infinity) */ { Tile *tUp, *tRight, *tDiag; Tile *tRT, *tTR; if(LEFT(tile)>=MIN_FINITE_COORDINATE) { if(BOTTOM(tile)>=MIN_FINITE_COORDINATE) { /* Lower Left */ vLLeft = &(tc->tc_vxLLeft); } if(TOP(tile)<=MAX_FINITE_COORDINATE) { /* Upper Left */ NEXT_TILE_UP(tUp, tile, LEFT(tile)); if (LEFT(tUp)tc_vxULeft); } else { /* no 'T', stored with tUp */ vULeft = &(((TileCosts *)(tUp->ti_client))->tc_vxLLeft); } } } if(RIGHT(tile)<=MAX_FINITE_COORDINATE) { if(BOTTOM(tile)>=MIN_FINITE_COORDINATE) { /* Lower Right */ NEXT_TILE_RIGHT(tRight, tile, BOTTOM(tile)); if (BOTTOM(tRight)tc_vxLRight); } else { /* no 'T', stored with tRight */ vLRight = &(((TileCosts *)(tRight->ti_client))->tc_vxLLeft); } } if(TOP(tile)<=MAX_FINITE_COORDINATE) { /* Upper Right */ tRT = RT(tile); /* right top corner stitch (up) */ tTR = TR(tile); /* top right corner stitch (to right) */ if(RIGHT(tRT)>RIGHT(tile)) { /* upper right at 'T' stored with tTR */ vURight = &(((TileCosts *)(tTR->ti_client))->tc_vxULeft); } else if (TOP(tTR)>TOP(tile)) { /* upper right at 'T' stored with tRT */ vURight = &(((TileCosts *)(tRT->ti_client))->tc_vxLRight); } else { /* no 'T', stored in own tile */ NEXT_TILE_UP(tDiag, tTR, RIGHT(tile)); vURight = &(((TileCosts *)(tDiag->ti_client))->tc_vxLLeft); } } } } /* Build estimates */ { Estimate *e; /* Estimate for lower left corner */ if (vLLeft) { e = (Estimate *) mallocMagic((unsigned) (sizeof(Estimate))); e->e_x0 = LEFT(tile); e->e_y0 = BOTTOM(tile); e->e_cost0 = vLLeft->vx_cost; e->e_hCost = tc->tc_hCost; e->e_vCost = tc->tc_vCost; e->e_next = tc->tc_estimates; tc->tc_estimates = e; } /* Estimate for lower right corner */ if (vLRight) { e = (Estimate *) mallocMagic((unsigned) (sizeof(Estimate))); e->e_x0 = RIGHT(tile); e->e_y0 = BOTTOM(tile); e->e_cost0 = vLRight->vx_cost; e->e_hCost = tc->tc_hCost; e->e_vCost = tc->tc_vCost; e->e_next = tc->tc_estimates; tc->tc_estimates = e; } /* Estimate for upper right corner */ if (vURight) { e = (Estimate *) mallocMagic((unsigned) (sizeof(Estimate))); e->e_x0 = RIGHT(tile); e->e_y0 = TOP(tile); e->e_cost0 = vURight->vx_cost; e->e_hCost = tc->tc_hCost; e->e_vCost = tc->tc_vCost; e->e_next = tc->tc_estimates; tc->tc_estimates = e; } /* Estimate for upper left corner */ if (vULeft) { e = (Estimate *) mallocMagic((unsigned)(sizeof(Estimate))); e->e_x0 = LEFT(tile); e->e_y0 = TOP(tile); e->e_cost0 = vULeft->vx_cost; e->e_hCost = tc->tc_hCost; e->e_vCost = tc->tc_vCost; e->e_next = tc->tc_estimates; tc->tc_estimates = e; } } return; } /* * ---------------------------------------------------------------------------- * * mzBuildStraightShotEstimators -- * * Build path estimates for paths straight to dest area (no jogs) * * Results: * None. * * Side effects: * Adds estimates to TileCost struc attached to tile. * * ---------------------------------------------------------------------------- */ void mzBuildStraightShotEstimators(tile) Tile *tile; { TileCosts *tc = (TileCosts *) (tile->ti_client); /* straight right */ { Tile *tSolid = tile; /* get first solid tile to right */ while(TiGetType(tSolid)==TT_SPACE && tSolid!=mzEstimatePlane->pl_right) { tSolid = TR(tSolid); } /* if dest tile, build estimator */ if(TiGetType(tSolid) == TT_EST_DEST) { Estimate *e; e = (Estimate *) mallocMagic((unsigned)(sizeof(Estimate))); e->e_x0 = RIGHT(tile); e->e_y0 = 0; if (tc->tc_hCost == INT_MAX) e->e_cost0 = COST_MAX; else e->e_cost0 = (dlong) (LEFT(tSolid) - RIGHT(tile)) * tc->tc_hCost; e->e_hCost = tc->tc_hCost; e->e_vCost = 0; e->e_next = tc->tc_estimates; tc->tc_estimates = e; } } /* straight left */ { Tile *tSolid = tile; /* get first solid tile to left */ while(TiGetType(tSolid)==TT_SPACE && tSolid!=mzEstimatePlane->pl_left) { tSolid = BL(tSolid); } /* if dest tile, build estimator */ if(TiGetType(tSolid) == TT_EST_DEST) { Estimate *e; e = (Estimate *) mallocMagic((unsigned)(sizeof(Estimate))); e->e_x0 = LEFT(tile); e->e_y0 = 0; if (tc->tc_hCost == INT_MAX) e->e_cost0 = COST_MAX; else e->e_cost0 = (dlong) (RIGHT(tSolid) - LEFT(tile)) * tc->tc_hCost; e->e_hCost = tc->tc_hCost; e->e_vCost = 0; e->e_next = tc->tc_estimates; tc->tc_estimates = e; } } /* straight up */ { Tile *tSolid = tile; /* get first solid tile above */ while(TiGetType(tSolid)==TT_SPACE && tSolid!=mzEstimatePlane->pl_top) { tSolid = RT(tSolid); } /* if dest tile, build estimator */ if(TiGetType(tSolid) == TT_EST_DEST) { Estimate *e; e = (Estimate *) mallocMagic((unsigned)(sizeof(Estimate))); e->e_x0 = 0; e->e_y0 = TOP(tile); if (tc->tc_vCost == INT_MAX) e->e_cost0 = COST_MAX; else e->e_cost0 = (dlong) (BOTTOM(tSolid) - TOP(tile)) * tc->tc_vCost; e->e_hCost = 0; e->e_vCost = tc->tc_vCost; e->e_next = tc->tc_estimates; tc->tc_estimates = e; } } /* straight down */ { Tile *tSolid = tile; /* get first solid tile below */ while(TiGetType(tSolid)==TT_SPACE && tSolid!=mzEstimatePlane->pl_bottom) { tSolid = LB(tSolid); } /* if dest tile, build estimator */ if(TiGetType(tSolid) == TT_EST_DEST) { Estimate *e; e = (Estimate *) mallocMagic((unsigned)(sizeof(Estimate))); e->e_x0 = 0; e->e_y0 = BOTTOM(tile); if (tc->tc_vCost == INT_MAX) e->e_cost0 = COST_MAX; else e->e_cost0 = (dlong)(TOP(tSolid) - BOTTOM(tile)) * tc->tc_vCost; e->e_hCost = 0; e->e_vCost = tc->tc_vCost; e->e_next = tc->tc_estimates; tc->tc_estimates = e; } } return; } /* * ---------------------------------------------------------------------------- * * AlwaysAsGood -- * * Compares two estimators. * * Results: * Returns TRUE iff est1 is always less than or equal to est2. * * Side effects: * modifies estimates list in TileCost struc attached to tile, * specifically sets floating origin coords. (Floating coords * are those with corresponding cost field of 0, hence * there value does not matter when computing estimates. They * are set here as a convience, to permit uniform treatment of * all estimators within this function.) * * * * ---------------------------------------------------------------------------- */ bool AlwaysAsGood(est1, est2, tile) Estimate *est1; Estimate *est2; Tile *tile; { if(est1->e_cost0 > est2->e_cost0) { return FALSE; } else /* check if using est1 even from est2 origin * is cheaper than using est2 */ { /* If est2 x origin is floating, set to worst case */ if(est2->e_hCost == 0) { est2->e_x0 = (ABS(LEFT(tile) - est1->e_x0) > ABS(RIGHT(tile) - est1->e_x0)) ? LEFT(tile) : RIGHT(tile); } /* If est2 y origin is floating, set to worst case */ if(est2->e_vCost == 0) { est2->e_y0 = (ABS(BOTTOM(tile) - est1->e_y0) > ABS(TOP(tile) - est1->e_y0)) ? BOTTOM(tile) : TOP(tile); } /* now compute the cost from est2 origin using est1 */ { dlong hCost, vCost, cost; if ((est1->e_hCost == INT_MAX) || (est1->e_vCost == INT_MAX)) return FALSE; hCost = (dlong) (est1->e_hCost * ABS(est2->e_x0 - est1->e_x0)); vCost = (dlong) (est1->e_vCost * ABS(est2->e_y0 - est1->e_y0)); cost = hCost + vCost; cost += est1->e_cost0; return (cost <= est2->e_cost0); } } } /* * ---------------------------------------------------------------------------- * * mzTrimEstimatesFunc -- * * Throw away redundant cost estimates. * * Results: * Returns 0 always. * * Side effects: * modifies estimates list in TileCost struc attached to tile. * * ---------------------------------------------------------------------------- */ int mzTrimEstimatesFunc(tile, notUsed) Tile *tile; ClientData notUsed; { TileCosts *tc = (TileCosts *) (tile->ti_client); Estimate *e; Estimate *reqEstimates = NULL; e = tc->tc_estimates; while(e) { Estimate *e2; bool found = FALSE; /* Check if a required estimate is always as good as e */ for(e2 = reqEstimates; e2!= NULL && !found; e2=e2->e_next) { if(AlwaysAsGood(e2,e,tile)) { found = TRUE; } } /* Check if a not-yet-processed estimate is always as good as e */ for(e2 = e->e_next; e2!= NULL && !found; e2=e2->e_next) { if(AlwaysAsGood(e2,e,tile)) { found = TRUE; } } /* Throw away e if redundant, else save on reqEstimates list, and * continue with next unprocessed estimate. */ { Estimate *eNext = e->e_next; if(found) /* Throw away */ { freeMagic((char *) e); } else /* Add to required list */ { e->e_next = reqEstimates; reqEstimates = e; } e = eNext; } } /* save required estimate list */ tc->tc_estimates = reqEstimates; /* return 0 - to continue traversal of estimate plane */ return(0); } /* * ---------------------------------------------------------------------------- * * mzSplitTiles -- * * Split space tiles in four directions from point - stopping at solid tiles. * * Results: * None * * Side effects: * Modifies tile structure of plane. * * ---------------------------------------------------------------------------- */ void mzSplitTiles(plane, point) Plane * plane; Point * point; /* origin from which tiles split */ { Tile *pointTile = TiSrPointNoHint(plane, point); Tile *t; int x = point->p_x; int y = point->p_y; /* Don't split from infinite points */ if(xMAX_FINITE_COORDINATE || yMAX_FINITE_COORDINATE) { return; } /* split tiles to right of point */ { /* init t to tile to right of pointTile */ NEXT_TILE_RIGHT(t,pointTile,y); while (TiGetType(t)==TT_SPACE && BOTTOM(t)!=y && t!=plane->pl_right) { /* split t */ t=TiSplitY(t, y); /* move one tile to right */ NEXT_TILE_RIGHT(t,t,y); } } /* split tiles up from point */ { /* init t to tile above pointTile */ NEXT_TILE_UP(t,pointTile,x) while (TiGetType(t)==TT_SPACE && LEFT(t)!=x && t!=plane->pl_top) { /* split t */ t=TiSplitX(t, x); /* move one tile up */ NEXT_TILE_UP(t,t,x); } } /* split tiles to left of point */ { /* init t to tile to left of pointTile */ NEXT_TILE_LEFT(t,pointTile,y); while (TiGetType(t)==TT_SPACE && BOTTOM(t)!=y && t!=plane->pl_left) { /* split t */ t = TiSplitY(t, y); /* move one tile to left */ NEXT_TILE_LEFT(t,t,y); } } /* split tiles down from point */ { /* init t to tile below pointTile */ NEXT_TILE_DOWN(t,pointTile,x); while (TiGetType(t)==TT_SPACE && LEFT(t)!=x && t!=plane->pl_bottom) { /* split t */ t=TiSplitX(t, x); /* move one tile down */ NEXT_TILE_DOWN(t,t,x); } } /* finally, if point is in a SPACE tile, split it in four */ if(TiGetType(pointTile)==TT_SPACE) { t = pointTile; if(x != LEFT(t)) { Tile *tOther = TiSplitX(t, x); if(y != BOTTOM(tOther)) TiSplitY(tOther, y); } if(y != BOTTOM(t)) TiSplitY(t, y); } return; } /* * ---------------------------------------------------------------------------- * * mzAssignVertexCosts -- * * Applies Djikstra's shortest path algorithm to compute minimum cost to * each tile corner, assuming cost along edge is minimum of cost associated * with adjacent tiles times length of the edge. (Hor costs for hor. edges, * vertical costs for vertical edges.) * * Treats estimate plane as a graph, with tile corners as vertices and * tile edges as nodes. Weights * * Results: * None. * * Side effects: * Fills in vertex costs in strucs hanging of clientData fields of * tiles in estimation plane. * * ---------------------------------------------------------------------------- */ void mzAssignVertexCosts() { Heap adjHeap; /* vertices adjacent to the IN set are put here */ HeapEntry buf, *he; Tile *t; /* Initialize Heap */ HeapInitType(&adjHeap, 1024, FALSE, FALSE, HE_DLONG); /* Initial at least one vertex of each dest term to zero cost and add * to adjHeap. Zero cost will be propagated to other vertices of dest * terms since hcost and vcost are 0 for dest tiles. */ { int mzDestInitialAssignFunc(); TileTypeBitMask destOnly; TTMaskSetOnlyType(&destOnly, TT_EST_DEST); DBSrPaintArea(NULL, /* no hint tile */ mzEstimatePlane, &mzBoundingRect, &destOnly, mzDestInitialAssignFunc, (ClientData) &adjHeap); } /* keep adding least cost ADJACENT vertex to IN until no ADJACENT vertices * left. (Vertices adjacent to the addvertex are added to adjHeap.) */ while((he = HeapRemoveTop(&adjHeap,&buf))!=NULL) { Vertex *v = (Vertex *)(he->he_id); if (!(v->vx_status & VX_IN)) { /* vertex not already IN, so process it */ mzAddVertex(v,&adjHeap); } } /* Free heap */ HeapKill(&adjHeap, (cb_heap_kill_t) NULL); return; } /* * ---------------------------------------------------------------------------- * * mzAddVertex -- * * Subroutine of mzAssignVertexCosts. * Adds least cost vertex on adjHeap to IN set. Adds vertices adjacent to * new IN vertex to adjHeap, or adjusts there cost if they are already there. * * Results: * None. * * Side effects: * Modifies adjHeap and vertex strucs attached to tiles * on estimation plane. * * ---------------------------------------------------------------------------- */ void mzAddVertex(vxThis, adjHeap) Vertex *vxThis; Heap *adjHeap; { Tile *tThis; /* Tile vxThis is attached to */ Point loc; /* location of vxThis */ Tile *tLoc; /* Tile containing location of vxThis */ Tile *tLeft, *tRight, *tAbove, *tBelow; /* Neighbors of tLoc */ /* Mark this vertex IN */ vxThis->vx_status |= VX_IN; /* Ignore if we're already at maximum cost */ if (vxThis->vx_cost == COST_MAX) return; /* compute location of this vertex, and tile containing that loc */ tThis = vxThis->vx_tile; switch (vxThis->vx_status & VX_CORNER) { case VX_L_LEFT: loc.p_x = LEFT(tThis); loc.p_y = BOTTOM(tThis); tLoc = tThis; break; case VX_L_RIGHT: loc.p_x = RIGHT(tThis); loc.p_y = BOTTOM(tThis); NEXT_TILE_RIGHT(tLoc, tThis, BOTTOM(tThis)); break; case VX_U_LEFT: loc.p_x = LEFT(tThis); loc.p_y = TOP(tThis); NEXT_TILE_UP(tLoc, tThis, LEFT(tThis)); break; } /* find tiles neighboring loc */ NEXT_TILE_LEFT(tLeft, tLoc, loc.p_y); NEXT_TILE_RIGHT(tRight, tLoc, loc.p_y); NEXT_TILE_UP(tAbove, tLoc, loc.p_x); NEXT_TILE_DOWN(tBelow, tLoc, loc.p_x); /* process adjacent vertex ABOVE */ { Vertex *vxAbove; int yAbove; /* Check for no edge above */ if(LEFT(tLoc)!=loc.p_x) goto noAbove; if(TOP(tLeft) < TOP(tLoc)) { /* T from left */ vxAbove = &(((TileCosts *)(RT(tLeft)->ti_client))->tc_vxLRight); yAbove = TOP(tLeft); } else { if(LEFT(tAbove)==LEFT(tLoc)) { /* no T */ vxAbove = &(((TileCosts *)(tAbove->ti_client))->tc_vxLLeft); yAbove = BOTTOM(tAbove); } else { /* T from bottom */ vxAbove = &(((TileCosts *)(tLoc->ti_client))->tc_vxULeft); yAbove = BOTTOM(tAbove); } } /* adjust cost */ { int rate, distance; dlong newCost; if(yAbove > MAX_FINITE_COORDINATE) goto noAbove; rate = MIN(((TileCosts *)(tLoc->ti_client))->tc_vCost, ((TileCosts *)(tLeft->ti_client))->tc_vCost); if(rate == INT_MAX) goto noAbove; distance = yAbove - loc.p_y; newCost = (dlong) (rate * distance); newCost += vxThis->vx_cost; if(newCost < vxAbove->vx_cost) { vxAbove->vx_cost = newCost; HeapAddDLong(adjHeap, newCost, (char *) vxAbove); } } noAbove:; } /* process adjacent vertex to RIGHT */ { Vertex *vxRight; int xRight; /* Check for no edge to RIGHT */ if(BOTTOM(tLoc)!=loc.p_y) goto noRight; if(RIGHT(tBelow) < RIGHT(tLoc)) { /* T from below */ vxRight = &(((TileCosts *)(TR(tBelow)->ti_client))->tc_vxULeft); xRight = RIGHT(tBelow); } else { if(BOTTOM(tRight)==BOTTOM(tLoc)) { /* no T */ vxRight = &(((TileCosts *)(tRight->ti_client))->tc_vxLLeft); xRight = LEFT(tRight); } else { /* T from left */ vxRight = &(((TileCosts *)(tLoc->ti_client))->tc_vxLRight); xRight = LEFT(tRight); } } /* adjust cost */ { int rate, distance; dlong newCost; if(xRight > MAX_FINITE_COORDINATE) goto noRight; rate = MIN( ((TileCosts *)(tLoc->ti_client))->tc_hCost, ((TileCosts *)(tBelow->ti_client))->tc_hCost); if(rate == INT_MAX) goto noRight; distance = xRight - loc.p_x; newCost = (dlong) (rate * distance); newCost += vxThis->vx_cost; if (newCost < vxRight->vx_cost) { vxRight->vx_cost = newCost; HeapAddDLong(adjHeap, newCost, (char *) vxRight); } } noRight:; } /* For going down and to the left, we want tiles to contain their * right and upper edges. Adjust tLoc and neighbors accordingly. * The trick is to center tLoc and neighbors around loc - (1,1). */ { Point locMinus; /* locMinus used to get tiles for loc, that contain top and right * edges. */ locMinus = loc; --(locMinus.p_x); --(locMinus.p_y); if(BOTTOM(tLoc)>locMinus.p_y) NEXT_TILE_DOWN(tLoc, tLoc, loc.p_x); if(LEFT(tLoc)>locMinus.p_x) NEXT_TILE_LEFT(tLoc, tLoc, locMinus.p_y); /* find tiles neighboring loc */ NEXT_TILE_LEFT(tLeft, tLoc, locMinus.p_y); NEXT_TILE_RIGHT(tRight, tLoc, locMinus.p_y); NEXT_TILE_UP(tAbove, tLoc, locMinus.p_x); NEXT_TILE_DOWN(tBelow, tLoc, locMinus.p_x); } /* process adjacent vertex BELOW */ { Vertex *vxBelow; int yBelow; /* Check for no edge below */ if(RIGHT(tLoc)!=loc.p_x) goto noBelow; if(BOTTOM(tRight) >= BOTTOM(tLoc)) { /* LowerLeft of tRight */ vxBelow = &(((TileCosts *)(tRight->ti_client))->tc_vxLLeft); yBelow = BOTTOM(tRight); } else { /* T from Left */ vxBelow = &(((TileCosts *)(tLoc->ti_client))->tc_vxLRight); yBelow = BOTTOM(tLoc); } /* adjust cost */ { int rate, distance; dlong newCost; if(yBelow < MIN_FINITE_COORDINATE) goto noBelow; rate = MIN( ((TileCosts *)(tLoc->ti_client))->tc_vCost, ((TileCosts *)(tRight->ti_client))->tc_vCost); if(rate == INT_MAX) goto noBelow; distance = loc.p_y - yBelow; newCost = (dlong) (rate * distance); newCost += vxThis->vx_cost; if(newCost < vxBelow->vx_cost) { vxBelow->vx_cost = newCost; HeapAddDLong(adjHeap, newCost, (char *) vxBelow); } } noBelow:; } /* process adjacent vertex to LEFT */ { Vertex *vxLeft; int xLeft; /* Check for no edge to Left */ if(TOP(tLoc)!=loc.p_y) goto noLeft; if(LEFT(tAbove) >= LEFT(tLoc)) { /* LowerLeft of tAbove */ vxLeft = &(((TileCosts *)(tAbove->ti_client))->tc_vxLLeft); xLeft = LEFT(tAbove); } else { /* T from Bottom */ vxLeft = &(((TileCosts *)(tLoc->ti_client))->tc_vxULeft); xLeft = LEFT(tLoc); } /* adjust cost */ { int rate, distance; dlong newCost; if(xLeft < MIN_FINITE_COORDINATE) goto noLeft; rate = MIN( ((TileCosts *)(tLoc->ti_client))->tc_hCost, ((TileCosts *)(tAbove->ti_client))->tc_hCost); if(rate == INT_MAX) goto noLeft; distance = loc.p_x - xLeft; newCost = (dlong) (rate * distance); newCost += vxThis->vx_cost; if(newCost < vxLeft->vx_cost) { vxLeft->vx_cost = newCost; HeapAddDLong(adjHeap, newCost, (char *) vxLeft); } } noLeft:; } } /* * ---------------------------------------------------------------------------- * * mzEstimatedCost -- * * Results: * Estimated cost of route from point to destination. * * Side effects: * None. * * ---------------------------------------------------------------------------- */ // changed from DoubleInt to dlong dlong mzEstimatedCost(point) Point *point; { Tile *t = TiSrPointNoHint(mzEstimatePlane, point); TileCosts *tc = ((TileCosts *) t->ti_client); Estimate *e; dlong bestCost; bestCost = COST_MAX; for (e=tc->tc_estimates; e!=NULL; e=e->e_next) { dlong hCost, vCost, cost; if (e->e_hCost == INT_MAX || e->e_vCost == INT_MAX) continue; hCost = (dlong)e->e_hCost * (dlong)ABS(point->p_x - e->e_x0); vCost = (dlong)e->e_vCost * (dlong)ABS(point->p_y - e->e_y0); cost = hCost + vCost; cost += e->e_cost0; if(cost < bestCost) bestCost = cost; } return bestCost; } /* * ---------------------------------------------------------------------------- * * mzDumpEstimates -- * * Dump info in estimate plane (for debugging). * * Results: * None. * * Side effects: * info written to file or via TxPrintf() * * ---------------------------------------------------------------------------- */ void mzDumpEstimates(area,fd) Rect *area; FILE *fd; { int mzDumpEstFunc(); if(mzEstimateExists) { /* Visit each tile in the Estimate plane - dumping associated info */ DBSrPaintArea(NULL, /* no hint tile */ mzEstimatePlane, area, &DBAllTypeBits, mzDumpEstFunc, (ClientData) fd); } else { TxPrintf("No estimate plane!\n"); TxPrintf("(Must ``:*ir deb noclean true'' and do a route first.)\n"); } return; } /* * ---------------------------------------------------------------------------- * * mzDumpEstFunc -- * * Filter function called via DBSrPaintArea on behalf of mzDumpEstimates() * above, for each estimate tile in the area of interest, * the info associated with each tile is dumped. * * Results: * Returns 0 always. * * Side effects: * Dumps info associated with tile. * * ---------------------------------------------------------------------------- */ int mzDumpEstFunc(tile, fd) Tile *tile; FILE *fd; { Rect r; TileCosts *tilec = (TileCosts *) tile->ti_client; /* Get boundary of tile */ TITORECT(tile, &r); /* dump info, to file if provided, else to screen */ if(fd) { fprintf(fd,"\ntile %p\t\t (x: %d to %d, y: %d to %d)\n", (void *) tile, r.r_xbot, r.r_xtop, r.r_ybot, r.r_ytop); fprintf(fd,"\thcost = %d ", tilec->tc_hCost); fprintf(fd,"vcost = %d \n", tilec->tc_vCost); { char str[100]; Estimate *e; fprintf(fd,"\tEstimates:\n"); for(e=tilec->tc_estimates; e!=NULL; e=e->e_next) { fprintf(fd,"\t\t%"DLONG_PREFIX"d + ABS(x - %d)*%d + ABS(y - %d)*%d\n", e->e_cost0,e->e_x0,e->e_hCost, e->e_y0,e->e_vCost); } } } else { TxPrintf("\ntile %lx\t\t (x: %d to %d, y: %d to %d)\n", (intmax_t) tile, r.r_xbot, r.r_xtop, r.r_ybot, r.r_ytop); TxPrintf("\thcost = %d, ", tilec->tc_hCost); TxPrintf("vcost = %d \n", tilec->tc_vCost); { char str[100]; Estimate *e; TxPrintf("\tEstimates:\n"); for(e=tilec->tc_estimates; e!=NULL; e=e->e_next) { TxPrintf("\t\t%lld + ABS(x - %d)*%d + ABS(y - %d)*%d\n", e->e_cost0,e->e_x0,e->e_hCost, e->e_y0,e->e_vCost); } } } /* continue search */ return (0); }