From 36f61ca6010cc1d46ffec07606213b990a865235 Mon Sep 17 00:00:00 2001 From: Tim Edwards Date: Mon, 7 Jun 2021 16:38:12 -0400 Subject: [PATCH] Modified the CIF/GDS array processing in CIFhier.c, finding that there were incorrect assumptions made in the code from 35 years back or so, for the case where the CIF layer halo is larger than the size/separation of the cells in the array. The new code will prevent the array routine from copying hierarchical additions to the mask layers outside of the array area. Whether or not the new code has its own faulty assumptions remains to be seen through thorough vetting. --- VERSION | 2 +- cif/CIFhier.c | 288 ++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 207 insertions(+), 83 deletions(-) diff --git a/VERSION b/VERSION index 90d348e0..683e77d6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.3.175 +8.3.176 diff --git a/cif/CIFhier.c b/cif/CIFhier.c index 9d2860e5..e7219ed5 100644 --- a/cif/CIFhier.c +++ b/cif/CIFhier.c @@ -911,6 +911,52 @@ CIFGenSubcells(def, area, output) UndoEnable(); } +/* + * ---------------------------------------------------------------------------- + * + * ---------------------------------------------------------------------------- + */ + +int +cifHierElementFuncLow(use, transform, x, y, checkArea) + CellUse *use; /* CellUse being array-checked. */ + Transform *transform; /* Transform from this instance to + * the parent. + */ + int x, y; /* Indices of this instance. */ + Rect *checkArea; /* Area (in parent coords) to be + * CIF-generated. + */ +{ + if (((x - use->cu_xlo) < 2) && ((y - use->cu_ylo) < 2)) + return cifHierElementFunc(use, transform, x, y, checkArea); + else + return 0; +} + +/* + * ---------------------------------------------------------------------------- + * + * ---------------------------------------------------------------------------- + */ + +int +cifHierElementFuncHigh(use, transform, x, y, checkArea) + CellUse *use; /* CellUse being array-checked. */ + Transform *transform; /* Transform from this instance to + * the parent. + */ + int x, y; /* Indices of this instance. */ + Rect *checkArea; /* Area (in parent coords) to be + * CIF-generated. + */ +{ + if (((use->cu_xhi - x) < 2) && ((use->cu_yhi - y) < 2)) + return cifHierElementFunc(use, transform, x, y, checkArea); + else + return 0; +} + /* * ---------------------------------------------------------------------------- * @@ -1139,7 +1185,6 @@ cifHierArrayFunc(scx, output) Rect childArea, parentArea, A, B, C, D, expandedArea; CellUse *use; int radius, xsep, ysep, xsize, ysize, nx, ny, i, oldTileOps; - int xhi, yhi; bool anyInteractions = FALSE; use = scx->scx_use; @@ -1147,17 +1192,9 @@ cifHierArrayFunc(scx, output) if ((use->cu_xlo == use->cu_xhi) && (use->cu_ylo == use->cu_yhi)) return 2; - /* We only want interactions between neighboring cells, so reduce */ - /* the array size to at most 2x2, process, then restore the */ - /* original array count. */ - - xhi = use->cu_xhi; - yhi = use->cu_yhi; - - if (use->cu_xlo != use->cu_xhi) - use->cu_xhi = use->cu_xlo + ((use->cu_xlo < use->cu_xhi) ? 1 : -1); - if (use->cu_ylo != use->cu_yhi) - use->cu_yhi = use->cu_ylo + ((use->cu_ylo < use->cu_yhi) ? 1 : -1); + /* We only want interactions between neighboring cells, so only */ + /* look at the bottom-left 2x2 set when calculating A and B, and */ + /* only look at the top-right 2x2 set when calculating C and D. */ /* Compute the sizes and separations of elements, in coordinates * of the parent. If the array is 1-dimensional, we set the @@ -1200,6 +1237,12 @@ cifHierArrayFunc(scx, output) * into CIFTotalPlanes. Note: in each case we have to yank a larger * area than we check, in order to include material that will be * bloated or shrunk. + * + * (Updated 6/7/2021) A and B should be calculated and processed + * completely independently of C and D, or else if the array is + * small and the radius is large, then results from one will get + * picked up when making copies of the other, resulting in things + * getting painted out-of-bounds. */ /* A */ @@ -1211,7 +1254,7 @@ cifHierArrayFunc(scx, output) A.r_ybot = use->cu_bbox.r_ybot + ysep - radius; A.r_ytop = use->cu_bbox.r_ybot + ysize + radius; GEO_EXPAND(&A, CIFCurStyle->cs_radius, &expandedArea); - (void) DBArraySr(use, &expandedArea, cifHierElementFunc, + (void) DBArraySr(use, &expandedArea, cifHierElementFuncLow, (ClientData) &A); CIFErrorDef = use->cu_parent; CIFGen(CIFTotalDef, use->cu_def, &A, CIFTotalPlanes, @@ -1220,24 +1263,6 @@ cifHierArrayFunc(scx, output) anyInteractions = TRUE; } - /* C */ - - if (xsep < xsize + radius) - { - C.r_xbot = use->cu_bbox.r_xtop - xsize - radius; - C.r_xtop = use->cu_bbox.r_xtop - xsep + radius; - C.r_ybot = use->cu_bbox.r_ytop - ysize - radius; - C.r_ytop = use->cu_bbox.r_ytop + radius; - GEO_EXPAND(&C, CIFCurStyle->cs_radius, &expandedArea); - (void) DBArraySr(use, &expandedArea, cifHierElementFunc, - (ClientData) &C); - CIFErrorDef = use->cu_parent; - CIFGen(CIFTotalDef, use->cu_def, &C, CIFTotalPlanes, - &CIFCurStyle->cs_hierLayers, FALSE, TRUE, TRUE, - (ClientData)NULL); - anyInteractions = TRUE; - } - if ((xsep < xsize + radius) && (ysep < ysize + radius)) { /* B */ @@ -1247,31 +1272,16 @@ cifHierArrayFunc(scx, output) B.r_ybot = use->cu_bbox.r_ybot - radius; B.r_ytop = use->cu_bbox.r_ybot + ysep - radius; GEO_EXPAND(&B, CIFCurStyle->cs_radius, &expandedArea); - (void) DBArraySr(use, &expandedArea, cifHierElementFunc, + (void) DBArraySr(use, &expandedArea, cifHierElementFuncLow, (ClientData) &B); CIFErrorDef = use->cu_parent; CIFGen(CIFTotalDef, use->cu_def, &B, CIFTotalPlanes, &CIFCurStyle->cs_hierLayers, FALSE, TRUE, TRUE, (ClientData)NULL); - - /* D */ - - D.r_xbot = use->cu_bbox.r_xtop - xsep + radius; - D.r_xtop = use->cu_bbox.r_xtop + radius; - D.r_ybot = use->cu_bbox.r_ytop - ysize - radius; - D.r_ytop = use->cu_bbox.r_ytop - ysep + radius; - GEO_EXPAND(&D, CIFCurStyle->cs_radius, &expandedArea); - (void) DBArraySr(use, &expandedArea, cifHierElementFunc, - (ClientData) &D); - CIFErrorDef = use->cu_parent; - CIFGen(CIFTotalDef, use->cu_def, &D, CIFTotalPlanes, - &CIFCurStyle->cs_hierLayers, FALSE, TRUE, TRUE, - (ClientData)NULL); } if (anyInteractions) { - /* Remove redundant CIF that's already in the children, and * make sure everything in the kids is in the parent too. */ @@ -1308,6 +1318,155 @@ cifHierArrayFunc(scx, output) (ClientData) NULL); } + if ((nx > 1) && (ny > 1) && (xsep < xsize + radius) + && (ysep < ysize + radius)) + { + /* The bottom edge of the array (from B). */ + + cifHierXSpacing = xsep * scale; + cifHierYSpacing = 0; + cifHierXCount = nx-1; + cifHierYCount = 1; + SCALE(&B, scale, &cifArea); + (void) DBSrPaintArea((Tile *) NULL, CIFTotalPlanes[i], + &cifArea, &CIFSolidBits, cifHierPaintArrayFunc, + (ClientData) NULL); + + /* The core of the array (copied from A and B). Copy area + * from A, but not to exceed 1/2 Y array separation, into the + * inside area of the array. If the expanded area is > 1/2 + * the array separation, then there is no need to copy from + * area B. Otherwise, copy area from B, extending up to + * the bottom of the area just copied, and not to exceed + * 1/2 the X array separation. + */ + + cifHierXSpacing = xsep * scale; + cifHierYSpacing = ysep * scale; + cifHierXCount = nx-1; + cifHierYCount = ny-1; + + /* Find top edge of cell */ + A.r_xbot = use->cu_bbox.r_xbot; + A.r_xtop = use->cu_bbox.r_xbot + xsep; + A.r_ybot = use->cu_bbox.r_ybot + ysep; + A.r_ytop = use->cu_bbox.r_ybot + ysize; + /* Expand up/down but not by more than 1/2 ysep */ + if ((2 * radius) > ysep) + { + A.r_ybot -= (ysep >> 1); + A.r_ytop += (ysep >> 1); + } + else + { + A.r_ybot -= radius; + A.r_ytop += radius; + } + SCALE(&A, scale, &cifArea); + (void) DBSrPaintArea((Tile *) NULL, CIFTotalPlanes[i], + &cifArea, &CIFSolidBits, cifHierPaintArrayFunc, + (ClientData) NULL); + + /* If the radius is more than half of ysep then there */ + /* is nothing left that needs to be copied. */ + + if ((2 * radius) < ysep) + { + /* Find right edge of cell */ + B.r_ybot = use->cu_bbox.r_ybot; + B.r_ytop = use->cu_bbox.r_ybot + ysep; + + if (B.r_ytop > A.r_ybot) B.r_ytop = A.r_ybot; + + B.r_xbot = use->cu_bbox.r_xbot + xsep; + B.r_xtop = use->cu_bbox.r_xbot + xsize; + /* Expand left/right but not by more than 1/2 xsep */ + if ((2 * radius) > xsep) + { + B.r_xbot -= (xsep >> 1); + B.r_xtop += (xsep >> 1); + } + else + { + B.r_xbot -= radius; + B.r_xtop += radius; + } + SCALE(&B, scale, &cifArea); + (void) DBSrPaintArea((Tile *) NULL, CIFTotalPlanes[i], + &cifArea, &CIFSolidBits, cifHierPaintArrayFunc, + (ClientData) NULL); + } + } + } + CIFHierRects += CIFTileOps - oldTileOps; + } + + /* Clean up from areas A and B */ + cifHierCleanup(); + anyInteractions = FALSE; + + /* Now do areas C and D */ + + /* C */ + + if (xsep < xsize + radius) + { + C.r_xbot = use->cu_bbox.r_xtop - xsize - radius; + C.r_xtop = use->cu_bbox.r_xtop - xsep + radius; + C.r_ybot = use->cu_bbox.r_ytop - ysize - radius; + C.r_ytop = use->cu_bbox.r_ytop + radius; + GEO_EXPAND(&C, CIFCurStyle->cs_radius, &expandedArea); + (void) DBArraySr(use, &expandedArea, cifHierElementFuncHigh, + (ClientData) &C); + CIFErrorDef = use->cu_parent; + CIFGen(CIFTotalDef, use->cu_def, &C, CIFTotalPlanes, + &CIFCurStyle->cs_hierLayers, FALSE, TRUE, TRUE, + (ClientData)NULL); + anyInteractions = TRUE; + } + + if ((xsep < xsize + radius) && (ysep < ysize + radius)) + { + /* D */ + + D.r_xbot = use->cu_bbox.r_xtop - xsep + radius; + D.r_xtop = use->cu_bbox.r_xtop + radius; + D.r_ybot = use->cu_bbox.r_ytop - ysize - radius; + D.r_ytop = use->cu_bbox.r_ytop - ysep + radius; + GEO_EXPAND(&D, CIFCurStyle->cs_radius, &expandedArea); + (void) DBArraySr(use, &expandedArea, cifHierElementFuncHigh, + (ClientData) &D); + CIFErrorDef = use->cu_parent; + CIFGen(CIFTotalDef, use->cu_def, &D, CIFTotalPlanes, + &CIFCurStyle->cs_hierLayers, FALSE, TRUE, TRUE, + (ClientData)NULL); + } + + if (anyInteractions) + { + + /* Remove redundant CIF that's already in the children, and + * make sure everything in the kids is in the parent too. + */ + + CIFErrorDef = use->cu_parent; + cifCheckAndErase(CIFCurStyle); + + /* Lastly, paint everything back from our local planes into + * the planes of the caller. In doing this, stuff has to + * be replicated many times over to cover each of the array + * interaction areas. + */ + + oldTileOps = CIFTileOps; + for (i=0; ics_nLayers; i++) + { + int scale = CIFCurStyle->cs_scaleFactor; + Rect cifArea; + + cifHierCurPlane = output[i]; + CurCifLayer = CIFCurStyle->cs_layers[i]; /* for growSliver */ + /* The top edge of the array (from C). */ if ((nx > 1) && (xsep < xsize + radius)) @@ -1325,17 +1484,6 @@ cifHierArrayFunc(scx, output) if ((nx > 1) && (ny > 1) && (xsep < xsize + radius) && (ysep < ysize + radius)) { - /* The bottom edge of the array (from B). */ - - cifHierXSpacing = xsep * scale; - cifHierYSpacing = 0; - cifHierXCount = nx-1; - cifHierYCount = 1; - SCALE(&B, scale, &cifArea); - (void) DBSrPaintArea((Tile *) NULL, CIFTotalPlanes[i], - &cifArea, &CIFSolidBits, cifHierPaintArrayFunc, - (ClientData) NULL); - /* The right edge of the array (from D). */ cifHierXSpacing = 0; @@ -1346,36 +1494,12 @@ cifHierArrayFunc(scx, output) (void) DBSrPaintArea((Tile *) NULL, CIFTotalPlanes[i], &cifArea, &CIFSolidBits, cifHierPaintArrayFunc, (ClientData) NULL); - - /* The core of the array (from A and B). This code is a bit - * tricky in order to work correctly even for arrays where - * radius < ysep. The "if" statement handles this case. - */ - - cifHierXSpacing = xsep * scale; - cifHierYSpacing = ysep * scale; - cifHierXCount = nx-1; - cifHierYCount = ny-1; - parentArea.r_xbot = A.r_xtop - xsep; - parentArea.r_ybot = A.r_ytop - ysep; - if (parentArea.r_ybot > B.r_ytop) parentArea.r_ybot = B.r_ytop; - parentArea.r_xtop = A.r_xtop; - parentArea.r_ytop = A.r_ytop; - SCALE(&parentArea, scale, &cifArea); - (void) DBSrPaintArea((Tile *) NULL, CIFTotalPlanes[i], - &cifArea, &CIFSolidBits, cifHierPaintArrayFunc, - (ClientData) NULL); } } CIFHierRects += CIFTileOps - oldTileOps; } cifHierCleanup(); - - /* Restore the array bounds of the array */ - use->cu_xhi = xhi; - use->cu_yhi = yhi; - return 2; }