|
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 |
|
|
|
|
|
|
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'); |
|
|
|
|
|
|
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; |
|
|
|
|
|
|
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); |
|
|
|
|
|
|
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
|
|
|
|