Пример #1
0
 public function union(Polygon $subject, Polygon $clip)
 {
     $hState = new HorizontalEdgeStates();
     $result = new Polygon();
     if ($subject->isEmpty() && $clip->isEmpty()) {
         return $result;
     }
     $lmtTable = new LocalMinimumTable();
     $sbte = new ScanBeamTreeEntries();
     $s_heap = null;
     $c_heap = null;
     if (!$subject->isEmpty()) {
         $sHeap = $this->buildLmt($lmtTable, $sbte, $subject, self::SUBJ);
     }
     if (!$clip->isEmpty()) {
         $cHeap = $this->buildLmt($lmtTable, $sbte, $clip, self::CLIP);
     }
     if ($lmtTable->getTopNode() === null) {
         return $result;
     }
     /* Build scanbeam table from scanbeam tree */
     $sbt = $sbte->buildSbt();
     $parity = [];
     $parity[0] = self::LEFT;
     $parity[1] = self::LEFT;
     $localMin = $lmtTable->getTopNode();
     $outPoly = new TopPolygonNode();
     //used to create resulting polygon
     $aet = new AetTree();
     $scanbeam = 0;
     /* Process each scanbeam */
     while ($scanbeam < count($sbt)) {
         echo '$scanbeam : ' . $scanbeam . ', count($sbt): ' . count($sbt) . PHP_EOL;
         /* Set yb and yt to the bottom and top of the scanbeam */
         $yb = $sbt[$scanbeam++];
         $yt = 0.0;
         $dy = 0.0;
         if ($scanbeam < count($sbt)) {
             $yt = $sbt[$scanbeam];
             $dy = $yt - $yb;
         }
         /* === SCANBEAM BOUNDARY PROCESSING ================================ */
         /* If LMT node corresponding to yb exists */
         if ($localMin != null) {
             if ($localMin->getY() === $yb) {
                 /* Add edges starting at this local minimum to the AET */
                 for ($edge = $localMin->getFirstBound(); $edge != null; $edge = $edge->getNextBound()) {
                     $aet = $this->addEdgeToAet($aet, $edge);
                 }
                 $localMin = $localMin->getNext();
             }
         }
         //            $aet->printTree();
         /* Set dummy previous x value */
         $px = -INF;
         /* Create bundles within AET */
         $e0 = $aet->getTopNode();
         $e1 = $aet->getTopNode();
         /* Set up bundle fields of first edge */
         $aet->getTopNode()->bundle[self::ABOVE][$aet->getTopNode()->getType()] = $aet->getTopNode()->getTop()->getY() !== $yb ? 1 : 0;
         $aet->getTopNode()->bundle[self::ABOVE][$aet->getTopNode()->getType() === 0 ? 1 : 0] = 0;
         $aet->getTopNode()->bstate[self::ABOVE] = BundleState::unbundled();
         for ($nextEdge = $aet->getTopNode()->getNext(); $nextEdge != null; $nextEdge = $nextEdge->getNext()) {
             $neType = $nextEdge->getType();
             $neTypeOpp = $nextEdge->getType() === 0 ? 1 : 0;
             //next edge type opposite
             /* Set up bundle fields of next edge */
             $nextEdge->bundle[self::ABOVE][$neType] = $nextEdge->getTop()->getY() !== $yb ? 1 : 0;
             $nextEdge->bundle[self::ABOVE][$neTypeOpp] = 0;
             $nextEdge->bstate[self::ABOVE] = BundleState::unbundled();
             /* Bundle edges above the scanbeam boundary if they coincide */
             if ($nextEdge->bundle[self::ABOVE][$neType] === 1) {
                 if (self::eq($e0->getXb(), $nextEdge->getXb()) && self::eq($e0->getDx(), $nextEdge->getDx()) && $e0->getTop()->getY() != $yb) {
                     $nextEdge->bundle[self::ABOVE][$neType] ^= $e0->bundle[self::ABOVE][$neType];
                     $nextEdge->bundle[self::ABOVE][$neTypeOpp] = $e0->bundle[self::ABOVE][$neTypeOpp];
                     $nextEdge->bstate[self::ABOVE] = BundleState::bundleHead();
                     $e0->bundle[self::ABOVE][self::CLIP] = 0;
                     $e0->bundle[self::ABOVE][self::SUBJ] = 0;
                     $e0->bstate[self::ABOVE] = BundleState::bundleTail();
                 }
                 $e0 = $nextEdge;
             }
         }
         $horiz = [];
         $horiz[self::CLIP] = HorizontalEdgeStates::NH;
         $horiz[self::SUBJ] = HorizontalEdgeStates::NH;
         $exists = [];
         $exists[self::CLIP] = 0;
         $exists[self::SUBJ] = 0;
         /** @var PolygonNode|null $cf */
         $cf = null;
         /* Process each edge at this scanbeam boundary */
         for ($edge = $aet->getTopNode(); $edge != null; $edge = $edge->getNext()) {
             echo $edge->getVertex() . PHP_EOL;
             $exists[self::CLIP] = $edge->bundle[self::ABOVE][self::CLIP] + ($edge->bundle[self::BELOW][self::CLIP] << 1);
             $exists[self::SUBJ] = $edge->bundle[self::ABOVE][self::SUBJ] + ($edge->bundle[self::BELOW][self::SUBJ] << 1);
             if ($exists[self::CLIP] !== 0 || $exists[self::SUBJ] !== 0) {
                 /* Set bundle side */
                 $edge->bside[self::CLIP] = $parity[self::CLIP];
                 $edge->bside[self::SUBJ] = $parity[self::SUBJ];
                 $contributing = false;
                 $br = 0;
                 $bl = 0;
                 $tr = 0;
                 $tl = 0;
                 $contributing = $exists[self::CLIP] !== 0 && (!($parity[self::SUBJ] !== 0) || $horiz[self::SUBJ] !== 0) || $exists[self::SUBJ] !== 0 && (!($parity[self::CLIP] !== 0) || $horiz[self::CLIP] !== 0) || $exists[self::CLIP] !== 0 && $exists[self::SUBJ] !== 0 && $parity[self::CLIP] === $parity[self::SUBJ];
                 $br = $parity[self::CLIP] !== 0 || $parity[self::SUBJ] !== 0 ? 1 : 0;
                 $bl = ($parity[self::CLIP] ^ $edge->bundle[self::ABOVE][self::CLIP]) !== 0 || ($parity[self::SUBJ] ^ $edge->bundle[self::ABOVE][self::SUBJ]) !== 0 ? 1 : 0;
                 $tr = ($parity[self::CLIP] ^ ($horiz[self::CLIP] !== HorizontalEdgeStates::NH ? 1 : 0)) !== 0 || ($parity[self::SUBJ] ^ ($horiz[self::SUBJ] !== HorizontalEdgeStates::NH ? 1 : 0)) !== 0 ? 1 : 0;
                 $tl = ($parity[self::CLIP] ^ ($horiz[self::CLIP] !== HorizontalEdgeStates::NH ? 1 : 0) ^ $edge->bundle[self::BELOW][self::CLIP]) !== 0 || ($parity[self::SUBJ] ^ ($horiz[self::SUBJ] !== HorizontalEdgeStates::NH ? 1 : 0) ^ $edge->bundle[self::BELOW][self::SUBJ]) !== 0 ? 1 : 0;
                 /* Update parity */
                 $parity[self::CLIP] ^= $edge->bundle[self::ABOVE][self::CLIP];
                 $parity[self::SUBJ] ^= $edge->bundle[self::ABOVE][self::SUBJ];
                 /* Update horizontal state */
                 if ($exists[self::CLIP] !== 0) {
                     $horiz[self::CLIP] = $hState->nextState[$horiz[self::CLIP]][($exists[self::CLIP] - 1 << 1) + $parity[self::CLIP]];
                 }
                 if ($exists[self::SUBJ] !== 0) {
                     $horiz[self::SUBJ] = $hState->nextState[$horiz[self::SUBJ]][($exists[self::SUBJ] - 1 << 1) + $parity[self::SUBJ]];
                 }
                 if ($contributing) {
                     $xb = $edge->getXb();
                     $vClass = VertexType::getType($tr, $tl, $br, $bl);
                     switch ($vClass) {
                         case VertexType::EMN:
                         case VertexType::IMN:
                             $edge->outp[self::ABOVE] = $outPoly->addLocalMin($xb, $yb);
                             $px = $xb;
                             $cf = $edge->outp[self::ABOVE];
                             break;
                         case VertexType::ERI:
                             if ($xb !== $px) {
                                 $cf->addRight($xb, $yb);
                                 $px = $xb;
                             }
                             $edge->outp[self::ABOVE] = $cf;
                             $cf = null;
                             break;
                         case VertexType::ELI:
                             $edge->outp[self::BELOW]->addLeft($xb, $yb);
                             $px = $xb;
                             $cf = $edge->outp[self::BELOW];
                             break;
                         case VertexType::EMX:
                             if ($xb !== $px) {
                                 $cf->addLeft($xb, $yb);
                                 $px = $xb;
                             }
                             $outPoly->mergeRight($cf, $edge->outp[self::BELOW]);
                             $cf = null;
                             break;
                         case VertexType::ILI:
                             if ($xb !== $px) {
                                 $cf->addLeft($xb, $yb);
                                 $px = $xb;
                             }
                             $edge->outp[self::ABOVE] = $cf;
                             $cf = null;
                             break;
                         case VertexType::IRI:
                             $edge->outp[self::BELOW]->addRight($xb, $yb);
                             $px = $xb;
                             $cf = $edge->outp[self::BELOW];
                             $edge->outp[self::BELOW] = null;
                             break;
                         case VertexType::IMX:
                             if ($xb !== $px) {
                                 $cf->addRight($xb, $yb);
                                 $px = $xb;
                             }
                             $outPoly->mergeLeft($cf, $edge->outp[self::BELOW]);
                             $cf = null;
                             $edge->outp[self::BELOW] = null;
                             break;
                         case VertexType::IMM:
                             if ($xb !== $px) {
                                 $cf->addRight($xb, $yb);
                                 $px = $xb;
                             }
                             $outPoly->mergeLeft($cf, $edge->outp[self::BELOW]);
                             $edge->outp[self::BELOW] = null;
                             $edge->outp[self::ABOVE] = $outPoly->addLocalMin($xb, $yb);
                             $cf = $edge->outp[self::ABOVE];
                             break;
                         case VertexType::EMM:
                             if ($xb !== $px) {
                                 $cf->addLeft($xb, $yb);
                                 $px = $xb;
                             }
                             $outPoly->mergeRight($cf, $edge->outp[self::BELOW]);
                             $edge->outp[self::BELOW] = null;
                             $edge->outp[self::ABOVE] = $outPoly->addLocalMin($xb, $yb);
                             $cf = $edge->outp[self::ABOVE];
                             break;
                         case VertexType::LED:
                             if ($edge->getBot()->getY() === $yb) {
                                 $edge->outp[self::BELOW]->addLeft($xb, $yb);
                             }
                             $edge->outp[self::ABOVE] = $edge->outp[self::BELOW];
                             $px = $xb;
                             break;
                         case VertexType::RED:
                             if ($edge->getBot()->getY() === $yb) {
                                 $edge->outp[self::BELOW]->addRight($xb, $yb);
                             }
                             $edge->outp[self::ABOVE] = $edge->outp[self::BELOW];
                             $px = $xb;
                             break;
                         default:
                             break;
                     }
                     /* End of switch */
                 }
                 /* End of contributing conditional */
             }
             /* End of edge exists conditional */
         }
         /* End of AET loop */
         /* Delete terminating edges from the AET, otherwise compute xt */
         for ($edge = $aet->getTopNode(); $edge != null; $edge = $edge->getNext()) {
             if ($edge->getTop()->getY() === $yb) {
                 $prevEdge = $edge->getPrev();
                 $nextEdge = $edge->getNext();
                 if ($prevEdge !== null) {
                     $prevEdge->setNext($nextEdge);
                 } else {
                     $aet->setTopNode($nextEdge);
                 }
                 if ($nextEdge !== null) {
                     $nextEdge->setPrev($prevEdge);
                 }
                 /* Copy bundle head state to the adjacent tail edge if required */
                 if ($edge->getBstate()[self::BELOW] === BundleState::bundleHead() && $prevEdge != null) {
                     if ($prevEdge->getBstate()[self::BELOW] == BundleState::bundleTail()) {
                         $prevEdge->outp[self::BELOW] = $edge->outp[self::BELOW];
                         $prevEdge->addBstate(self::BELOW, BundleState::unbundled());
                         if ($prevEdge->getPrev() != null) {
                             if ($prevEdge->getPrev()->getBstate()[self::BELOW] == BundleState::bundleTail()) {
                                 $prevEdge->addBstate(self::BELOW, BundleState::bundleHead());
                             }
                         }
                     }
                 }
             } else {
                 if ($edge->getTop()->getY() === $yt) {
                     $edge->setXt($edge->getTop()->getX());
                 } else {
                     $edge->setXt($edge->getBot()->getX() + $edge->getDx() * ($yt - $edge->getBot()->getY()));
                 }
             }
         }
         if ($scanbeam < $sbte->getSbtEntries()) {
             /*
              * === SCANBEAM INTERIOR PROCESSING
              * ==============================
              */
             /* Build intersection table for the current scanbeam */
             $itTable = new ItNodeTable();
             $this->buildIntersectionTable($aet, $dy, $itTable);
             /* Process each node in the intersection table */
             for ($insct = $itTable->getTopNode(); $insct != null; $insct = $insct->getNext()) {
                 $e0 = $insct->getIe()[0];
                 $e1 = $insct->getIe()[1];
                 /* Only generate output for contributing intersections */
                 if (($e0->bundle[self::ABOVE][self::CLIP] != 0 || $e0->bundle[self::ABOVE][self::SUBJ] != 0) && ($e1->bundle[self::ABOVE][self::CLIP] != 0 || $e1->bundle[self::ABOVE][self::SUBJ] != 0)) {
                     $p = $e0->outp[self::ABOVE];
                     $q = $e1->outp[self::ABOVE];
                     $ix = $insct->getPoint()->getX();
                     $iy = $insct->getPoint()->getY();
                     $inClip = $e0->bundle[self::ABOVE][self::CLIP] != 0 && !($e0->getBside()[self::CLIP] != 0) || $e1->bundle[self::ABOVE][self::CLIP] != 0 && $e1->getBside()[self::CLIP] != 0 || !($e0->bundle[self::ABOVE][self::CLIP] != 0) && !($e1->bundle[self::ABOVE][self::CLIP] != 0) && $e0->getBside()[self::CLIP] != 0 && $e1->getBside()[self::CLIP] != 0 ? 1 : 0;
                     $inSubj = $e0->bundle[self::ABOVE][self::SUBJ] != 0 && !($e0->getBside()[self::SUBJ] != 0) || $e1->bundle[self::ABOVE][self::SUBJ] != 0 && $e1->getBside()[self::SUBJ] != 0 || !($e0->bundle[self::ABOVE][self::SUBJ] != 0) && !($e1->bundle[self::ABOVE][self::SUBJ] != 0) && $e0->getBside()[self::SUBJ] != 0 && $e1->getBside()[self::SUBJ] != 0 ? 1 : 0;
                     $br = 0;
                     $bl = 0;
                     $tr = 0;
                     $tl = 0;
                     $tr = $inClip != 0 || $inSubj != 0 ? 1 : 0;
                     $tl = ($inClip ^ $e1->bundle[self::ABOVE][self::CLIP]) != 0 || ($inSubj ^ $e1->bundle[self::ABOVE][self::SUBJ]) != 0 ? 1 : 0;
                     $br = ($inClip ^ $e0->bundle[self::ABOVE][self::CLIP]) != 0 || ($inSubj ^ $e0->bundle[self::ABOVE][self::SUBJ]) != 0 ? 1 : 0;
                     $bl = ($inClip ^ $e1->bundle[self::ABOVE][self::CLIP] ^ $e0->bundle[self::ABOVE][self::CLIP]) != 0 || ($inSubj ^ $e1->bundle[self::ABOVE][self::SUBJ] ^ $e0->bundle[self::ABOVE][self::SUBJ]) != 0 ? 1 : 0;
                     $vClass = VertexType::getType($tr, $tl, $br, $bl);
                     switch ($vClass) {
                         case VertexType::EMN:
                             $e0->outp[self::ABOVE] = $outPoly->addLocalMin($ix, $iy);
                             $e1->outp[self::ABOVE] = $e0->outp[self::ABOVE];
                             break;
                         case VertexType::ERI:
                             if ($p != null) {
                                 $p->addRight($ix, $iy);
                                 $e1->outp[self::ABOVE] = $p;
                                 $e0->outp[self::ABOVE] = null;
                             }
                             break;
                         case VertexType::ELI:
                             if ($q != null) {
                                 $q->addLeft($ix, $iy);
                                 $e0->outp[self::ABOVE] = $q;
                                 $e1->outp[self::ABOVE] = null;
                             }
                             break;
                         case VertexType::EMX:
                             if ($p != null && $q != null) {
                                 $p->addLeft($ix, $iy);
                                 $outPoly->mergeRight($p, $q);
                                 $e0->outp[self::ABOVE] = null;
                                 $e1->outp[self::ABOVE] = null;
                             }
                             break;
                         case VertexType::IMN:
                             $e0->outp[self::ABOVE] = $outPoly->addLocalMin($ix, $iy);
                             $e1->outp[self::ABOVE] = $e0->outp[self::ABOVE];
                             break;
                         case VertexType::ILI:
                             if ($p != null) {
                                 $p->addLeft($ix, $iy);
                                 $e1->outp[self::ABOVE] = $p;
                                 $e0->outp[self::ABOVE] = null;
                             }
                             break;
                         case VertexType::IRI:
                             if ($q != null) {
                                 $q->addRight($ix, $iy);
                                 $e0->outp[self::ABOVE] = $q;
                                 $e1->outp[self::ABOVE] = null;
                             }
                             break;
                         case VertexType::IMX:
                             if ($p !== null && $q != null) {
                                 $p->addRight($ix, $iy);
                                 $outPoly->mergeLeft($p, $q);
                                 $e0->outp[self::ABOVE] = null;
                                 $e1->outp[self::ABOVE] = null;
                             }
                             break;
                         case VertexType::IMM:
                             if ($p !== null && $q != null) {
                                 $p->addRight($ix, $iy);
                                 $outPoly->mergeLeft($p, $q);
                                 $e0->outp[self::ABOVE] = $outPoly->addLocalMin($ix, $iy);
                                 $e1->outp[self::ABOVE] = $e0->outp[self::ABOVE];
                             }
                             break;
                         case VertexType::EMM:
                             if ($p !== null && $q != null) {
                                 $p->addLeft($ix, $iy);
                                 $outPoly->mergeRight($p, $q);
                                 $e0->outp[self::ABOVE] = $outPoly->addLocalMin($ix, $iy);
                                 $e1->outp[self::ABOVE] = $e0->outp[self::ABOVE];
                             }
                             break;
                         default:
                             break;
                     }
                     /* End of switch */
                 }
                 /* End of contributing intersection conditional */
                 /* Swap bundle sides in response to edge crossing */
                 if ($e0->bundle[self::ABOVE][self::CLIP] != 0) {
                     $e1->bside[self::CLIP] = $e1->bside[self::CLIP] === 0 ? 1 : 0;
                 }
                 if ($e1->bundle[self::ABOVE][self::CLIP] != 0) {
                     $e0->bside[self::CLIP] = $e0->bside[self::CLIP] === 0 ? 1 : 0;
                 }
                 if ($e0->bundle[self::ABOVE][self::SUBJ] != 0) {
                     $e1->bside[self::SUBJ] = $e1->bside[self::SUBJ] === 0 ? 1 : 0;
                 }
                 if ($e1->bundle[self::ABOVE][self::SUBJ] != 0) {
                     $e0->bside[self::SUBJ] = $e0->bside[self::SUBJ] === 0 ? 1 : 0;
                 }
                 /* Swap e0 and e1 bundles in the AET */
                 $prevEdge = $e0->getPrev();
                 $nextEdge = $e1->getNext();
                 if ($nextEdge != null) {
                     $nextEdge->setPrev($e0);
                 }
                 if ($e0->getBstate()[self::ABOVE] == BundleState::bundleHead()) {
                     $search = true;
                     while ($search) {
                         $prevEdge = $prevEdge->getPrev();
                         if ($prevEdge != null) {
                             if ($prevEdge->getBstate()[self::ABOVE] != BundleState::bundleTail()) {
                                 $search = false;
                             }
                         } else {
                             $search = false;
                         }
                     }
                 }
                 if ($prevEdge === null) {
                     $aet->getTopNode()->setPrev($e1);
                     $e1->setNext($aet->getTopNode());
                     $aet->setTopNode($e0->getNext());
                 } else {
                     $prevEdge->getNext()->setPrev($e1);
                     $e1->setNext($prevEdge->getNext());
                     $prevEdge->setNext($e0->getNext());
                 }
                 $e0->getNext()->setPrev($prevEdge);
                 $e1->getNext()->setPrev($e1);
                 $e0->setNext($nextEdge);
             }
             /* End of IT loop */
             /* Prepare for next scanbeam */
             for ($edge = $aet->getTopNode(); $edge != null; $edge = $edge->getNext()) {
                 $nextEdge = $edge->getNext();
                 $succEdge = $edge->getSucc();
                 if ($edge->getTop()->getY() === $yt && $succEdge !== null) {
                     /* Replace AET edge by its successor */
                     $succEdge->outp[self::BELOW] = $edge->outp[self::ABOVE];
                     $succEdge->bstate[self::BELOW] = $edge->bstate[self::ABOVE];
                     $succEdge->bundle[self::BELOW][self::CLIP] = $edge->bundle[self::ABOVE][self::CLIP];
                     $succEdge->bundle[self::BELOW][self::SUBJ] = $edge->bundle[self::ABOVE][self::SUBJ];
                     $prevEdge = $edge->getPrev();
                     if ($prevEdge !== null) {
                         $prevEdge->setNext($succEdge);
                     } else {
                         $aet->setTopNode($succEdge);
                     }
                     if ($nextEdge !== null) {
                         $nextEdge->setPrev($succEdge);
                     }
                     $succEdge->setPrev($prevEdge);
                     $succEdge->setNext($nextEdge);
                 } else {
                     /* Update this edge */
                     $edge->outp[self::BELOW] = $edge->outp[self::ABOVE];
                     $edge->bstate[self::BELOW] = $edge->bstate[self::ABOVE];
                     $edge->bundle[self::BELOW][self::CLIP] = $edge->bundle[self::ABOVE][self::CLIP];
                     $edge->bundle[self::BELOW][self::SUBJ] = $edge->bundle[self::ABOVE][self::SUBJ];
                     $edge->setXb($edge->getXt());
                 }
                 $edge->outp[self::ABOVE] = null;
             }
         }
     }
     /* === END OF SCANBEAM PROCESSING ================================== */
     /* Generate result polygon from out_poly */
     $result = $outPoly->getResult();
     return $result;
 }