GanttGraph::Stroke()   F
last analyzed

Complexity

Conditions 12
Paths 864

Size

Total Lines 109
Code Lines 55

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 156

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 12
eloc 55
nc 864
nop 1
dl 0
loc 109
ccs 0
cts 70
cp 0
crap 156
rs 2.9888
c 1
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * JPGraph v4.0.3
5
 */
6
7
namespace Amenadiel\JpGraph\Graph;
8
9
use Amenadiel\JpGraph\Image;
10
use Amenadiel\JpGraph\Plot;
11
use Amenadiel\JpGraph\Text;
12
use Amenadiel\JpGraph\Util;
13
14
/**
15
 * @class GanttGraph
16
 * // Description: Main class to handle gantt graphs
17
 */
18
class GanttGraph extends Graph
19
{
20
    public $scale; // Public accessible
21
    public $hgrid;
22
    private $iObj                    = []; // Gantt objects
23
    private $iLabelHMarginFactor     = 0.2; // 10% margin on each side of the labels
0 ignored issues
show
introduced by
The private property $iLabelHMarginFactor is not used, and could be removed.
Loading history...
24
    private $iLabelVMarginFactor     = 0.4; // 40% margin on top and bottom of label
25
    private $iLayout                 = GANTT_FROMTOP; // Could also be GANTT_EVEN
26
    private $iSimpleFont             = FF_FONT1;
27
    private $iSimpleFontSize         = 11;
28
    private $iSimpleStyle            = GANTT_RDIAG;
29
    private $iSimpleColor            = 'yellow';
30
    private $iSimpleBkgColor         = 'red';
31
    private $iSimpleProgressBkgColor = 'gray';
32
    private $iSimpleProgressColor    = 'darkgreen';
33
    private $iSimpleProgressStyle    = GANTT_SOLID;
34
    private $iZoomFactor             = 1.0;
35
36
    /**
37
     * CONSTRUCTOR
38
     * // Create a new gantt graph.
39
     *
40
     * @param mixed $aWidth
41
     * @param mixed $aHeight
42
     * @param mixed $aCachedName
43
     * @param mixed $aTimeOut
44
     * @param mixed $aInline
45
     */
46
    public function __construct($aWidth = 0, $aHeight = 0, $aCachedName = '', $aTimeOut = 0, $aInline = true)
47
    {
48
        // Backward compatibility
49
        if ($aWidth == -1) {
50
            $aWidth = 0;
51
        }
52
53
        if ($aHeight == -1) {
54
            $aHeight = 0;
55
        }
56
57
        if ($aWidth < 0 || $aHeight < 0) {
58
            Util\JpGraphError::RaiseL(6002);
59
            //("You can't specify negative sizes for Gantt graph dimensions. Use 0 to indicate that you want the library to automatically determine a dimension.");
60
        }
61
        parent::__construct($aWidth, $aHeight, $aCachedName, $aTimeOut, $aInline);
62
        $this->scale = new GanttScale($this->img);
63
64
        // Default margins
65
        $this->img->SetMargin(15, 17, 25, 15);
66
67
        $this->hgrid = new HorizontalGridLine();
68
69
        $this->scale->ShowHeaders(GANTT_HWEEK | GANTT_HDAY);
70
        $this->SetBox();
71
    }
72
73
    /**
74
     * PUBLIC METHODS.
75
     *
76
     * @param mixed $aFont
77
     * @param mixed $aSize
78
     */
79
    public function SetSimpleFont($aFont, $aSize)
80
    {
81
        $this->iSimpleFont     = $aFont;
82
        $this->iSimpleFontSize = $aSize;
83
    }
84
85
    public function SetSimpleStyle($aBand, $aColor, $aBkgColor)
86
    {
87
        $this->iSimpleStyle    = $aBand;
88
        $this->iSimpleColor    = $aColor;
89
        $this->iSimpleBkgColor = $aBkgColor;
90
    }
91
92
    // A utility function to help create basic Gantt charts
93
    public function CreateSimple($data, $constrains = [], $progress = [])
94
    {
95
        $num = safe_count($data);
96
        for ($i = 0; $i < $num; ++$i) {
97
            switch ($data[$i][1]) {
98
                case ACTYPE_GROUP:
99
                    // Create a slightly smaller height bar since the
100
                    // "wings" at the end will make it look taller
101
                    $a = new Plot\GanttBar($data[$i][0], $data[$i][2], $data[$i][3], $data[$i][4], '', 8);
102
                    $a->title->SetFont($this->iSimpleFont, FS_BOLD, $this->iSimpleFontSize);
103
                    $a->rightMark->Show();
104
                    $a->rightMark->SetType(MARK_RIGHTTRIANGLE);
105
                    $a->rightMark->SetWidth(8);
106
                    $a->rightMark->SetColor('black');
107
                    $a->rightMark->SetFillColor('black');
108
109
                    $a->leftMark->Show();
110
                    $a->leftMark->SetType(MARK_LEFTTRIANGLE);
111
                    $a->leftMark->SetWidth(8);
112
                    $a->leftMark->SetColor('black');
113
                    $a->leftMark->SetFillColor('black');
114
115
                    $a->SetPattern(BAND_SOLID, 'black');
116
                    $csimpos = 6;
117
118
                    break;
119
                case ACTYPE_NORMAL:
120
                    $a = new Plot\GanttBar($data[$i][0], $data[$i][2], $data[$i][3], $data[$i][4], '', 10);
121
                    $a->title->SetFont($this->iSimpleFont, FS_NORMAL, $this->iSimpleFontSize);
122
                    $a->SetPattern($this->iSimpleStyle, $this->iSimpleColor);
123
                    $a->SetFillColor($this->iSimpleBkgColor);
124
                    // Check if this activity should have a constrain line
125
                    $n = safe_count($constrains);
126
                    for ($j = 0; $j < $n; ++$j) {
127
                        if (empty($constrains[$j]) || (safe_count($constrains[$j]) != 3)) {
128
                            Util\JpGraphError::RaiseL(6003, $j);
129
                            //("Invalid format for Constrain parameter at index=$j in CreateSimple(). Parameter must start with index 0 and contain arrays of (Row,Constrain-To,Constrain-Type)");
130
                        }
131
                        if ($constrains[$j][0] == $data[$i][0]) {
132
                            $a->SetConstrain($constrains[$j][1], $constrains[$j][2], 'black', ARROW_S2, ARROWT_SOLID);
133
                        }
134
                    }
135
136
                    // Check if this activity have a progress bar
137
                    $n = safe_count($progress);
138
                    for ($j = 0; $j < $n; ++$j) {
139
                        if (empty($progress[$j]) || (safe_count($progress[$j]) != 2)) {
140
                            Util\JpGraphError::RaiseL(6004, $j);
141
                            //("Invalid format for Progress parameter at index=$j in CreateSimple(). Parameter must start with index 0 and contain arrays of (Row,Progress)");
142
                        }
143
                        if ($progress[$j][0] == $data[$i][0]) {
144
                            $a->progress->Set($progress[$j][1]);
145
                            $a->progress->SetPattern(
146
                                $this->iSimpleProgressStyle,
147
                                $this->iSimpleProgressColor
148
                            );
149
                            $a->progress->SetFillColor($this->iSimpleProgressBkgColor);
150
                            //$a->progress->SetPattern($progress[$j][2],$progress[$j][3]);
151
                            break;
152
                        }
153
                    }
154
                    $csimpos = 6;
155
156
                    break;
157
                case ACTYPE_MILESTONE:
158
                    $a = new Plot\MileStone($data[$i][0], $data[$i][2], $data[$i][3]);
159
                    $a->title->SetFont($this->iSimpleFont, FS_NORMAL, $this->iSimpleFontSize);
160
                    $a->caption->SetFont($this->iSimpleFont, FS_NORMAL, $this->iSimpleFontSize);
161
                    $csimpos = 5;
162
163
                    break;
164
                default:
165
                    die('Unknown activity type');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
166
167
                    break;
168
            }
169
170
            // Setup caption
171
            $a->caption->Set($data[$i][$csimpos - 1]);
172
173
            // Check if this activity should have a CSIM target�?
174
            if (!empty($data[$i][$csimpos])) {
175
                $a->SetCSIMTarget($data[$i][$csimpos]);
176
                $a->SetCSIMAlt($data[$i][$csimpos + 1]);
177
            }
178
            if (!empty($data[$i][$csimpos + 2])) {
179
                $a->title->SetCSIMTarget($data[$i][$csimpos + 2]);
180
                $a->title->SetCSIMAlt($data[$i][$csimpos + 3]);
181
            }
182
183
            $this->Add($a);
184
        }
185
    }
186
187
    // Set user specified scale zoom factor when auto sizing is used
188
    public function SetZoomFactor($aZoom)
189
    {
190
        $this->iZoomFactor = $aZoom;
191
    }
192
193
    // Set what headers should be shown
194
    public function ShowHeaders($aFlg)
195
    {
196
        $this->scale->ShowHeaders($aFlg);
197
    }
198
199
    // Specify the fraction of the font height that should be added
200
    // as vertical margin
201
    public function SetLabelVMarginFactor($aVal)
202
    {
203
        $this->iLabelVMarginFactor = $aVal;
204
    }
205
206
    // Synonym to the method above
207
    public function SetVMarginFactor($aVal)
208
    {
209
        $this->iLabelVMarginFactor = $aVal;
210
    }
211
212
    // Add a new Gantt object
213
    public function Add($aObject)
214
    {
215
        if (is_array($aObject) && safe_count($aObject) > 0) {
216
            $cl = $aObject[0];
217
            if (($cl instanceof Plot\IconPlot)) {
218
                $this->AddIcon($aObject);
219
            } elseif (($cl instanceof Text\Text)) {
220
                $this->AddText($aObject);
221
            } else {
222
                $n = safe_count($aObject);
223
                for ($i = 0; $i < $n; ++$i) {
224
                    $this->iObj[] = $aObject[$i];
225
                }
226
            }
227
        } else {
228
            if (($aObject instanceof Plot\IconPlot)) {
229
                $this->AddIcon($aObject);
230
            } elseif (($aObject instanceof Text\Text)) {
231
                $this->AddText($aObject);
232
            } else {
233
                $this->iObj[] = $aObject;
234
            }
235
        }
236
    }
237
238
    public function StrokeTexts()
239
    {
240
        // Stroke any user added text objects
241
        if ($this->texts != null) {
242
            $n = safe_count($this->texts);
243
            for ($i = 0; $i < $n; ++$i) {
244
                if ($this->texts[$i]->iScalePosX !== null && $this->texts[$i]->iScalePosY !== null) {
245
                    $x = $this->scale->TranslateDate($this->texts[$i]->iScalePosX);
246
                    $y = $this->scale->TranslateVertPos($this->texts[$i]->iScalePosY);
247
                    $y -= $this->scale->GetVertSpacing() / 2;
248
                } else {
249
                    $x = $y = null;
250
                }
251
                $this->texts[$i]->Stroke($this->img, $x, $y);
252
            }
253
        }
254
    }
255
256
    // Override inherit method from Graph and give a warning message
257
    public function SetScale($aAxisType, $aYMin = 1, $aYMax = 1, $aXMin = 1, $aXMax = 1)
258
    {
259
        Util\JpGraphError::RaiseL(6005);
260
        //("SetScale() is not meaningfull with Gantt charts.");
261
    }
262
263
    // Specify the date range for Gantt graphs (if this is not set it will be
264
    // automtically determined from the input data)
265
    public function SetDateRange($aStart, $aEnd)
266
    {
267
        // Adjust the start and end so that the indicate the
268
        // begining and end of respective start and end days
269
        if (strpos($aStart, ':') === false) {
270
            $aStart = date('Y-m-d 00:00', strtotime($aStart));
271
        }
272
273
        if (strpos($aEnd, ':') === false) {
274
            $aEnd = date('Y-m-d 23:59', strtotime($aEnd));
275
        }
276
277
        $this->scale->SetRange($aStart, $aEnd);
278
    }
279
280
    // Get the maximum width of the activity titles columns for the bars
281
    // The name is lightly misleading since we from now on can have
282
    // multiple columns in the label section. When this was first written
283
    // it only supported a single label, hence the name.
284
    public function GetMaxLabelWidth()
285
    {
286
        $m = 10;
287
        if ($this->iObj != null) {
288
            $marg = $this->scale->actinfo->iLeftColMargin + $this->scale->actinfo->iRightColMargin;
0 ignored issues
show
Unused Code introduced by
The assignment to $marg is dead and can be removed.
Loading history...
289
            $n    = safe_count($this->iObj);
290
            for ($i = 0; $i < $n; ++$i) {
291
                if (!empty($this->iObj[$i]->title)) {
292
                    if ($this->iObj[$i]->title->HasTabs()) {
293
                        list($tot, $w) = $this->iObj[$i]->title->GetWidth($this->img, true);
294
                        $m             = max($m, $tot);
295
                    } else {
296
                        $m = max($m, $this->iObj[$i]->title->GetWidth($this->img));
297
                    }
298
                }
299
            }
300
        }
301
302
        return $m;
303
    }
304
305
    // Get the maximum height of the titles for the bars
306
    public function GetMaxLabelHeight()
307
    {
308
        $m = 10;
309
        if ($this->iObj != null) {
310
            $n = safe_count($this->iObj);
311
            // We can not include the title of GnttVLine since that title is stroked at the bottom
312
            // of the Gantt bar and not in the activity title columns
313
            for ($i = 0; $i < $n; ++$i) {
314
                if (!empty($this->iObj[$i]->title) && !($this->iObj[$i] instanceof Plot\GanttVLine)) {
315
                    $m = max($m, $this->iObj[$i]->title->GetHeight($this->img));
316
                }
317
            }
318
        }
319
320
        return $m;
321
    }
322
323
    public function GetMaxBarAbsHeight()
324
    {
325
        $m = 0;
326
        if ($this->iObj != null) {
327
            $m = $this->iObj[0]->GetAbsHeight($this->img);
328
            $n = safe_count($this->iObj);
329
            for ($i = 1; $i < $n; ++$i) {
330
                $m = max($m, $this->iObj[$i]->GetAbsHeight($this->img));
331
            }
332
        }
333
334
        return $m;
335
    }
336
337
    // Get the maximum used line number (vertical position) for bars
338
    public function GetBarMaxLineNumber()
339
    {
340
        $m = 1;
341
        if ($this->iObj != null) {
342
            $m = $this->iObj[0]->GetLineNbr();
343
            $n = safe_count($this->iObj);
344
            for ($i = 1; $i < $n; ++$i) {
345
                $m = max($m, $this->iObj[$i]->GetLineNbr());
346
            }
347
        }
348
349
        return $m;
350
    }
351
352
    // Get the minumum and maximum used dates for all bars
353
    public function GetBarMinMax()
354
    {
355
        $start = 0;
356
        $n     = safe_count($this->iObj);
357
        while ($start < $n && $this->iObj[$start]->GetMaxDate() === false) {
358
            ++$start;
359
        }
360
361
        if ($start >= $n) {
362
            Util\JpGraphError::RaiseL(6006);
363
            //('Cannot autoscale Gantt chart. No dated activities exist. [GetBarMinMax() start >= n]');
364
        }
365
366
        $max = $this->scale->NormalizeDate($this->iObj[$start]->GetMaxDate());
367
        $min = $this->scale->NormalizeDate($this->iObj[$start]->GetMinDate());
368
369
        for ($i = $start + 1; $i < $n; ++$i) {
370
            $rmax = $this->scale->NormalizeDate($this->iObj[$i]->GetMaxDate());
371
            if ($rmax != false) {
372
                $max = max($max, $rmax);
373
            }
374
375
            $rmin = $this->scale->NormalizeDate($this->iObj[$i]->GetMinDate());
376
            if ($rmin != false) {
377
                $min = min($min, $rmin);
378
            }
379
        }
380
        $minDate = date('Y-m-d', $min);
381
        $min     = strtotime($minDate);
382
        $maxDate = date('Y-m-d 23:59', $max);
383
        $max     = strtotime($maxDate);
384
385
        return [$min, $max];
386
    }
387
388
    // Create a new auto sized canvas if the user hasn't specified a size
389
    // The size is determined by what scale the user has choosen and hence
390
    // the minimum width needed to display the headers. Some margins are
391
    // also added to make it better looking.
392
    public function AutoSize()
393
    {
394
        if ($this->img->img == null) {
395
            // The predefined left, right, top, bottom margins.
396
            // Note that the top margin might incease depending on
397
            // the title.
398
            $hadj = $vadj = 0;
399
            if ($this->doshadow) {
400
                $hadj = $this->shadow_width;
401
                $vadj = $this->shadow_width + 5;
402
            }
403
404
            $lm = $this->img->left_margin;
405
            $rm = $this->img->right_margin + $hadj;
406
            $rm += 2;
407
            $tm = $this->img->top_margin;
408
            $bm = $this->img->bottom_margin + $vadj;
409
            $bm += 2;
410
411
            // If there are any added Plot\GanttVLine we must make sure that the
412
            // bottom margin is wide enough to hold a title.
413
            $n = safe_count($this->iObj);
414
            for ($i = 0; $i < $n; ++$i) {
415
                if ($this->iObj[$i] instanceof Plot\GanttVLine) {
416
                    $bm = max($bm, $this->iObj[$i]->title->GetHeight($this->img) + 10);
417
                }
418
            }
419
420
            // First find out the height
421
            $n      = $this->GetBarMaxLineNumber() + 1;
422
            $m      = max($this->GetMaxLabelHeight(), $this->GetMaxBarAbsHeight());
423
            $height = $n * ((1 + $this->iLabelVMarginFactor) * $m);
424
425
            // Add the height of the scale titles
426
            $h = $this->scale->GetHeaderHeight();
427
            $height += $h;
428
429
            // Calculate the top margin needed for title and subtitle
430
            if ($this->title->t != '') {
431
                $tm += $this->title->GetFontHeight($this->img);
432
            }
433
            if ($this->subtitle->t != '') {
434
                $tm += $this->subtitle->GetFontHeight($this->img);
435
            }
436
437
            // ...and then take the bottom and top plot margins into account
438
            $height += $tm + $bm + $this->scale->iTopPlotMargin + $this->scale->iBottomPlotMargin;
439
            // Now find the minimum width for the chart required
440
441
            // If day scale or smaller is shown then we use the day font width
442
            // as the base size unit.
443
            // If only weeks or above is displayed we use a modified unit to
444
            // get a smaller image.
445
            if ($this->scale->IsDisplayHour() || $this->scale->IsDisplayMinute()) {
446
                // Add 2 pixel margin on each side
447
                $fw = $this->scale->day->GetFontWidth($this->img) + 4;
448
            } elseif ($this->scale->IsDisplayWeek()) {
449
                $fw = 8;
450
            } elseif ($this->scale->IsDisplayMonth()) {
451
                $fw = 4;
452
            } else {
453
                $fw = 2;
454
            }
455
456
            $nd = $this->scale->GetNumberOfDays();
457
458
            if ($this->scale->IsDisplayDay()) {
459
                // If the days are displayed we also need to figure out
460
                // how much space each day's title will require.
461
                switch ($this->scale->day->iStyle) {
462
                    case DAYSTYLE_LONG:
463
                        $txt = 'Monday';
464
465
                        break;
466
                    case DAYSTYLE_LONGDAYDATE1:
467
                        $txt = 'Monday 23 Jun';
468
469
                        break;
470
                    case DAYSTYLE_LONGDAYDATE2:
471
                        $txt = 'Monday 23 Jun 2003';
472
473
                        break;
474
                    case DAYSTYLE_SHORT:
475
                        $txt = 'Mon';
476
477
                        break;
478
                    case DAYSTYLE_SHORTDAYDATE1:
479
                        $txt = 'Mon 23/6';
480
481
                        break;
482
                    case DAYSTYLE_SHORTDAYDATE2:
483
                        $txt = 'Mon 23 Jun';
484
485
                        break;
486
                    case DAYSTYLE_SHORTDAYDATE3:
487
                        $txt = 'Mon 23';
488
489
                        break;
490
                    case DAYSTYLE_SHORTDATE1:
491
                        $txt = '23/6';
492
493
                        break;
494
                    case DAYSTYLE_SHORTDATE2:
495
                        $txt = '23 Jun';
496
497
                        break;
498
                    case DAYSTYLE_SHORTDATE3:
499
                        $txt = 'Mon 23';
500
501
                        break;
502
                    case DAYSTYLE_SHORTDATE4:
503
                        $txt = '88';
504
505
                        break;
506
                    case DAYSTYLE_CUSTOM:
507
                        $txt = date($this->scale->day->iLabelFormStr, strtotime('2003-12-20 18:00'));
508
509
                        break;
510
                    case DAYSTYLE_ONELETTER:
511
                    default:
512
                        $txt = 'M';
513
514
                        break;
515
                }
516
                $fw = $this->scale->day->GetStrWidth($this->img, $txt) + 6;
517
            }
518
519
            // If we have hours enabled we must make sure that each day has enough
520
            // space to fit the number of hours to be displayed.
521
            if ($this->scale->IsDisplayHour()) {
522
                // Depending on what format the user has choose we need different amount
523
                // of space. We therefore create a typical string for the choosen format
524
                // and determine the length of that string.
525
                switch ($this->scale->hour->iStyle) {
526
                    case HOURSTYLE_HMAMPM:
527
                        $txt = '12:00pm';
528
529
                        break;
530
                    case HOURSTYLE_H24:
531
                        // 13
532
                        $txt = '24';
533
534
                        break;
535
                    case HOURSTYLE_HAMPM:
536
                        $txt = '12pm';
537
538
                        break;
539
                    case HOURSTYLE_CUSTOM:
540
                        $txt = date($this->scale->hour->iLabelFormStr, strtotime('2003-12-20 18:00'));
541
542
                        break;
543
                    case HOURSTYLE_HM24:
544
                    default:
545
                        $txt = '24:00';
546
547
                        break;
548
                }
549
550
                $hfw = $this->scale->hour->GetStrWidth($this->img, $txt) + 6;
551
                $mw  = $hfw;
552
                if ($this->scale->IsDisplayMinute()) {
553
                    // Depending on what format the user has choose we need different amount
554
                    // of space. We therefore create a typical string for the choosen format
555
                    // and determine the length of that string.
556
                    switch ($this->scale->minute->iStyle) {
557
                        case HOURSTYLE_CUSTOM:
558
                            $txt2 = date($this->scale->minute->iLabelFormStr, strtotime('2005-05-15 18:55'));
559
560
                            break;
561
                        case MINUTESTYLE_MM:
562
                        default:
563
                            $txt2 = '15';
564
565
                            break;
566
                    }
567
568
                    $mfw = $this->scale->minute->GetStrWidth($this->img, $txt2) + 6;
569
                    $n2  = ceil(60 / $this->scale->minute->GetIntervall());
570
                    $mw  = $n2 * $mfw;
571
                }
572
                $hfw = $hfw < $mw ? $mw : $hfw;
573
                $n   = ceil(24 * 60 / $this->scale->TimeToMinutes($this->scale->hour->GetIntervall()));
574
                $hw  = $n * $hfw;
575
                $fw  = $fw < $hw ? $hw : $fw;
576
            }
577
578
            // We need to repeat this code block here as well.
579
            // THIS iS NOT A MISTAKE !
580
            // We really need it since we need to adjust for minutes both in the case
581
            // where hour scale is shown and when it is not shown.
582
583
            if ($this->scale->IsDisplayMinute()) {
584
                // Depending on what format the user has choose we need different amount
585
                // of space. We therefore create a typical string for the choosen format
586
                // and determine the length of that string.
587
                switch ($this->scale->minute->iStyle) {
588
                    case HOURSTYLE_CUSTOM:
589
                        $txt = date($this->scale->minute->iLabelFormStr, strtotime('2005-05-15 18:55'));
590
591
                        break;
592
                    case MINUTESTYLE_MM:
593
                    default:
594
                        $txt = '15';
595
596
                        break;
597
                }
598
599
                $mfw = $this->scale->minute->GetStrWidth($this->img, $txt) + 6;
600
                $n   = ceil(60 / $this->scale->TimeToMinutes($this->scale->minute->GetIntervall()));
601
                $mw  = $n * $mfw;
602
                $fw  = $fw < $mw ? $mw : $fw;
603
            }
604
605
            // If we display week we must make sure that 7*$fw is enough
606
            // to fit up to 10 characters of the week font (if the week is enabled)
607
            if ($this->scale->IsDisplayWeek()) {
608
                // Depending on what format the user has choose we need different amount
609
                // of space
610
                $fsw = strlen($this->scale->week->iLabelFormStr);
611
                if ($this->scale->week->iStyle == WEEKSTYLE_FIRSTDAY2WNBR) {
612
                    $fsw += 8;
613
                } elseif ($this->scale->week->iStyle == WEEKSTYLE_FIRSTDAYWNBR) {
614
                    $fsw += 7;
615
                } else {
616
                    $fsw += 4;
617
                }
618
619
                $ww = $fsw * $this->scale->week->GetFontWidth($this->img);
620
                if (7 * $fw < $ww) {
621
                    $fw = ceil($ww / 7);
622
                }
623
            }
624
625
            if (!$this->scale->IsDisplayDay() && !$this->scale->IsDisplayHour() &&
626
                !(($this->scale->week->iStyle == WEEKSTYLE_FIRSTDAYWNBR ||
627
                    $this->scale->week->iStyle == WEEKSTYLE_FIRSTDAY2WNBR) && $this->scale->IsDisplayWeek())) {
628
                // If we don't display the individual days we can shrink the
629
                // scale a little bit. This is a little bit pragmatic at the
630
                // moment and should be re-written to take into account
631
                // a) What scales exactly are shown and
632
                // b) what format do they use so we know how wide we need to
633
                // make each scale text space at minimum.
634
                $fw /= 2;
635
                if (!$this->scale->IsDisplayWeek()) {
636
                    $fw /= 1.8;
637
                }
638
            }
639
640
            $cw = $this->GetMaxActInfoColWidth();
641
            $this->scale->actinfo->SetMinColWidth($cw);
642
            if ($this->img->width <= 0) {
643
                // Now determine the width for the activity titles column
644
645
                // Firdst find out the maximum width of each object column
646
                $titlewidth = max(
647
                    max(
648
                        $this->GetMaxLabelWidth(),
649
                        $this->scale->tableTitle->GetWidth($this->img)
650
                    ),
651
                    $this->scale->actinfo->GetWidth($this->img)
652
                );
653
654
                // Add the width of the vertivcal divider line
655
                $titlewidth += $this->scale->divider->iWeight * 2;
656
657
                // Adjust the width by the user specified zoom factor
658
                $fw *= $this->iZoomFactor;
659
660
                // Now get the total width taking
661
                // titlewidth, left and rigt margin, dayfont size
662
                // into account
663
                $width = $titlewidth + $nd * $fw + $lm + $rm;
664
            } else {
665
                $width = $this->img->width;
666
            }
667
668
            $width  = round($width);
669
            $height = round($height);
670
            // Make a sanity check on image size
671
            if ($width > MAX_GANTTIMG_SIZE_W || $height > MAX_GANTTIMG_SIZE_H) {
672
                Util\JpGraphError::RaiseL(6007, $width, $height);
673
                //("Sanity check for automatic Gantt chart size failed. Either the width (=$width) or height (=$height) is larger than MAX_GANTTIMG_SIZE. This could potentially be caused by a wrong date in one of the activities.");
674
            }
675
            $this->img->CreateImgCanvas($width, $height);
676
            $this->img->SetMargin($lm, $rm, $tm, $bm);
677
        }
678
    }
679
680
    // Return an array width the maximum width for each activity
681
    // column. This is used when we autosize the columns where we need
682
    // to find out the maximum width of each column. In order to do that we
683
    // must walk through all the objects, sigh...
684
    public function GetMaxActInfoColWidth()
685
    {
686
        $n = safe_count($this->iObj);
687
        if ($n == 0) {
688
            return;
689
        }
690
691
        $w = [];
692
        $m = $this->scale->actinfo->iLeftColMargin + $this->scale->actinfo->iRightColMargin;
693
694
        for ($i = 0; $i < $n; ++$i) {
695
            $tmp = $this->iObj[$i]->title->GetColWidth($this->img, $m);
696
            $nn  = safe_count($tmp);
697
            for ($j = 0; $j < $nn; ++$j) {
698
                if (empty($w[$j])) {
699
                    $w[$j] = $tmp[$j];
700
                } else {
701
                    $w[$j] = max($w[$j], $tmp[$j]);
702
                }
703
            }
704
        }
705
706
        return $w;
707
    }
708
709
    // Stroke the gantt chart
710
    public function Stroke($aStrokeFileName = '')
711
    {
712
        // If the filename is the predefined value = '_csim_special_'
713
        // we assume that the call to stroke only needs to do enough
714
        // to correctly generate the CSIM maps.
715
        // We use this variable to skip things we don't strictly need
716
        // to do to generate the image map to improve performance
717
        // a best we can. Therefor you will see a lot of tests !$_csim in the
718
        // code below.
719
        $_csim = ($aStrokeFileName === _CSIM_SPECIALFILE);
720
721
        // Should we autoscale dates?
722
723
        if (!$this->scale->IsRangeSet()) {
724
            list($min, $max) = $this->GetBarMinMax();
725
            $this->scale->SetRange($min, $max);
726
        }
727
728
        $this->scale->AdjustStartEndDay();
729
730
        // Check if we should autoscale the image
731
        $this->AutoSize();
732
733
        // Should we start from the top or just spread the bars out even over the
734
        // available height
735
        $this->scale->SetVertLayout($this->iLayout);
736
        if ($this->iLayout == GANTT_FROMTOP) {
737
            $maxheight = max($this->GetMaxLabelHeight(), $this->GetMaxBarAbsHeight());
738
            $this->scale->SetVertSpacing($maxheight * (1 + $this->iLabelVMarginFactor));
739
        }
740
        // If it hasn't been set find out the maximum line number
741
        if ($this->scale->iVertLines == -1) {
742
            $this->scale->iVertLines = $this->GetBarMaxLineNumber() + 1;
743
        }
744
745
        $maxwidth = max(
746
            $this->scale->actinfo->GetWidth($this->img),
747
            max(
748
                $this->GetMaxLabelWidth(),
749
                $this->scale->tableTitle->GetWidth($this->img)
750
            )
751
        );
752
753
        $this->scale->SetLabelWidth($maxwidth + $this->scale->divider->iWeight); //*(1+$this->iLabelHMarginFactor));
754
755
        if (!$_csim) {
756
            $this->StrokePlotArea();
757
            if ($this->iIconDepth == DEPTH_BACK) {
758
                $this->StrokeIcons();
759
            }
760
        }
761
762
        $this->scale->Stroke();
763
764
        if (!$_csim) {
765
            // Due to a minor off by 1 bug we need to temporarily adjust the margin
766
            --$this->img->right_margin;
767
            $this->StrokePlotBox();
768
            ++$this->img->right_margin;
769
        }
770
771
        // Stroke Grid line
772
        $this->hgrid->Stroke($this->img, $this->scale);
773
774
        $n = safe_count($this->iObj);
775
        for ($i = 0; $i < $n; ++$i) {
776
            //$this->iObj[$i]->SetLabelLeftMargin(round($maxwidth*$this->iLabelHMarginFactor/2));
777
            $this->iObj[$i]->Stroke($this->img, $this->scale);
778
        }
779
780
        $this->StrokeTitles();
781
782
        if (!$_csim) {
783
            $this->StrokeConstrains();
784
            $this->footer->Stroke($this->img);
785
786
            if ($this->iIconDepth == DEPTH_FRONT) {
787
                $this->StrokeIcons();
788
            }
789
790
            // Stroke all added user texts
791
            $this->StrokeTexts();
792
793
            // Should we do any final image transformation
794
            if ($this->iImgTrans) {
795
                $tform          = new Image\ImgTrans($this->img->img);
796
                $this->img->img = $tform->Skew3D(
797
                    $this->iImgTransHorizon,
798
                    $this->iImgTransSkewDist,
799
                    $this->iImgTransDirection,
800
                    $this->iImgTransHighQ,
801
                    $this->iImgTransMinSize,
802
                    $this->iImgTransFillColor,
803
                    $this->iImgTransBorder
804
                );
805
            }
806
807
            // If the filename is given as the special "__handle"
808
            // then the image handler is returned and the image is NOT
809
            // streamed back
810
            if ($aStrokeFileName == _IMG_HANDLER) {
811
                return $this->img->img;
812
            }
813
            // Finally stream the generated picture
814
            $this->cache->PutAndStream(
815
                $this->img,
816
                $this->cache_name,
817
                $this->inline,
818
                $aStrokeFileName
819
            );
820
        }
821
    }
822
823
    public function StrokeConstrains()
824
    {
825
        $n = safe_count($this->iObj);
826
827
        // Stroke all constrains
828
        for ($i = 0; $i < $n; ++$i) {
829
            // Some gantt objects may not have constraints associated with them
830
            // for example we can add Plot\IconPlots which doesn't have this property.
831
            if (empty($this->iObj[$i]->constraints)) {
832
                continue;
833
            }
834
835
            $numConstrains = safe_count($this->iObj[$i]->constraints);
836
837
            for ($k = 0; $k < $numConstrains; ++$k) {
838
                $vpos = $this->iObj[$i]->constraints[$k]->iConstrainRow;
839
                if ($vpos >= 0) {
840
                    $c1 = $this->iObj[$i]->iConstrainPos;
841
842
                    // Find out which object is on the target row
843
                    $targetobj = -1;
844
                    for ($j = 0; $j < $n && $targetobj == -1; ++$j) {
845
                        if ($this->iObj[$j]->iVPos == $vpos) {
846
                            $targetobj = $j;
847
                        }
848
                    }
849
                    if ($targetobj == -1) {
850
                        Util\JpGraphError::RaiseL(6008, $this->iObj[$i]->iVPos, $vpos);
851
                        //('You have specifed a constrain from row='.$this->iObj[$i]->iVPos.' to row='.$vpos.' which does not have any activity.');
852
                    }
853
                    $c2 = $this->iObj[$targetobj]->iConstrainPos;
854
                    if (safe_count($c1) == 4 && safe_count($c2) == 4) {
855
                        switch ($this->iObj[$i]->constraints[$k]->iConstrainType) {
856
                            case CONSTRAIN_ENDSTART:
857
                                if ($c1[1] < $c2[1]) {
858
                                    $link = new Image\GanttLink($c1[2], $c1[3], $c2[0], $c2[1]);
859
                                } else {
860
                                    $link = new Image\GanttLink($c1[2], $c1[1], $c2[0], $c2[3]);
861
                                }
862
                                $link->SetPath(3);
863
864
                                break;
865
                            case CONSTRAIN_STARTEND:
866
                                if ($c1[1] < $c2[1]) {
867
                                    $link = new Image\GanttLink($c1[0], $c1[3], $c2[2], $c2[1]);
868
                                } else {
869
                                    $link = new Image\GanttLink($c1[0], $c1[1], $c2[2], $c2[3]);
870
                                }
871
                                $link->SetPath(0);
872
873
                                break;
874
                            case CONSTRAIN_ENDEND:
875
                                if ($c1[1] < $c2[1]) {
876
                                    $link = new Image\GanttLink($c1[2], $c1[3], $c2[2], $c2[1]);
877
                                } else {
878
                                    $link = new Image\GanttLink($c1[2], $c1[1], $c2[2], $c2[3]);
879
                                }
880
                                $link->SetPath(1);
881
882
                                break;
883
                            case CONSTRAIN_STARTSTART:
884
                                if ($c1[1] < $c2[1]) {
885
                                    $link = new Image\GanttLink($c1[0], $c1[3], $c2[0], $c2[1]);
886
                                } else {
887
                                    $link = new Image\GanttLink($c1[0], $c1[1], $c2[0], $c2[3]);
888
                                }
889
                                $link->SetPath(3);
890
891
                                break;
892
                            default:
893
                                Util\JpGraphError::RaiseL(6009, $this->iObj[$i]->iVPos, $vpos);
894
                                //('Unknown constrain type specified from row='.$this->iObj[$i]->iVPos.' to row='.$vpos);
895
                                break;
896
                        }
897
898
                        $link->SetColor($this->iObj[$i]->constraints[$k]->iConstrainColor);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $link does not seem to be defined for all execution paths leading up to this point.
Loading history...
899
                        $link->SetArrow(
900
                            $this->iObj[$i]->constraints[$k]->iConstrainArrowSize,
901
                            $this->iObj[$i]->constraints[$k]->iConstrainArrowType
902
                        );
903
904
                        $link->Stroke($this->img);
905
                    }
906
                }
907
            }
908
        }
909
    }
910
911
    public function GetCSIMAreas()
912
    {
913
        if (!$this->iHasStroked) {
914
            $this->Stroke(_CSIM_SPECIALFILE);
915
        }
916
917
        $csim = $this->title->GetCSIMAreas();
918
        $csim .= $this->subtitle->GetCSIMAreas();
919
        $csim .= $this->subsubtitle->GetCSIMAreas();
920
921
        $n = safe_count($this->iObj);
922
        for ($i = $n - 1; $i >= 0; --$i) {
923
            $csim .= $this->iObj[$i]->GetCSIMArea();
924
        }
925
926
        return $csim;
927
    }
928
}
929