static seg_t *FindSegFromStaleNode(superblock_t *part_list, node_t *stale_nd, int *stale_opposite) { seg_t *part; int num; for (part=part_list->segs; part; part = part->next) { float_g fa, fb; /* ignore minisegs as partition candidates */ if (! part->linedef) continue; fa = fabs(UtilPerpDist(part, stale_nd->x, stale_nd->y)); fb = fabs(UtilPerpDist(part, stale_nd->x + stale_nd->dx, stale_nd->y + stale_nd->dy)); if (fa < DIST_EPSILON && fb < DIST_EPSILON) { /* OK found it. check if runs in same direction */ (*stale_opposite) = (stale_nd->dx * part->pdx + stale_nd->dy * part->pdy < 0) ? 1 : 0; return part; } } /* handle sub-blocks recursively */ for (num=0; num < 2; num++) { if (! part_list->subs[num]) continue; part = FindSegFromStaleNode(part_list->subs[num], stale_nd, stale_opposite); if (part) return part; } return NULL; }
// // DivideOneSeg // // Apply the partition line to the given seg, taking the necessary // action (moving it into either the left list, right list, or // splitting it). // // -AJA- I have rewritten this routine based on the EvalPartition // routine above (which I've also reworked, heavily). I think // it is important that both these routines follow the exact // same logic when determining which segs should go left, right // or be split. // void DivideOneSeg(seg_t *cur, seg_t *part, superblock_t *left_list, superblock_t *right_list, intersection_t ** cut_list) { seg_t *new_seg; float_g x, y; /* get state of lines' relation to each other */ float_g a = UtilPerpDist(part, cur->psx, cur->psy); float_g b = UtilPerpDist(part, cur->pex, cur->pey); boolean_g self_ref = cur->linedef ? cur->linedef->self_ref : FALSE; if (cur->source_line == part->source_line) a = b = 0; /* check for being on the same line */ if (fabs(a) <= DIST_EPSILON && fabs(b) <= DIST_EPSILON) { AddIntersection(cut_list, cur->start, part, self_ref); AddIntersection(cut_list, cur->end, part, self_ref); // this seg runs along the same line as the partition. check // whether it goes in the same direction or the opposite. if (cur->pdx*part->pdx + cur->pdy*part->pdy < 0) { AddSegToSuper(left_list, cur); } else { AddSegToSuper(right_list, cur); } return; } /* check for right side */ if (a > -DIST_EPSILON && b > -DIST_EPSILON) { if (a < DIST_EPSILON) AddIntersection(cut_list, cur->start, part, self_ref); else if (b < DIST_EPSILON) AddIntersection(cut_list, cur->end, part, self_ref); AddSegToSuper(right_list, cur); return; } /* check for left side */ if (a < DIST_EPSILON && b < DIST_EPSILON) { if (a > -DIST_EPSILON) AddIntersection(cut_list, cur->start, part, self_ref); else if (b > -DIST_EPSILON) AddIntersection(cut_list, cur->end, part, self_ref); AddSegToSuper(left_list, cur); return; } // when we reach here, we have a and b non-zero and opposite sign, // hence this seg will be split by the partition line. ComputeIntersection(cur, part, a, b, &x, &y); new_seg = SplitSeg(cur, x, y); AddIntersection(cut_list, cur->end, part, self_ref); if (a < 0) { AddSegToSuper(left_list, cur); AddSegToSuper(right_list, new_seg); } else { AddSegToSuper(right_list, cur); AddSegToSuper(left_list, new_seg); } }
// // EvalPartitionWorker // // Returns TRUE if a "bad seg" was found early. // static int EvalPartitionWorker(superblock_t *seg_list, seg_t *part, int best_cost, eval_info_t *info) { seg_t *check; float_g qnty; float_g a, b, fa, fb; int num; int factor = cur_info->factor; // -AJA- this is the heart of my superblock idea, it tests the // _whole_ block against the partition line to quickly handle // all the segs within it at once. Only when the partition // line intercepts the box do we need to go deeper into it. num = BoxOnLineSide(seg_list, part); if (num < 0) { // LEFT info->real_left += seg_list->real_num; info->mini_left += seg_list->mini_num; return FALSE; } else if (num > 0) { // RIGHT info->real_right += seg_list->real_num; info->mini_right += seg_list->mini_num; return FALSE; } # define ADD_LEFT() \ do { \ if (check->linedef) info->real_left += 1; \ else info->mini_left += 1; \ } while (0) # define ADD_RIGHT() \ do { \ if (check->linedef) info->real_right += 1; \ else info->mini_right += 1; \ } while (0) /* check partition against all Segs */ for (check=seg_list->segs; check; check=check->next) { // This is the heart of my pruning idea - it catches // bad segs early on. Killough if (info->cost > best_cost) return TRUE; /* get state of lines' relation to each other */ if (check->source_line == part->source_line) { a = b = fa = fb = 0; } else { a = UtilPerpDist(part, check->psx, check->psy); b = UtilPerpDist(part, check->pex, check->pey); fa = fabs(a); fb = fabs(b); } /* check for being on the same line */ if (fa <= DIST_EPSILON && fb <= DIST_EPSILON) { // this seg runs along the same line as the partition. Check // whether it goes in the same direction or the opposite. if (check->pdx*part->pdx + check->pdy*part->pdy < 0) { ADD_LEFT(); } else { ADD_RIGHT(); } continue; } // -AJA- check for passing through a vertex. Normally this is fine // (even ideal), but the vertex could on a sector that we // DONT want to split, and the normal linedef-based checks // may fail to detect the sector being cut in half. Thanks // to Janis Legzdinsh for spotting this obscure bug. if (fa <= DIST_EPSILON || fb <= DIST_EPSILON) { if (check->linedef && check->linedef->is_precious) info->cost += 40 * factor * PRECIOUS_MULTIPLY; } /* check for right side */ if (a > -DIST_EPSILON && b > -DIST_EPSILON) { ADD_RIGHT(); /* check for a near miss */ if ((a >= IFFY_LEN && b >= IFFY_LEN) || (a <= DIST_EPSILON && b >= IFFY_LEN) || (b <= DIST_EPSILON && a >= IFFY_LEN)) { continue; } info->near_miss++; // -AJA- near misses are bad, since they have the potential to // cause really short minisegs to be created in future // processing. Thus the closer the near miss, the higher // the cost. if (a <= DIST_EPSILON || b <= DIST_EPSILON) qnty = IFFY_LEN / MAX(a, b); else qnty = IFFY_LEN / MIN(a, b); info->cost += (int) (100 * factor * (qnty * qnty - 1.0)); continue; } /* check for left side */ if (a < DIST_EPSILON && b < DIST_EPSILON) { ADD_LEFT(); /* check for a near miss */ if ((a <= -IFFY_LEN && b <= -IFFY_LEN) || (a >= -DIST_EPSILON && b <= -IFFY_LEN) || (b >= -DIST_EPSILON && a <= -IFFY_LEN)) { continue; } info->near_miss++; // the closer the miss, the higher the cost (see note above) if (a >= -DIST_EPSILON || b >= -DIST_EPSILON) qnty = IFFY_LEN / -MIN(a, b); else qnty = IFFY_LEN / -MAX(a, b); info->cost += (int) (70 * factor * (qnty * qnty - 1.0)); continue; } // When we reach here, we have a and b non-zero and opposite sign, // hence this seg will be split by the partition line. info->splits++; // If the linedef associated with this seg has a tag >= 900, treat // it as precious; i.e. don't split it unless all other options // are exhausted. This is used to protect deep water and invisible // lifts/stairs from being messed up accidentally by splits. if (check->linedef && check->linedef->is_precious) info->cost += 100 * factor * PRECIOUS_MULTIPLY; else info->cost += 100 * factor; // -AJA- check if the split point is very close to one end, which // is quite an undesirable situation (producing really short // segs). This is perhaps _one_ source of those darn slime // trails. Hence the name "IFFY segs", and a rather hefty // surcharge :->. if (fa < IFFY_LEN || fb < IFFY_LEN) { info->iffy++; // the closer to the end, the higher the cost qnty = IFFY_LEN / MIN(fa, fb); info->cost += (int) (140 * factor * (qnty * qnty - 1.0)); } } /* handle sub-blocks recursively */ for (num=0; num < 2; num++) { if (! seg_list->subs[num]) continue; if (EvalPartitionWorker(seg_list->subs[num], part, best_cost, info)) return TRUE; } /* no "bad seg" was found */ return FALSE; }