1<?php 2 3namespace CpChart\Chart; 4 5use CpChart\Data; 6use CpChart\Image; 7 8class Radar 9{ 10 /** 11 * @var Image 12 */ 13 public $pChartObject; 14 15 /** 16 * Draw a radar chart 17 * @param Image $Object 18 * @param Data $Values 19 * @param array $Format 20 */ 21 public function drawRadar(Image $Object, Data $Values, array $Format = []) 22 { 23 $this->pChartObject = $Object; 24 25 $FixedMax = isset($Format["FixedMax"]) ? $Format["FixedMax"] : VOID; 26 $AxisR = isset($Format["AxisR"]) ? $Format["AxisR"] : 60; 27 $AxisG = isset($Format["AxisG"]) ? $Format["AxisG"] : 60; 28 $AxisB = isset($Format["AxisB"]) ? $Format["AxisB"] : 60; 29 $AxisAlpha = isset($Format["AxisAlpha"]) ? $Format["AxisAlpha"] : 50; 30 $AxisRotation = isset($Format["AxisRotation"]) ? $Format["AxisRotation"] : 0; 31 $DrawTicks = isset($Format["DrawTicks"]) ? $Format["DrawTicks"] : true; 32 $TicksLength = isset($Format["TicksLength"]) ? $Format["TicksLength"] : 2; 33 $DrawAxisValues = isset($Format["DrawAxisValues"]) ? $Format["DrawAxisValues"] : true; 34 $AxisBoxRounded = isset($Format["AxisBoxRounded"]) ? $Format["AxisBoxRounded"] : true; 35 $AxisFontName = isset($Format["AxisFontName"]) ? $Format["AxisFontName"] : $this->pChartObject->FontName; 36 $AxisFontSize = isset($Format["AxisFontSize"]) ? $Format["AxisFontSize"] : $this->pChartObject->FontSize; 37 $WriteValues = isset($Format["WriteValues"]) ? $Format["WriteValues"] : false; 38 $WriteValuesInBubble = isset($Format["WriteValuesInBubble"]) ? $Format["WriteValuesInBubble"] : true; 39 $ValueFontName = isset($Format["ValueFontName"]) ? $Format["ValueFontName"] : $this->pChartObject->FontName 40 ; 41 $ValueFontSize = isset($Format["ValueFontSize"]) ? $Format["ValueFontSize"] : $this->pChartObject->FontSize 42 ; 43 $ValuePadding = isset($Format["ValuePadding"]) ? $Format["ValuePadding"] : 4; 44 $OuterBubbleRadius = isset($Format["OuterBubbleRadius"]) ? $Format["OuterBubbleRadius"] : 2; 45 $OuterBubbleR = isset($Format["OuterBubbleR"]) ? $Format["OuterBubbleR"] : VOID; 46 $OuterBubbleG = isset($Format["OuterBubbleG"]) ? $Format["OuterBubbleG"] : VOID; 47 $OuterBubbleB = isset($Format["OuterBubbleB"]) ? $Format["OuterBubbleB"] : VOID; 48 $OuterBubbleAlpha = isset($Format["OuterBubbleAlpha"]) ? $Format["OuterBubbleAlpha"] : 100; 49 $InnerBubbleR = isset($Format["InnerBubbleR"]) ? $Format["InnerBubbleR"] : 255; 50 $InnerBubbleG = isset($Format["InnerBubbleG"]) ? $Format["InnerBubbleG"] : 255; 51 $InnerBubbleB = isset($Format["InnerBubbleB"]) ? $Format["InnerBubbleB"] : 255; 52 $InnerBubbleAlpha = isset($Format["InnerBubbleAlpha"]) ? $Format["InnerBubbleAlpha"] : 100; 53 $DrawBackground = isset($Format["DrawBackground"]) ? $Format["DrawBackground"] : true; 54 $BackgroundR = isset($Format["BackgroundR"]) ? $Format["BackgroundR"] : 255; 55 $BackgroundG = isset($Format["BackgroundG"]) ? $Format["BackgroundG"] : 255; 56 $BackgroundB = isset($Format["BackgroundB"]) ? $Format["BackgroundB"] : 255; 57 $BackgroundAlpha = isset($Format["BackgroundAlpha"]) ? $Format["BackgroundAlpha"] : 50; 58 $BackgroundGradient = isset($Format["BackgroundGradient"]) ? $Format["BackgroundGradient"] : null; 59 $Layout = isset($Format["Layout"]) ? $Format["Layout"] : RADAR_LAYOUT_STAR; 60 $SegmentHeight = isset($Format["SegmentHeight"]) ? $Format["SegmentHeight"] : SEGMENT_HEIGHT_AUTO; 61 $Segments = isset($Format["Segments"]) ? $Format["Segments"] : 4; 62 $WriteLabels = isset($Format["WriteLabels"]) ? $Format["WriteLabels"] : true; 63 $SkipLabels = isset($Format["SkipLabels"]) ? $Format["SkipLabels"] : 1; 64 $LabelMiddle = isset($Format["LabelMiddle"]) ? $Format["LabelMiddle"] : false; 65 $LabelsBackground = isset($Format["LabelsBackground"]) ? $Format["LabelsBackground"] : true; 66 $LabelsBGR = isset($Format["LabelsBGR"]) ? $Format["LabelsBGR"] : 255; 67 $LabelsBGG = isset($Format["LabelsBGR"]) ? $Format["LabelsBGG"] : 255; 68 $LabelsBGB = isset($Format["LabelsBGR"]) ? $Format["LabelsBGB"] : 255; 69 $LabelsBGAlpha = isset($Format["LabelsBGAlpha"]) ? $Format["LabelsBGAlpha"] : 50; 70 $LabelPos = isset($Format["LabelPos"]) ? $Format["LabelPos"] : RADAR_LABELS_ROTATED; 71 $LabelPadding = isset($Format["LabelPadding"]) ? $Format["LabelPadding"] : 4; 72 $DrawPoints = isset($Format["DrawPoints"]) ? $Format["DrawPoints"] : true; 73 $PointRadius = isset($Format["PointRadius"]) ? $Format["PointRadius"] : 4; 74 $PointSurrounding = isset($Format["PointRadius"]) ? $Format["PointRadius"] : -30; 75 $DrawLines = isset($Format["DrawLines"]) ? $Format["DrawLines"] : true; 76 $LineLoopStart = isset($Format["LineLoopStart"]) ? $Format["LineLoopStart"] : true; 77 $DrawPoly = isset($Format["DrawPoly"]) ? $Format["DrawPoly"] : false; 78 $PolyAlpha = isset($Format["PolyAlpha"]) ? $Format["PolyAlpha"] : 40; 79 $FontSize = $Object->FontSize; 80 $X1 = $Object->GraphAreaX1; 81 $Y1 = $Object->GraphAreaY1; 82 $X2 = $Object->GraphAreaX2; 83 $Y2 = $Object->GraphAreaY2; 84 $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : false; 85 86 /* Cancel default tick length if ticks not enabled */ 87 if ($DrawTicks == false) { 88 $TicksLength = 0; 89 } 90 91 /* Data Processing */ 92 $Data = $Values->getData(); 93 $Palette = $Values->getPalette(); 94 95 /* Catch the number of required axis */ 96 $LabelSerie = $Data["Abscissa"]; 97 if ($LabelSerie) { 98 $Points = count($Data["Series"][$LabelSerie]["Data"]); 99 } else { 100 $Points = 0; 101 foreach ($Data["Series"] as $SerieName => $DataArray) { 102 if (count($DataArray["Data"]) > $Points) { 103 $Points = count($DataArray["Data"]); 104 } 105 } 106 } 107 108 /* Draw the axis */ 109 $CenterX = ($X2 - $X1) / 2 + $X1; 110 $CenterY = ($Y2 - $Y1) / 2 + $Y1; 111 112 $EdgeHeight = min(($X2 - $X1) / 2, ($Y2 - $Y1) / 2); 113 if ($WriteLabels) { 114 $EdgeHeight = $EdgeHeight - $FontSize - $LabelPadding - $TicksLength; 115 } 116 /* Determine the scale if set to automatic */ 117 if ($SegmentHeight == SEGMENT_HEIGHT_AUTO) { 118 if ($FixedMax != VOID) { 119 $Max = $FixedMax; 120 } else { 121 $Max = 0; 122 foreach ($Data["Series"] as $SerieName => $DataArray) { 123 if ($SerieName != $LabelSerie) { 124 if (max($DataArray["Data"]) > $Max) { 125 $Max = max($DataArray["Data"]); 126 } 127 } 128 } 129 } 130 $MaxSegments = $EdgeHeight / 20; 131 $Scale = $Object->computeScale(0, $Max, $MaxSegments, [1, 2, 5]); 132 133 $Segments = $Scale["Rows"]; 134 $SegmentHeight = $Scale["RowHeight"]; 135 } 136 137 if ($LabelMiddle && $SkipLabels == 1) { 138 $Axisoffset = (360 / $Points) / 2; 139 } elseif ($LabelMiddle && $SkipLabels != 1) { 140 $Axisoffset = (360 / ($Points / $SkipLabels)) / 2; 141 } elseif (!$LabelMiddle) { 142 $Axisoffset = 0; 143 } 144 145 /* Background processing */ 146 if ($DrawBackground) { 147 $RestoreShadow = $Object->Shadow; 148 $Object->Shadow = false; 149 150 if ($BackgroundGradient == null) { 151 if ($Layout == RADAR_LAYOUT_STAR) { 152 $Color = [ 153 "R" => $BackgroundR, 154 "G" => $BackgroundG, 155 "B" => $BackgroundB, 156 "Alpha" => $BackgroundAlpha 157 ]; 158 $PointArray = []; 159 for ($i = 0; $i <= 360; $i = $i + (360 / $Points)) { 160 $PointArray[] = cos(deg2rad($i + $AxisRotation)) * $EdgeHeight + $CenterX; 161 $PointArray[] = sin(deg2rad($i + $AxisRotation)) * $EdgeHeight + $CenterY; 162 } 163 $Object->drawPolygon($PointArray, $Color); 164 } elseif ($Layout == RADAR_LAYOUT_CIRCLE) { 165 $Color = [ 166 "R" => $BackgroundR, 167 "G" => $BackgroundG, 168 "B" => $BackgroundB, 169 "Alpha" => $BackgroundAlpha 170 ]; 171 $Object->drawFilledCircle( 172 $CenterX, 173 $CenterY, 174 $EdgeHeight, 175 $Color 176 ); 177 } 178 } else { 179 $GradientROffset = ($BackgroundGradient["EndR"] - $BackgroundGradient["StartR"]) / $Segments; 180 $GradientGOffset = ($BackgroundGradient["EndG"] - $BackgroundGradient["StartG"]) / $Segments; 181 $GradientBOffset = ($BackgroundGradient["EndB"] - $BackgroundGradient["StartB"]) / $Segments; 182 $GradientAlphaOffset = ($BackgroundGradient["EndAlpha"] - $BackgroundGradient["StartAlpha"]) 183 / $Segments 184 ; 185 186 if ($Layout == RADAR_LAYOUT_STAR) { 187 for ($j = $Segments; $j >= 1; $j--) { 188 $Color = [ 189 "R" => $BackgroundGradient["StartR"] + $GradientROffset * $j, 190 "G" => $BackgroundGradient["StartG"] + $GradientGOffset * $j, 191 "B" => $BackgroundGradient["StartB"] + $GradientBOffset * $j, 192 "Alpha" => $BackgroundGradient["StartAlpha"] + $GradientAlphaOffset * $j 193 ]; 194 $PointArray = []; 195 196 for ($i = 0; $i <= 360; $i = $i + (360 / $Points)) { 197 $PointArray[] = cos(deg2rad($i + $AxisRotation)) 198 * ($EdgeHeight / $Segments) * $j + $CenterX 199 ; 200 $PointArray[] = sin(deg2rad($i + $AxisRotation)) 201 * ($EdgeHeight / $Segments) * $j + $CenterY 202 ; 203 } 204 $Object->drawPolygon($PointArray, $Color); 205 } 206 } elseif ($Layout == RADAR_LAYOUT_CIRCLE) { 207 for ($j = $Segments; $j >= 1; $j--) { 208 $Color = [ 209 "R" => $BackgroundGradient["StartR"] + $GradientROffset * $j, 210 "G" => $BackgroundGradient["StartG"] + $GradientGOffset * $j, 211 "B" => $BackgroundGradient["StartB"] + $GradientBOffset * $j, 212 "Alpha" => $BackgroundGradient["StartAlpha"] + $GradientAlphaOffset * $j 213 ]; 214 $Object->drawFilledCircle( 215 $CenterX, 216 $CenterY, 217 ($EdgeHeight / $Segments) * $j, 218 $Color 219 ); 220 } 221 } 222 } 223 $Object->Shadow = $RestoreShadow; 224 } 225 226 /* Axis to axis lines */ 227 $Color = ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]; 228 $ColorDotted = [ 229 "R" => $AxisR, 230 "G" => $AxisG, 231 "B" => $AxisB, 232 "Alpha" => $AxisAlpha * .8, 233 "Ticks" => 2 234 ]; 235 if ($Layout == RADAR_LAYOUT_STAR) { 236 for ($j = 1; $j <= $Segments; $j++) { 237 for ($i = 0; $i < 360; $i = $i + (360 / $Points)) { 238 $EdgeX1 = cos(deg2rad($i + $AxisRotation)) * ($EdgeHeight / $Segments) * $j + $CenterX; 239 $EdgeY1 = sin(deg2rad($i + $AxisRotation)) * ($EdgeHeight / $Segments) * $j + $CenterY; 240 $EdgeX2 = cos(deg2rad($i + $AxisRotation + (360 / $Points))) 241 * ($EdgeHeight / $Segments) * $j + $CenterX 242 ; 243 $EdgeY2 = sin(deg2rad($i + $AxisRotation + (360 / $Points))) 244 * ($EdgeHeight / $Segments) * $j + $CenterY 245 ; 246 247 $Object->drawLine($EdgeX1, $EdgeY1, $EdgeX2, $EdgeY2, $Color); 248 } 249 } 250 } elseif ($Layout == RADAR_LAYOUT_CIRCLE) { 251 for ($j = 1; $j <= $Segments; $j++) { 252 $Radius = ($EdgeHeight / $Segments) * $j; 253 $Object->drawCircle($CenterX, $CenterY, $Radius, $Radius, $Color); 254 } 255 } 256 257 if ($DrawAxisValues) { 258 if ($LabelsBackground) { 259 $Options = [ 260 "DrawBox" => true, 261 "Align" => TEXT_ALIGN_MIDDLEMIDDLE, 262 "BoxR" => $LabelsBGR, 263 "BoxG" => $LabelsBGG, 264 "BoxB" => $LabelsBGB, 265 "BoxAlpha" => $LabelsBGAlpha 266 ]; 267 } else { 268 $Options = ["Align" => TEXT_ALIGN_MIDDLEMIDDLE]; 269 } 270 if ($AxisBoxRounded) { 271 $Options["BoxRounded"] = true; 272 } 273 274 $Options["FontName"] = $AxisFontName; 275 $Options["FontSize"] = $AxisFontSize; 276 277 $Angle = 360 / ($Points * 2); 278 for ($j = 1; $j <= $Segments; $j++) { 279 $Label = $j * $SegmentHeight; 280 281 if ($Layout == RADAR_LAYOUT_CIRCLE) { 282 $EdgeX1 = cos(deg2rad($Angle + $AxisRotation)) * ($EdgeHeight / $Segments) * $j + $CenterX; 283 $EdgeY1 = sin(deg2rad($Angle + $AxisRotation)) * ($EdgeHeight / $Segments) * $j + $CenterY; 284 } elseif ($Layout == RADAR_LAYOUT_STAR) { 285 $EdgeX1 = cos(deg2rad($AxisRotation)) * ($EdgeHeight / $Segments) * $j + $CenterX; 286 $EdgeY1 = sin(deg2rad($AxisRotation)) * ($EdgeHeight / $Segments) * $j + $CenterY; 287 $EdgeX2 = cos(deg2rad((360 / $Points) + $AxisRotation)) * ($EdgeHeight / $Segments) 288 * $j + $CenterX 289 ; 290 $EdgeY2 = sin(deg2rad((360 / $Points) + $AxisRotation)) * ($EdgeHeight / $Segments) 291 * $j + $CenterY 292 ; 293 294 $EdgeX1 = ($EdgeX2 - $EdgeX1) / 2 + $EdgeX1; 295 $EdgeY1 = ($EdgeY2 - $EdgeY1) / 2 + $EdgeY1; 296 } 297 298 $Object->drawText($EdgeX1, $EdgeY1, $Label, $Options); 299 } 300 } 301 302 /* Axis lines */ 303 $ID = 0; 304 for ($i = 0; $i < 360; $i = $i + (360 / $Points)) { 305 $EdgeX = cos(deg2rad($i + $AxisRotation)) * ($EdgeHeight + $TicksLength) + $CenterX; 306 $EdgeY = sin(deg2rad($i + $AxisRotation)) * ($EdgeHeight + $TicksLength) + $CenterY; 307 308 if ($ID % $SkipLabels == 0) { 309 $Object->drawLine($CenterX, $CenterY, $EdgeX, $EdgeY, $Color); 310 } else { 311 $Object->drawLine($CenterX, $CenterY, $EdgeX, $EdgeY, $ColorDotted); 312 } 313 314 if ($WriteLabels) { 315 $LabelX = cos(deg2rad($i + $AxisRotation + $Axisoffset)) 316 * ($EdgeHeight + $LabelPadding + $TicksLength) + $CenterX 317 ; 318 $LabelY = sin(deg2rad($i + $AxisRotation + $Axisoffset)) 319 * ($EdgeHeight + $LabelPadding + $TicksLength) + $CenterY 320 ; 321 322 if ($LabelSerie) { 323 $Label = isset($Data["Series"][$LabelSerie]["Data"][$ID]) 324 ? $Data["Series"][$LabelSerie]["Data"][$ID] : "" 325 ; 326 } else { 327 $Label = $ID; 328 } 329 330 if ($ID % $SkipLabels == 0) { 331 if ($LabelPos == RADAR_LABELS_ROTATED) { 332 $Object->drawText( 333 $LabelX, 334 $LabelY, 335 $Label, 336 [ 337 "Angle" => (360 - ($i + $AxisRotation + $Axisoffset)) - 90, 338 "Align" => TEXT_ALIGN_BOTTOMMIDDLE 339 ] 340 ); 341 } else { 342 if ((floor($LabelX) == floor($CenterX)) && (floor($LabelY) < floor($CenterY))) { 343 $Object->drawText( 344 $LabelX, 345 $LabelY, 346 $Label, 347 ["Align" => TEXT_ALIGN_BOTTOMMIDDLE] 348 ); 349 } 350 if ((floor($LabelX) > floor($CenterX)) && (floor($LabelY) < floor($CenterY))) { 351 $Object->drawText( 352 $LabelX, 353 $LabelY, 354 $Label, 355 ["Align" => TEXT_ALIGN_BOTTOMLEFT] 356 ); 357 } 358 if ((floor($LabelX) > floor($CenterX)) && (floor($LabelY) == floor($CenterY))) { 359 $Object->drawText( 360 $LabelX, 361 $LabelY, 362 $Label, 363 ["Align" => TEXT_ALIGN_MIDDLELEFT] 364 ); 365 } 366 if ((floor($LabelX) > floor($CenterX)) && (floor($LabelY) > floor($CenterY))) { 367 $Object->drawText( 368 $LabelX, 369 $LabelY, 370 $Label, 371 ["Align" => TEXT_ALIGN_TOPLEFT] 372 ); 373 } 374 if ((floor($LabelX) < floor($CenterX)) && (floor($LabelY) < floor($CenterY))) { 375 $Object->drawText( 376 $LabelX, 377 $LabelY, 378 $Label, 379 ["Align" => TEXT_ALIGN_BOTTOMRIGHT] 380 ); 381 } 382 if ((floor($LabelX) < floor($CenterX)) && (floor($LabelY) == floor($CenterY))) { 383 $Object->drawText( 384 $LabelX, 385 $LabelY, 386 $Label, 387 ["Align" => TEXT_ALIGN_MIDDLERIGHT] 388 ); 389 } 390 if ((floor($LabelX) < floor($CenterX)) && (floor($LabelY) > floor($CenterY))) { 391 $Object->drawText( 392 $LabelX, 393 $LabelY, 394 $Label, 395 ["Align" => TEXT_ALIGN_TOPRIGHT] 396 ); 397 } 398 if ((floor($LabelX) == floor($CenterX)) && (floor($LabelY) > floor($CenterY))) { 399 $Object->drawText( 400 $LabelX, 401 $LabelY, 402 $Label, 403 ["Align" => TEXT_ALIGN_TOPMIDDLE] 404 ); 405 } 406 } 407 } 408 } 409 $ID++; 410 } 411 412 /* Compute the plots position */ 413 $ID = 0; 414 $Plot = []; 415 foreach ($Data["Series"] as $SerieName => $DataS) { 416 if ($SerieName != $LabelSerie) { 417 $Series[$ID] = &$Data["Series"][$SerieName]; // Map $ID to series to allow customisation 418 $Color = [ 419 "R" => $Palette[$ID]["R"], 420 "G" => $Palette[$ID]["G"], 421 "B" => $Palette[$ID]["B"], 422 "Alpha" => $Palette[$ID]["Alpha"], 423 "Surrounding" => $PointSurrounding 424 ]; 425 foreach ($DataS["Data"] as $Key => $Value) { 426 $Angle = (360 / $Points) * $Key; 427 $Length = ($EdgeHeight / ($Segments * $SegmentHeight)) * $Value; 428 429 $X = cos(deg2rad($Angle + $AxisRotation)) * $Length + $CenterX; 430 $Y = sin(deg2rad($Angle + $AxisRotation)) * $Length + $CenterY; 431 432 $Plot[$ID][] = [$X, $Y, $Value]; 433 434 if ($RecordImageMap) { 435 $this->pChartObject->addToImageMap( 436 "CIRCLE", 437 sprintf("%s,%s,%s", floor($X), floor($Y), floor($PointRadius)), 438 $this->pChartObject->toHTMLColor( 439 $Palette[$ID]["R"], 440 $Palette[$ID]["G"], 441 $Palette[$ID]["B"] 442 ), 443 $DataS["Description"], 444 $Data["Series"][$LabelSerie]["Data"][$Key] . " = " . $Value 445 ); 446 } 447 } 448 $ID++; 449 } 450 } 451 452 /* Draw all that stuff! */ 453 foreach ($Plot as $ID => $Points) { 454 $Color = [ 455 "R" => $Palette[$ID]["R"], 456 "G" => $Palette[$ID]["G"], 457 "B" => $Palette[$ID]["B"], 458 "Alpha" => $Palette[$ID]["Alpha"], 459 "Surrounding" => $PointSurrounding 460 ]; 461 462 /* Draw the polygons */ 463 if ($DrawPoly) { 464 if ($PolyAlpha != null) { 465 $Color = [ 466 "R" => $Palette[$ID]["R"], 467 "G" => $Palette[$ID]["G"], 468 "B" => $Palette[$ID]["B"], 469 "Alpha" => $PolyAlpha, 470 "Surrounding" => $PointSurrounding 471 ]; 472 } 473 $PointsArray = []; 474 for ($i = 0; $i < count($Points); $i++) { 475 $PointsArray[] = $Points[$i][0]; 476 $PointsArray[] = $Points[$i][1]; 477 } 478 $Object->drawPolygon($PointsArray, $Color); 479 } 480 481 $Color = [ 482 "R" => $Palette[$ID]["R"], 483 "G" => $Palette[$ID]["G"], 484 "B" => $Palette[$ID]["B"], 485 "Alpha" => $Palette[$ID]["Alpha"], 486 "Surrounding" => $PointSurrounding 487 ]; 488 489 /* Bubble and labels settings */ 490 $TextSettings = [ 491 "Align" => TEXT_ALIGN_MIDDLEMIDDLE, 492 "FontName" => $ValueFontName, 493 "FontSize" => $ValueFontSize, 494 "R" => $Palette[$ID]["R"], 495 "G" => $Palette[$ID]["G"], 496 "B" => $Palette[$ID]["B"] 497 ]; 498 $InnerColor = [ 499 "R" => $InnerBubbleR, 500 "G" => $InnerBubbleG, 501 "B" => $InnerBubbleB, 502 "Alpha" => $InnerBubbleAlpha 503 ]; 504 if ($OuterBubbleR != VOID) { 505 $OuterColor = [ 506 "R" => $OuterBubbleR, 507 "G" => $OuterBubbleG, 508 "B" => $OuterBubbleB, 509 "Alpha" => $OuterBubbleAlpha 510 ]; 511 } else { 512 $OuterColor = [ 513 "R" => $Palette[$ID]["R"] + 20, 514 "G" => $Palette[$ID]["G"] + 20, 515 "B" => $Palette[$ID]["B"] + 20, 516 "Alpha" => $Palette[$ID]["Alpha"] 517 ]; 518 } 519 /* Loop to the starting points if asked */ 520 if ($LineLoopStart && $DrawLines) { 521 $Object->drawLine( 522 $Points[count($Points) - 1][0], 523 $Points[count($Points) - 1][1], 524 $Points[0][0], 525 $Points[0][1], 526 $Color + (isset($Format['LineWeight']) ? [ 'Weight' => $Format['LineWeight'] ] : [] ) 527 ); 528 } 529 530 /* Draw the lines & points */ 531 for ($i = 0; $i < count($Points); $i++) { 532 if ($DrawLines && $i < count($Points) - 1) { 533 $Object->drawLine( 534 $Points[$i][0], 535 $Points[$i][1], 536 $Points[$i + 1][0], 537 $Points[$i + 1][1], 538 $Color + (isset($Format['LineWeight']) ? [ 'Weight' => $Format['LineWeight'] ] : [] ) 539 ); 540 } 541 if ($DrawPoints) { 542 $Object->drawShape( 543 $Points[$i][0], 544 $Points[$i][1], 545 (isset($Series[$ID]['Shape']) ? $Series[$ID]['Shape'] : SERIE_SHAPE_CIRCLE), 546 $PointRadius, 547 true, 548 1, 549 $Color['R'], 550 $Color['G'], 551 $Color['B'], 552 $Color['Alpha'], 553 0, 554 0, 555 0, 556 100 557 ); 558 } 559 if ($WriteValuesInBubble && $WriteValues) { 560 $TxtPos = $this->pChartObject->getTextBox( 561 $Points[$i][0], 562 $Points[$i][1], 563 $ValueFontName, 564 $ValueFontSize, 565 0, 566 $Points[$i][2] 567 ); 568 $Radius = floor(($TxtPos[1]["X"] - $TxtPos[0]["X"] + $ValuePadding * 2) / 2); 569 570 $this->pChartObject->drawFilledCircle( 571 $Points[$i][0], 572 $Points[$i][1], 573 $Radius + $OuterBubbleRadius, 574 $OuterColor 575 ); 576 $this->pChartObject->drawFilledCircle( 577 $Points[$i][0], 578 $Points[$i][1], 579 $Radius, 580 $InnerColor 581 ); 582 } 583 584 if ($WriteValues) { 585 $this->pChartObject->drawText( 586 $Points[$i][0] - 1, 587 $Points[$i][1] - 1, 588 $Points[$i][2], 589 $TextSettings 590 ); 591 } 592 } 593 } 594 } 595 596 /** 597 * Draw a radar chart 598 * @param Image $Object 599 * @param Data $Values 600 * @param array $Format 601 */ 602 public function drawPolar(Image $Object, Data $Values, array $Format = []) 603 { 604 $this->pChartObject = $Object; 605 606 $FixedMax = isset($Format["FixedMax"]) ? $Format["FixedMax"] : VOID; 607 $AxisR = isset($Format["AxisR"]) ? $Format["AxisR"] : 60; 608 $AxisG = isset($Format["AxisG"]) ? $Format["AxisG"] : 60; 609 $AxisB = isset($Format["AxisB"]) ? $Format["AxisB"] : 60; 610 $AxisAlpha = isset($Format["AxisAlpha"]) ? $Format["AxisAlpha"] : 50; 611 $AxisRotation = isset($Format["AxisRotation"]) ? $Format["AxisRotation"] : -90; 612 $DrawTicks = isset($Format["DrawTicks"]) ? $Format["DrawTicks"] : true; 613 $TicksLength = isset($Format["TicksLength"]) ? $Format["TicksLength"] : 2; 614 $DrawAxisValues = isset($Format["DrawAxisValues"]) ? $Format["DrawAxisValues"] : true; 615 $AxisBoxRounded = isset($Format["AxisBoxRounded"]) ? $Format["AxisBoxRounded"] : true; 616 $AxisFontName = isset($Format["FontName"]) ? $Format["FontName"] : $this->pChartObject->FontName; 617 $AxisFontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->pChartObject->FontSize; 618 $WriteValues = isset($Format["WriteValues"]) ? $Format["WriteValues"] : false; 619 $WriteValuesInBubble = isset($Format["WriteValuesInBubble"]) ? $Format["WriteValuesInBubble"] : true; 620 $ValueFontName = isset($Format["ValueFontName"]) 621 ? $Format["ValueFontName"] : $this->pChartObject->FontName 622 ; 623 $ValueFontSize = isset($Format["ValueFontSize"]) 624 ? $Format["ValueFontSize"] : $this->pChartObject->FontSize 625 ; 626 $ValuePadding = isset($Format["ValuePadding"]) ? $Format["ValuePadding"] : 4; 627 $OuterBubbleRadius = isset($Format["OuterBubbleRadius"]) ? $Format["OuterBubbleRadius"] : 2; 628 $OuterBubbleR = isset($Format["OuterBubbleR"]) ? $Format["OuterBubbleR"] : VOID; 629 $OuterBubbleG = isset($Format["OuterBubbleG"]) ? $Format["OuterBubbleG"] : VOID; 630 $OuterBubbleB = isset($Format["OuterBubbleB"]) ? $Format["OuterBubbleB"] : VOID; 631 $OuterBubbleAlpha = isset($Format["OuterBubbleAlpha"]) ? $Format["OuterBubbleAlpha"] : 100; 632 $InnerBubbleR = isset($Format["InnerBubbleR"]) ? $Format["InnerBubbleR"] : 255; 633 $InnerBubbleG = isset($Format["InnerBubbleG"]) ? $Format["InnerBubbleG"] : 255; 634 $InnerBubbleB = isset($Format["InnerBubbleB"]) ? $Format["InnerBubbleB"] : 255; 635 $InnerBubbleAlpha = isset($Format["InnerBubbleAlpha"]) ? $Format["InnerBubbleAlpha"] : 100; 636 $DrawBackground = isset($Format["DrawBackground"]) ? $Format["DrawBackground"] : true; 637 $BackgroundR = isset($Format["BackgroundR"]) ? $Format["BackgroundR"] : 255; 638 $BackgroundG = isset($Format["BackgroundG"]) ? $Format["BackgroundG"] : 255; 639 $BackgroundB = isset($Format["BackgroundB"]) ? $Format["BackgroundB"] : 255; 640 $BackgroundAlpha = isset($Format["BackgroundAlpha"]) ? $Format["BackgroundAlpha"] : 50; 641 $BackgroundGradient = isset($Format["BackgroundGradient"]) ? $Format["BackgroundGradient"] : null; 642 $AxisSteps = isset($Format["AxisSteps"]) ? $Format["AxisSteps"] : 20; 643 $SegmentHeight = isset($Format["SegmentHeight"]) ? $Format["SegmentHeight"] : SEGMENT_HEIGHT_AUTO; 644 $Segments = isset($Format["Segments"]) ? $Format["Segments"] : 4; 645 $WriteLabels = isset($Format["WriteLabels"]) ? $Format["WriteLabels"] : true; 646 $LabelsBackground = isset($Format["LabelsBackground"]) ? $Format["LabelsBackground"] : true; 647 $LabelsBGR = isset($Format["LabelsBGR"]) ? $Format["LabelsBGR"] : 255; 648 $LabelsBGG = isset($Format["LabelsBGR"]) ? $Format["LabelsBGG"] : 255; 649 $LabelsBGB = isset($Format["LabelsBGR"]) ? $Format["LabelsBGB"] : 255; 650 $LabelsBGAlpha = isset($Format["LabelsBGAlpha"]) ? $Format["LabelsBGAlpha"] : 50; 651 $LabelPos = isset($Format["LabelPos"]) ? $Format["LabelPos"] : RADAR_LABELS_ROTATED; 652 $LabelPadding = isset($Format["LabelPadding"]) ? $Format["LabelPadding"] : 4; 653 $DrawPoints = isset($Format["DrawPoints"]) ? $Format["DrawPoints"] : true; 654 $PointRadius = isset($Format["PointRadius"]) ? $Format["PointRadius"] : 4; 655 $PointSurrounding = isset($Format["PointRadius"]) ? $Format["PointRadius"] : -30; 656 $DrawLines = isset($Format["DrawLines"]) ? $Format["DrawLines"] : true; 657 $LineLoopStart = isset($Format["LineLoopStart"]) ? $Format["LineLoopStart"] : false; 658 $DrawPoly = isset($Format["DrawPoly"]) ? $Format["DrawPoly"] : false; 659 $PolyAlpha = isset($Format["PolyAlpha"]) ? $Format["PolyAlpha"] : null; 660 $FontSize = $Object->FontSize; 661 $X1 = $Object->GraphAreaX1; 662 $Y1 = $Object->GraphAreaY1; 663 $X2 = $Object->GraphAreaX2; 664 $Y2 = $Object->GraphAreaY2; 665 $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : false; 666 667 if ($AxisBoxRounded) { 668 $DrawAxisValues = true; 669 } 670 671 /* Cancel default tick length if ticks not enabled */ 672 if ($DrawTicks == false) { 673 $TicksLength = 0; 674 } 675 676 /* Data Processing */ 677 $Data = $Values->getData(); 678 $Palette = $Values->getPalette(); 679 680 /* Catch the number of required axis */ 681 $LabelSerie = $Data["Abscissa"]; 682 if ($LabelSerie) { 683 $Points = count($Data["Series"][$LabelSerie]["Data"]); 684 } else { 685 $Points = 0; 686 foreach ($Data["Series"] as $SerieName => $DataArray) { 687 if (count($DataArray["Data"]) > $Points) { 688 $Points = count($DataArray["Data"]); 689 } 690 } 691 } 692 693 /* Draw the axis */ 694 $CenterX = ($X2 - $X1) / 2 + $X1; 695 $CenterY = ($Y2 - $Y1) / 2 + $Y1; 696 697 $EdgeHeight = min(($X2 - $X1) / 2, ($Y2 - $Y1) / 2); 698 if ($WriteLabels) { 699 $EdgeHeight = $EdgeHeight - $FontSize - $LabelPadding - $TicksLength; 700 } 701 702 /* Determine the scale if set to automatic */ 703 if ($SegmentHeight == SEGMENT_HEIGHT_AUTO) { 704 if ($FixedMax != VOID) { 705 $Max = $FixedMax; 706 } else { 707 $Max = 0; 708 foreach ($Data["Series"] as $SerieName => $DataArray) { 709 if ($SerieName != $LabelSerie) { 710 if (max($DataArray["Data"]) > $Max) { 711 $Max = max($DataArray["Data"]); 712 } 713 } 714 } 715 } 716 $MaxSegments = $EdgeHeight / 20; 717 $Scale = $Object->computeScale(0, $Max, $MaxSegments, [1, 2, 5]); 718 719 $Segments = $Scale["Rows"]; 720 $SegmentHeight = $Scale["RowHeight"]; 721 } 722 723 724 /* Background processing */ 725 if ($DrawBackground) { 726 $RestoreShadow = $Object->Shadow; 727 $Object->Shadow = false; 728 729 if ($BackgroundGradient == null) { 730 $Color = [ 731 "R" => $BackgroundR, 732 "G" => $BackgroundG, 733 "B" => $BackgroundB, 734 "Alpha" => $BackgroundAlpha 735 ]; 736 $Object->drawFilledCircle( 737 $CenterX, 738 $CenterY, 739 $EdgeHeight, 740 $Color 741 ); 742 } else { 743 $GradientROffset = ($BackgroundGradient["EndR"] - $BackgroundGradient["StartR"]) / $Segments; 744 $GradientGOffset = ($BackgroundGradient["EndG"] - $BackgroundGradient["StartG"]) / $Segments; 745 $GradientBOffset = ($BackgroundGradient["EndB"] - $BackgroundGradient["StartB"]) / $Segments; 746 $GradientAlphaOffset = ($BackgroundGradient["EndAlpha"] - $BackgroundGradient["StartAlpha"]) 747 / $Segments 748 ; 749 750 for ($j = $Segments; $j >= 1; $j--) { 751 $Color = [ 752 "R" => $BackgroundGradient["StartR"] + $GradientROffset * $j, 753 "G" => $BackgroundGradient["StartG"] + $GradientGOffset * $j, 754 "B" => $BackgroundGradient["StartB"] + $GradientBOffset * $j, 755 "Alpha" => $BackgroundGradient["StartAlpha"] + $GradientAlphaOffset * $j 756 ]; 757 $Object->drawFilledCircle( 758 $CenterX, 759 $CenterY, 760 ($EdgeHeight / $Segments) * $j, 761 $Color 762 ); 763 } 764 } 765 $Object->Shadow = $RestoreShadow; 766 } 767 768 /* Axis to axis lines */ 769 $Color = ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]; 770 for ($j = 1; $j <= $Segments; $j++) { 771 $Radius = ($EdgeHeight / $Segments) * $j; 772 $Object->drawCircle($CenterX, $CenterY, $Radius, $Radius, $Color); 773 } 774 775 if ($DrawAxisValues) { 776 if ($LabelsBackground) { 777 $Options = [ 778 "DrawBox" => true, 779 "Align" => TEXT_ALIGN_MIDDLEMIDDLE, 780 "BoxR" => $LabelsBGR, 781 "BoxG" => $LabelsBGG, 782 "BoxB" => $LabelsBGB, 783 "BoxAlpha" => $LabelsBGAlpha 784 ]; 785 } else { 786 $Options = ["Align" => TEXT_ALIGN_MIDDLEMIDDLE]; 787 } 788 789 if ($AxisBoxRounded) { 790 $Options["BoxRounded"] = true; 791 } 792 793 $Options["FontName"] = $AxisFontName; 794 $Options["FontSize"] = $AxisFontSize; 795 796 $Angle = 360 / ($Points * 2); 797 for ($j = 1; $j <= $Segments; $j++) { 798 $EdgeX1 = cos(deg2rad($Angle + $AxisRotation)) * ($EdgeHeight / $Segments) * $j + $CenterX; 799 $EdgeY1 = sin(deg2rad($Angle + $AxisRotation)) * ($EdgeHeight / $Segments) * $j + $CenterY; 800 $Label = $j * $SegmentHeight; 801 802 $Object->drawText($EdgeX1, $EdgeY1, $Label, $Options); 803 } 804 } 805 806 /* Axis lines */ 807 $ID = 0; 808 for ($i = 0; $i <= 359; $i = $i + $AxisSteps) { 809 $EdgeX = cos(deg2rad($i + $AxisRotation)) * ($EdgeHeight + $TicksLength) + $CenterX; 810 $EdgeY = sin(deg2rad($i + $AxisRotation)) * ($EdgeHeight + $TicksLength) + $CenterY; 811 812 $Object->drawLine($CenterX, $CenterY, $EdgeX, $EdgeY, $Color); 813 814 if ($WriteLabels) { 815 $LabelX = cos(deg2rad($i + $AxisRotation)) 816 * ($EdgeHeight + $LabelPadding + $TicksLength) + $CenterX 817 ; 818 $LabelY = sin(deg2rad($i + $AxisRotation)) 819 * ($EdgeHeight + $LabelPadding + $TicksLength) + $CenterY 820 ; 821 $Label = $i . "°"; 822 823 if ($LabelPos == RADAR_LABELS_ROTATED) { 824 $Object->drawText( 825 $LabelX, 826 $LabelY, 827 $Label, 828 ["Angle" => (360 - $i), "Align" => TEXT_ALIGN_BOTTOMMIDDLE] 829 ); 830 } else { 831 if ((floor($LabelX) == floor($CenterX)) && (floor($LabelY) < floor($CenterY))) { 832 $Object->drawText( 833 $LabelX, 834 $LabelY, 835 $Label, 836 ["Align" => TEXT_ALIGN_BOTTOMMIDDLE] 837 ); 838 } 839 if ((floor($LabelX) > floor($CenterX)) && (floor($LabelY) < floor($CenterY))) { 840 $Object->drawText( 841 $LabelX, 842 $LabelY, 843 $Label, 844 ["Align" => TEXT_ALIGN_BOTTOMLEFT] 845 ); 846 } 847 if ((floor($LabelX) > floor($CenterX)) && (floor($LabelY) == floor($CenterY))) { 848 $Object->drawText( 849 $LabelX, 850 $LabelY, 851 $Label, 852 ["Align" => TEXT_ALIGN_MIDDLELEFT] 853 ); 854 } 855 if ((floor($LabelX) > floor($CenterX)) && (floor($LabelY) > floor($CenterY))) { 856 $Object->drawText( 857 $LabelX, 858 $LabelY, 859 $Label, 860 ["Align" => TEXT_ALIGN_TOPLEFT] 861 ); 862 } 863 if ((floor($LabelX) < floor($CenterX)) && (floor($LabelY) < floor($CenterY))) { 864 $Object->drawText( 865 $LabelX, 866 $LabelY, 867 $Label, 868 ["Align" => TEXT_ALIGN_BOTTOMRIGHT] 869 ); 870 } 871 if ((floor($LabelX) < floor($CenterX)) && (floor($LabelY) == floor($CenterY))) { 872 $Object->drawText( 873 $LabelX, 874 $LabelY, 875 $Label, 876 ["Align" => TEXT_ALIGN_MIDDLERIGHT] 877 ); 878 } 879 if ((floor($LabelX) < floor($CenterX)) && (floor($LabelY) > floor($CenterY))) { 880 $Object->drawText( 881 $LabelX, 882 $LabelY, 883 $Label, 884 ["Align" => TEXT_ALIGN_TOPRIGHT] 885 ); 886 } 887 if ((floor($LabelX) == floor($CenterX)) && (floor($LabelY) > floor($CenterY))) { 888 $Object->drawText( 889 $LabelX, 890 $LabelY, 891 $Label, 892 ["Align" => TEXT_ALIGN_TOPMIDDLE] 893 ); 894 } 895 } 896 } 897 $ID++; 898 } 899 900 /* Compute the plots position */ 901 $ID = 0; 902 $Plot = []; 903 foreach ($Data["Series"] as $SerieName => $DataSet) { 904 if ($SerieName != $LabelSerie) { 905 $Color = [ 906 "R" => $Palette[$ID]["R"], 907 "G" => $Palette[$ID]["G"], 908 "B" => $Palette[$ID]["B"], 909 "Alpha" => $Palette[$ID]["Alpha"], 910 "Surrounding" => $PointSurrounding 911 ]; 912 foreach ($DataSet["Data"] as $Key => $Value) { 913 $Angle = $Data["Series"][$LabelSerie]["Data"][$Key]; 914 $Length = ($EdgeHeight / ($Segments * $SegmentHeight)) * $Value; 915 916 $X = cos(deg2rad($Angle + $AxisRotation)) * $Length + $CenterX; 917 $Y = sin(deg2rad($Angle + $AxisRotation)) * $Length + $CenterY; 918 919 if ($RecordImageMap) { 920 $this->pChartObject->addToImageMap( 921 "CIRCLE", 922 sprintf("%s,%s,%s", floor($X), floor($Y), floor($PointRadius)), 923 $this->pChartObject->toHTMLColor( 924 $Palette[$ID]["R"], 925 $Palette[$ID]["G"], 926 $Palette[$ID]["B"] 927 ), 928 $DataSet["Description"], 929 $Data["Series"][$LabelSerie]["Data"][$Key] . "° = " . $Value 930 ); 931 } 932 933 $Plot[$ID][] = [$X, $Y, $Value]; 934 } 935 $ID++; 936 } 937 } 938 939 /* Draw all that stuff! */ 940 foreach ($Plot as $ID => $Points) { 941 $Color = [ 942 "R" => $Palette[$ID]["R"], 943 "G" => $Palette[$ID]["G"], 944 "B" => $Palette[$ID]["B"], 945 "Alpha" => $Palette[$ID]["Alpha"], 946 "Surrounding" => $PointSurrounding 947 ]; 948 949 /* Draw the polygons */ 950 if ($DrawPoly) { 951 if ($PolyAlpha != null) { 952 $Color = [ 953 "R" => $Palette[$ID]["R"], 954 "G" => $Palette[$ID]["G"], 955 "B" => $Palette[$ID]["B"], 956 "Alpha" => $PolyAlpha, 957 "Surrounding" => $PointSurrounding 958 ]; 959 } 960 $PointsArray = []; 961 for ($i = 0; $i < count($Points); $i++) { 962 $PointsArray[] = $Points[$i][0]; 963 $PointsArray[] = $Points[$i][1]; 964 } 965 966 $Object->drawPolygon($PointsArray, $Color); 967 } 968 969 $Color = [ 970 "R" => $Palette[$ID]["R"], 971 "G" => $Palette[$ID]["G"], 972 "B" => $Palette[$ID]["B"], 973 "Alpha" => $Palette[$ID]["Alpha"], 974 "Surrounding" => $PointSurrounding 975 ]; 976 977 /* Bubble and labels settings */ 978 $TextSettings = [ 979 "Align" => TEXT_ALIGN_MIDDLEMIDDLE, 980 "FontName" => $ValueFontName, 981 "FontSize" => $ValueFontSize, 982 "R" => $Palette[$ID]["R"], 983 "G" => $Palette[$ID]["G"], 984 "B" => $Palette[$ID]["B"] 985 ]; 986 $InnerColor = [ 987 "R" => $InnerBubbleR, 988 "G" => $InnerBubbleG, 989 "B" => $InnerBubbleB, 990 "Alpha" => $InnerBubbleAlpha 991 ]; 992 if ($OuterBubbleR != VOID) { 993 $OuterColor = [ 994 "R" => $OuterBubbleR, 995 "G" => $OuterBubbleG, 996 "B" => $OuterBubbleB, 997 "Alpha" => $OuterBubbleAlpha 998 ]; 999 } else { 1000 $OuterColor = [ 1001 "R" => $Palette[$ID]["R"] + 20, 1002 "G" => $Palette[$ID]["G"] + 20, 1003 "B" => $Palette[$ID]["B"] + 20, 1004 "Alpha" => $Palette[$ID]["Alpha"] 1005 ]; 1006 } 1007 /* Loop to the starting points if asked */ 1008 if ($LineLoopStart && $DrawLines) { 1009 $Object->drawLine( 1010 $Points[count($Points) - 1][0], 1011 $Points[count($Points) - 1][1], 1012 $Points[0][0], 1013 $Points[0][1], 1014 $Color 1015 ); 1016 } 1017 /* Draw the lines & points */ 1018 for ($i = 0; $i < count($Points); $i++) { 1019 if ($DrawLines && $i < count($Points) - 1) { 1020 $Object->drawLine( 1021 $Points[$i][0], 1022 $Points[$i][1], 1023 $Points[$i + 1][0], 1024 $Points[$i + 1][1], 1025 $Color 1026 ); 1027 } 1028 if ($DrawPoints) { 1029 $Object->drawFilledCircle( 1030 $Points[$i][0], 1031 $Points[$i][1], 1032 $PointRadius, 1033 $Color 1034 ); 1035 } 1036 if ($WriteValuesInBubble && $WriteValues) { 1037 $TxtPos = $this->pChartObject->getTextBox( 1038 $Points[$i][0], 1039 $Points[$i][1], 1040 $ValueFontName, 1041 $ValueFontSize, 1042 0, 1043 $Points[$i][2] 1044 ); 1045 $Radius = floor(($TxtPos[1]["X"] - $TxtPos[0]["X"] + $ValuePadding * 2) / 2); 1046 1047 $this->pChartObject->drawFilledCircle( 1048 $Points[$i][0], 1049 $Points[$i][1], 1050 $Radius + $OuterBubbleRadius, 1051 $OuterColor 1052 ); 1053 $this->pChartObject->drawFilledCircle( 1054 $Points[$i][0], 1055 $Points[$i][1], 1056 $Radius, 1057 $InnerColor 1058 ); 1059 } 1060 1061 if ($WriteValues) { 1062 $this->pChartObject->drawText( 1063 $Points[$i][0] - 1, 1064 $Points[$i][1] - 1, 1065 $Points[$i][2], 1066 $TextSettings 1067 ); 1068 } 1069 } 1070 } 1071 } 1072} 1073