/**
  * @param float $angle
  */
 public function rotate($angle)
 {
     $points = array();
     $points[0] = $this->dx;
     $points[1] = $this->dy;
     rotateAboutPoint($points, 0, 0, $angle);
     $this->dx = $points[0];
     $this->dy = $points[1];
 }
 private function drawLabelRotated($imageRef, $centre, $angle, $text, $padding, $direction)
 {
     $fontObject = $this->owner->fonts->getFont($this->bwfont);
     list($strWidth, $strHeight) = $fontObject->calculateImageStringSize($text);
     $angle = $this->normaliseAngle($angle);
     $radianAngle = -deg2rad($angle);
     $extra = 3;
     $topleft_x = $centre->x - $strWidth / 2 - $padding - $extra;
     $topleft_y = $centre->y - $strHeight / 2 - $padding - $extra;
     $botright_x = $centre->x + $strWidth / 2 + $padding + $extra;
     $botright_y = $centre->y + $strHeight / 2 + $padding + $extra;
     // a box. the last point is the start point for the text.
     $points = array($topleft_x, $topleft_y, $topleft_x, $botright_y, $botright_x, $botright_y, $botright_x, $topleft_y, $centre->x - $strWidth / 2, $centre->y + $strHeight / 2 + 1);
     if ($radianAngle != 0) {
         rotateAboutPoint($points, $centre->x, $centre->y, $radianAngle);
     }
     $textY = array_pop($points);
     $textX = array_pop($points);
     if ($this->bwboxcolour->isRealColour()) {
         imagefilledpolygon($imageRef, $points, 4, $this->bwboxcolour->gdAllocate($imageRef));
     }
     if ($this->bwoutlinecolour->isRealColour()) {
         imagepolygon($imageRef, $points, 4, $this->bwoutlinecolour->gdAllocate($imageRef));
     }
     $fontObject->drawImageString($imageRef, $textX, $textY, $text, $this->bwfontcolour->gdallocate($imageRef), $angle);
     $areaName = "LINK:L" . $this->id . ':' . ($direction + 2);
     // the rectangle is about half the size in the HTML, and easier to optimise/detect in the browser
     if ($angle % 90 == 0) {
         // We optimise for 0, 90, 180, 270 degrees - find the rectangle from the rotated points
         $rectanglePoints = array();
         $rectanglePoints[] = min($points[0], $points[2]);
         $rectanglePoints[] = min($points[1], $points[3]);
         $rectanglePoints[] = max($points[0], $points[2]);
         $rectanglePoints[] = max($points[1], $points[3]);
         $newArea = new HTML_ImageMap_Area_Rectangle($areaName, "", array($rectanglePoints));
         wm_debug("Adding Rectangle imagemap for {$areaName}\n");
     } else {
         $newArea = new HTML_ImageMap_Area_Polygon($areaName, "", array($points));
         wm_debug("Adding Poly imagemap for {$areaName}\n");
     }
     // Make a note that we added this area
     $this->imap_areas[] = $areaName;
     $this->imageMapAreas[] = $newArea;
     $this->owner->imap->addArea($newArea);
 }
 /**
  * moveNode - move a node, taking into account any relative nodes, and any links that
  * join to it, dealing with VIAs in an attractive way.
  *
  * @param string $nodeName
  * @param number $newX
  * @param number $newY
  * @return array
  * @throws WMException
  */
 function moveNode($nodeName, $newX, $newY)
 {
     if (!$this->isLoaded()) {
         throw new WMException("Map must be loaded before editing API called.");
     }
     // if the node doesn't exist, nothing will be changing
     if (!$this->map->nodeExists($nodeName)) {
         return array(0, 0, 0, 0);
     }
     $movingNode = $this->map->getNode($nodeName);
     $affected_nodes = array();
     $affected_links = array();
     // This is a complicated bit. Find out if this node is involved in any
     // links that have VIAs. If it is, we want to rotate those VIA points
     // about the *other* node in the link
     foreach ($this->map->links as $link) {
         if (count($link->vialist) > 0 && ($link->a->name == $nodeName || $link->b->name == $nodeName)) {
             $affected_links[] = $link->name;
             // get the other node from us
             if ($link->a->name == $nodeName) {
                 $pivot = $link->b;
             }
             if ($link->b->name == $nodeName) {
                 $pivot = $link->a;
             }
             // this is a weird special case, but it is possible, with link offsets
             // if the link starts and ends on this node, translate any VIAs
             if ($link->a->name == $nodeName && $link->b->name == $nodeName) {
                 $dx = $link->a->x - $newX;
                 $dy = $link->a->y - $newY;
                 for ($count = 0; $count < count($link->vialist); $count++) {
                     $link->vialist[$count][0] = $link->vialist[$count][0] - $dx;
                     $link->vialist[$count][1] = $link->vialist[$count][1] - $dy;
                 }
             } else {
                 $pivotX = $pivot->x;
                 $pivotY = $pivot->y;
                 $newPoint = new WMPoint($newX, $newY);
                 $pivotPoint = $pivot->getPosition();
                 $movingPoint = $movingNode->getPosition();
                 $oldVector = $pivotPoint->vectorToPoint($movingPoint);
                 $newVector = $pivotPoint->vectorToPoint($newPoint);
                 //                    $dx_old = $pivotX - $movingNode->x;
                 //                    $dy_old = $pivotY - $movingNode->y;
                 //                    $dx_new = $pivotX - $newX;
                 //                    $dy_new = $pivotY - $newY;
                 //                    $l_old = sqrt($dx_old*$dx_old + $dy_old*$dy_old);
                 //                    $l_new = sqrt($dx_new*$dx_new + $dy_new*$dy_new);
                 //
                 //                    $angle_old = rad2deg(atan2(-$dy_old, $dx_old));
                 //                    $angle_new = rad2deg(atan2(-$dy_new, $dx_new));
                 $angle_old = $oldVector->getAngle();
                 $angle_new = $newVector->getAngle();
                 $l_new = $newVector->getLength();
                 $l_old = $oldVector->getLength();
                 # $log .= "$pivx,$pivy\n$dx_old $dy_old $l_old => $angle_old\n";
                 # $log .= "$dx_new $dy_new $l_new => $angle_new\n";
                 // the geometry stuff uses a different point format, helpfully
                 $points = array();
                 foreach ($link->vialist as $via) {
                     $points[] = $via[0];
                     $points[] = $via[1];
                 }
                 $scaleFactor = $l_new / $l_old;
                 // rotate so that link is along the axis
                 rotateAboutPoint($points, $pivotX, $pivotY, deg2rad($angle_old));
                 // do the scaling in here
                 for ($count = 0; $count < count($points) / 2; $count++) {
                     $basex = ($points[$count * 2] - $pivotX) * $scaleFactor + $pivotX;
                     $points[$count * 2] = $basex;
                 }
                 // rotate back so that link is along the new direction
                 rotateAboutPoint($points, $pivotX, $pivotY, deg2rad(-$angle_new));
                 // now put the modified points back into the vialist again
                 $viaCount = 0;
                 $count = 0;
                 foreach ($points as $p) {
                     // skip a point if it positioned relative to a node. Those shouldn't be rotated (well, IMHO)
                     if (!isset($link->vialist[$viaCount][2])) {
                         $link->vialist[$viaCount][$count] = $p;
                     }
                     $count++;
                     if ($count == 2) {
                         $count = 0;
                         $viaCount++;
                     }
                 }
             }
         }
     }
     $movingNode->x = $newX;
     $movingNode->y = $newY;
     $n_links = count($affected_links);
     $n_nodes = count($affected_nodes);
     return array($n_nodes, $n_links, $affected_nodes, $affected_links);
 }