/* * Find the bounding box of a POLYAREA * POLYAREAs linked by ->f/b are outlines. n->contours->next would * be the start of the inner holes (irrelevant for bounding box) */ static BoxType POLYAREA_boundingBox(POLYAREA *a) { POLYAREA *n; PLINE *pa; BoxType box; int first = 1; n = a; do { pa = n->contours; if (first) { box.X1 = pa->xmin; box.X2 = pa->xmax + 1; box.Y1 = pa->ymin; box.Y2 = pa->ymax + 1; first = 0; } else { MAKEMIN(box.X1, pa->xmin); MAKEMAX(box.X2, pa->xmax + 1); MAKEMIN(box.Y1, pa->ymin); MAKEMAX(box.Y2, pa->ymax + 1); } } while ((n = n->f) != a); return box; }
/*! * \brief Set the node bounds large enough to encompass all of the * children's rectangles. */ static void adjust_bounds (struct rtree_node *node) { int i; assert (node); assert (node->u.kids[0]); if (node->flags.is_leaf) { node->box = node->u.rects[0].bounds; for (i = 1; i < M_SIZE + 1; i++) { if (!node->u.rects[i].bptr) return; MAKEMIN (node->box.X1, node->u.rects[i].bounds.X1); MAKEMAX (node->box.X2, node->u.rects[i].bounds.X2); MAKEMIN (node->box.Y1, node->u.rects[i].bounds.Y1); MAKEMAX (node->box.Y2, node->u.rects[i].bounds.Y2); } } else { node->box = node->u.kids[0]->box; for (i = 1; i < M_SIZE + 1; i++) { if (!node->u.kids[i]) return; MAKEMIN (node->box.X1, node->u.kids[i]->box.X1); MAKEMAX (node->box.X2, node->u.kids[i]->box.X2); MAKEMIN (node->box.Y1, node->u.kids[i]->box.Y1); MAKEMAX (node->box.Y2, node->u.kids[i]->box.Y2); } } }
/* --------------------------------------------------------------------------- * Compute cost function. * note that area overlap cost is correct for SMD devices: SMD devices on * opposite sides of the board don't overlap. * * Algorithms follow those described in sections 4.1 of * "Placement and Routing of Electronic Modules" edited by Michael Pecht * Marcel Dekker, Inc. 1993. ISBN: 0-8247-8916-4 TK7868.P7.P57 1993 */ static double ComputeCost (NetListTypePtr Nets, double T0, double T) { double W = 0; /* wire cost */ double delta1 = 0; /* wire congestion penalty function */ double delta2 = 0; /* module overlap penalty function */ double delta3 = 0; /* out of bounds penalty */ double delta4 = 0; /* alignment bonus */ double delta5 = 0; /* total area penalty */ Cardinal i, j; LocationType minx, maxx, miny, maxy; bool allpads, allsameside; Cardinal thegroup; BoxListType bounds = { 0, 0, NULL }; /* save bounding rectangles here */ BoxListType solderside = { 0, 0, NULL }; /* solder side component bounds */ BoxListType componentside = { 0, 0, NULL }; /* component side bounds */ /* make sure the NetList have the proper updated X and Y coords */ UpdateXY (Nets); /* wire length term. approximated by half-perimeter of minimum * rectangle enclosing the net. Note that we penalize vias in * all-SMD nets by making the rectangle a cube and weighting * the "layer height" of the net. */ for (i = 0; i < Nets->NetN; i++) { NetTypePtr n = &Nets->Net[i]; if (n->ConnectionN < 2) continue; /* no cost to go nowhere */ minx = maxx = n->Connection[0].X; miny = maxy = n->Connection[0].Y; thegroup = n->Connection[0].group; allpads = (n->Connection[0].type == PAD_TYPE); allsameside = true; for (j = 1; j < n->ConnectionN; j++) { ConnectionTypePtr c = &(n->Connection[j]); MAKEMIN (minx, c->X); MAKEMAX (maxx, c->X); MAKEMIN (miny, c->Y); MAKEMAX (maxy, c->Y); if (c->type != PAD_TYPE) allpads = false; if (c->group != thegroup) allsameside = false; } /* save bounding rectangle */ { BoxTypePtr box = GetBoxMemory (&bounds); box->X1 = minx; box->Y1 = miny; box->X2 = maxx; box->Y2 = maxy; } /* okay, add half-perimeter to cost! */ W += (maxx - minx) / 100 + (maxy - miny) / 100 + ((allpads && !allsameside) ? CostParameter.via_cost : 0); } /* now compute penalty function Wc which is proportional to * amount of overlap and congestion. */ /* delta1 is congestion penalty function */ delta1 = CostParameter.congestion_penalty * sqrt (fabs (ComputeIntersectionArea (&bounds))); #if 0 printf ("Wire Congestion Area: %f\n", ComputeIntersectionArea (&bounds)); #endif /* free bounding rectangles */ FreeBoxListMemory (&bounds); /* now collect module areas (bounding rect of pins/pads) */ /* two lists for solder side / component side. */ ELEMENT_LOOP (PCB->Data); { BoxListTypePtr thisside; BoxListTypePtr otherside; BoxTypePtr box; BoxTypePtr lastbox = NULL; BDimension thickness; BDimension clearance; if (TEST_FLAG (ONSOLDERFLAG, element)) { thisside = &solderside; otherside = &componentside; } else { thisside = &componentside; otherside = &solderside; } box = GetBoxMemory (thisside); /* protect against elements with no pins/pads */ if (element->PinN == 0 && element->PadN == 0) continue; /* initialize box so that it will take the dimensions of * the first pin/pad */ box->X1 = MAX_COORD; box->Y1 = MAX_COORD; box->X2 = -MAX_COORD; box->Y2 = -MAX_COORD; PIN_LOOP (element); { thickness = pin->Thickness / 2; clearance = pin->Clearance * 2; EXPANDRECTXY (box, pin->X - (thickness + clearance), pin->Y - (thickness + clearance), pin->X + (thickness + clearance), pin->Y + (thickness + clearance))} END_LOOP; PAD_LOOP (element); { thickness = pad->Thickness / 2; clearance = pad->Clearance * 2; EXPANDRECTXY (box, MIN (pad->Point1.X, pad->Point2.X) - (thickness + clearance), MIN (pad->Point1.Y, pad->Point2.Y) - (thickness + clearance), MAX (pad->Point1.X, pad->Point2.X) + (thickness + clearance), MAX (pad->Point1.Y, pad->Point2.Y) + (thickness + clearance))} END_LOOP; /* add a box for each pin to the "opposite side": * surface mount components can't sit on top of pins */ if (!CostParameter.fast) PIN_LOOP (element); { box = GetBoxMemory (otherside); thickness = pin->Thickness / 2; clearance = pin->Clearance * 2; /* we ignore clearance here */ /* (otherwise pins don't fit next to each other) */ box->X1 = pin->X - thickness; box->Y1 = pin->Y - thickness; box->X2 = pin->X + thickness; box->Y2 = pin->Y + thickness; /* speed hack! coalesce with last box if we can */ if (lastbox != NULL && ((lastbox->X1 == box->X1 && lastbox->X2 == box->X2 && MIN (abs (lastbox->Y1 - box->Y2), abs (box->Y1 - lastbox->Y2)) < clearance) || (lastbox->Y1 == box->Y1 && lastbox->Y2 == box->Y2 && MIN (abs (lastbox->X1 - box->X2), abs (box->X1 - lastbox->X2)) < clearance))) { EXPANDRECT (lastbox, box); otherside->BoxN--; } else lastbox = box; } END_LOOP; /* assess out of bounds penalty */ if (element->VBox.X1 < 0 || element->VBox.Y1 < 0 || element->VBox.X2 > PCB->MaxWidth || element->VBox.Y2 > PCB->MaxHeight) delta3 += CostParameter.out_of_bounds_penalty; } END_LOOP; /* compute intersection area of module areas box list */ delta2 = sqrt (fabs (ComputeIntersectionArea (&solderside) + ComputeIntersectionArea (&componentside))) * (CostParameter.overlap_penalty_min + (1 - (T / T0)) * CostParameter.overlap_penalty_max); #if 0 printf ("Module Overlap Area (solder): %f\n", ComputeIntersectionArea (&solderside)); printf ("Module Overlap Area (component): %f\n", ComputeIntersectionArea (&componentside)); #endif FreeBoxListMemory (&solderside); FreeBoxListMemory (&componentside); /* reward pin/pad x/y alignment */ /* score higher if pins/pads belong to same *type* of component */ /* XXX: subkey should be *distance* from thing aligned with, so that * aligning to something far away isn't profitable */ { /* create r tree */ PointerListType seboxes = { 0, 0, NULL } , ceboxes = { 0, 0, NULL}; struct ebox { BoxType box; ElementTypePtr element; }; direction_t dir[4] = { NORTH, EAST, SOUTH, WEST }; struct ebox **boxpp, *boxp; rtree_t *rt_s, *rt_c; int factor; ELEMENT_LOOP (PCB->Data); { boxpp = (struct ebox **) GetPointerMemory (TEST_FLAG (ONSOLDERFLAG, element) ? &seboxes : &ceboxes); *boxpp = malloc (sizeof (**boxpp)); if (*boxpp == NULL ) { fprintf (stderr, "malloc() failed in %s\n", __FUNCTION__); exit (1); } (*boxpp)->box = element->VBox; (*boxpp)->element = element; } END_LOOP; rt_s = r_create_tree ((const BoxType **) seboxes.Ptr, seboxes.PtrN, 1); rt_c = r_create_tree ((const BoxType **) ceboxes.Ptr, ceboxes.PtrN, 1); FreePointerListMemory (&seboxes); FreePointerListMemory (&ceboxes); /* now, for each element, find its neighbor on all four sides */ delta4 = 0; for (i = 0; i < 4; i++) ELEMENT_LOOP (PCB->Data); { boxp = (struct ebox *) r_find_neighbor (TEST_FLAG (ONSOLDERFLAG, element) ? rt_s : rt_c, &element->VBox, dir[i]); /* score bounding box alignments */ if (!boxp) continue; factor = 1; if (element->Name[0].TextString && boxp->element->Name[0].TextString && 0 == NSTRCMP (element->Name[0].TextString, boxp->element->Name[0].TextString)) { delta4 += CostParameter.matching_neighbor_bonus; factor++; } if (element->Name[0].Direction == boxp->element->Name[0].Direction) delta4 += factor * CostParameter.oriented_neighbor_bonus; if (element->VBox.X1 == boxp->element->VBox.X1 || element->VBox.X1 == boxp->element->VBox.X2 || element->VBox.X2 == boxp->element->VBox.X1 || element->VBox.X2 == boxp->element->VBox.X2 || element->VBox.Y1 == boxp->element->VBox.Y1 || element->VBox.Y1 == boxp->element->VBox.Y2 || element->VBox.Y2 == boxp->element->VBox.Y1 || element->VBox.Y2 == boxp->element->VBox.Y2) delta4 += factor * CostParameter.aligned_neighbor_bonus; } END_LOOP; /* free k-d tree memory */ r_destroy_tree (&rt_s); r_destroy_tree (&rt_c); } /* penalize total area used by this layout */ { LocationType minX = MAX_COORD, minY = MAX_COORD; LocationType maxX = -MAX_COORD, maxY = -MAX_COORD; ELEMENT_LOOP (PCB->Data); { MAKEMIN (minX, element->VBox.X1); MAKEMIN (minY, element->VBox.Y1); MAKEMAX (maxX, element->VBox.X2); MAKEMAX (maxY, element->VBox.Y2); } END_LOOP; if (minX < maxX && minY < maxY) delta5 = CostParameter.overall_area_penalty * sqrt ((double) (maxX - minX) * (maxY - minY) * 0.0001); } if (T == 5) { T = W + delta1 + delta2 + delta3 - delta4 + delta5; printf ("cost components are %.3f %.3f %.3f %.3f %.3f %.3f\n", W / T, delta1 / T, delta2 / T, delta3 / T, -delta4 / T, delta5 / T); } /* done! */ return W + (delta1 + delta2 + delta3 - delta4 + delta5); }
static void __r_insert_node (struct rtree_node *node, const BoxType * query, int manage, bool force) { #ifdef SLOW_ASSERTS assert (__r_node_is_good (node)); #endif /* Ok, at this point we must already enclose the query or we're forcing * this node to expand to enclose it, so if we're a leaf, simply store * the query here */ if (node->flags.is_leaf) { register int i; if (UNLIKELY (manage)) { register int flag = 1; for (i = 0; i < M_SIZE; i++) { if (!node->u.rects[i].bptr) break; flag <<= 1; } node->flags.manage |= flag; } else { for (i = 0; i < M_SIZE; i++) if (!node->u.rects[i].bptr) break; } /* the node always has an extra space available */ node->u.rects[i].bptr = query; node->u.rects[i].bounds = *query; /* first entry in node determines initial bounding box */ if (i == 0) node->box = *query; else if (force) { MAKEMIN (node->box.X1, query->X1); MAKEMAX (node->box.X2, query->X2); MAKEMIN (node->box.Y1, query->Y1); MAKEMAX (node->box.Y2, query->Y2); } if (i < M_SIZE) { sort_node (node); return; } /* we must split the node */ split_node (node); return; } else { int i; struct rtree_node *best_node; double score, best_score; if (force) { MAKEMIN (node->box.X1, query->X1); MAKEMAX (node->box.X2, query->X2); MAKEMIN (node->box.Y1, query->Y1); MAKEMAX (node->box.Y2, query->Y2); } /* this node encloses it, but it's not a leaf, so descend the tree */ /* First check if any children actually encloses it */ assert (node->u.kids[0]); for (i = 0; i < M_SIZE; i++) { if (!node->u.kids[i]) break; if (contained (node->u.kids[i], query)) { __r_insert_node (node->u.kids[i], query, manage, false); sort_node (node); return; } } /* see if there is room for a new leaf node */ if (node->u.kids[0]->flags.is_leaf && i < M_SIZE) { struct rtree_node *new_node; new_node = (struct rtree_node *)calloc (1, sizeof (*new_node)); new_node->parent = node; new_node->flags.is_leaf = true; node->u.kids[i] = new_node; new_node->u.rects[0].bptr = query; new_node->u.rects[0].bounds = *query; new_node->box = *query; if (UNLIKELY (manage)) new_node->flags.manage = 1; sort_node (node); return; } /* Ok, so we're still here - look for the best child to push it into */ best_score = penalty (node->u.kids[0], query); best_node = node->u.kids[0]; for (i = 1; i < M_SIZE; i++) { if (!node->u.kids[i]) break; score = penalty (node->u.kids[i], query); if (score < best_score) { best_score = score; best_node = node->u.kids[i]; } } __r_insert_node (best_node, query, manage, true); sort_node (node); return; } }