1 | <?php |
||
2 | |||
3 | /** |
||
4 | * JPGraph v4.0.3 |
||
5 | */ |
||
6 | |||
7 | namespace Amenadiel\JpGraph\Plot; |
||
8 | |||
9 | use Amenadiel\JpGraph\Graph; |
||
10 | use Amenadiel\JpGraph\Image; |
||
11 | use Amenadiel\JpGraph\Text; |
||
12 | use Amenadiel\JpGraph\Util; |
||
13 | |||
14 | /** |
||
15 | * @class GanttBar |
||
16 | * // Responsible for formatting individual gantt bars |
||
17 | */ |
||
18 | class GanttBar extends GanttPlotObject |
||
19 | { |
||
20 | public $progress; |
||
21 | public $leftMark; |
||
22 | public $rightMark; |
||
23 | private $iEnd; |
||
24 | private $iHeightFactor = 0.5; |
||
25 | private $iFillColor = 'white'; |
||
26 | private $iFrameColor = 'black'; |
||
27 | private $iShadow = false; |
||
28 | private $iShadowColor = 'darkgray'; |
||
29 | private $iShadowWidth = 1; |
||
30 | private $iShadowFrame = 'black'; |
||
0 ignored issues
–
show
introduced
by
![]() |
|||
31 | private $iPattern = GANTT_RDIAG; |
||
32 | private $iPatternColor = 'blue'; |
||
33 | private $iPatternDensity = 95; |
||
34 | private $iBreakStyle = false; |
||
35 | private $iBreakLineStyle = 'dotted'; |
||
36 | private $iBreakLineWeight = 1; |
||
37 | |||
38 | /** |
||
39 | * CONSTRUCTOR. |
||
40 | * |
||
41 | * @param mixed $aPos |
||
42 | * @param mixed $aLabel |
||
43 | * @param mixed $aStart |
||
44 | * @param mixed $aEnd |
||
45 | * @param mixed $aCaption |
||
46 | * @param mixed $aHeightFactor |
||
47 | */ |
||
48 | public function __construct($aPos, $aLabel, $aStart, $aEnd, $aCaption = '', $aHeightFactor = 0.6) |
||
49 | { |
||
50 | parent::__construct(); |
||
51 | $this->iStart = $aStart; |
||
52 | // Is the end date given as a date or as number of days added to start date? |
||
53 | if (is_string($aEnd)) { |
||
54 | // If end date has been specified without a time we will asssume |
||
55 | // end date is at the end of that date |
||
56 | if (strpos($aEnd, ':') === false) { |
||
57 | $this->iEnd = strtotime($aEnd) + SECPERDAY - 1; |
||
58 | } else { |
||
59 | $this->iEnd = $aEnd; |
||
60 | } |
||
61 | } elseif (is_int($aEnd) || is_float($aEnd)) { |
||
62 | $this->iEnd = strtotime($aStart) + round($aEnd * SECPERDAY); |
||
63 | } |
||
64 | $this->iVPos = $aPos; |
||
65 | $this->iHeightFactor = $aHeightFactor; |
||
66 | $this->title->Set($aLabel); |
||
67 | $this->caption = new Text\TextProperty($aCaption); |
||
68 | $this->caption->Align('left', 'center'); |
||
69 | $this->leftMark = new PlotMark(); |
||
70 | $this->leftMark->Hide(); |
||
71 | $this->rightMark = new PlotMark(); |
||
72 | $this->rightMark->Hide(); |
||
73 | $this->progress = new Image\Progress(); |
||
74 | } |
||
75 | |||
76 | /** |
||
77 | * PUBLIC METHODS. |
||
78 | * |
||
79 | * @param mixed $aShadow |
||
80 | * @param mixed $aColor |
||
81 | */ |
||
82 | public function SetShadow($aShadow = true, $aColor = 'gray') |
||
83 | { |
||
84 | $this->iShadow = $aShadow; |
||
85 | $this->iShadowColor = $aColor; |
||
86 | } |
||
87 | |||
88 | public function SetBreakStyle($aFlg = true, $aLineStyle = 'dotted', $aLineWeight = 1) |
||
89 | { |
||
90 | $this->iBreakStyle = $aFlg; |
||
91 | $this->iBreakLineStyle = $aLineStyle; |
||
92 | $this->iBreakLineWeight = $aLineWeight; |
||
93 | } |
||
94 | |||
95 | public function GetMaxDate() |
||
96 | { |
||
97 | return $this->iEnd; |
||
98 | } |
||
99 | |||
100 | public function SetHeight($aHeight) |
||
101 | { |
||
102 | $this->iHeightFactor = $aHeight; |
||
103 | } |
||
104 | |||
105 | public function SetColor($aColor) |
||
106 | { |
||
107 | $this->iFrameColor = $aColor; |
||
108 | } |
||
109 | |||
110 | public function SetFillColor($aColor) |
||
111 | { |
||
112 | $this->iFillColor = $aColor; |
||
113 | } |
||
114 | |||
115 | public function GetAbsHeight($aImg) |
||
116 | { |
||
117 | if (is_int($this->iHeightFactor) || $this->leftMark->show || $this->rightMark->show) { |
||
118 | $m = -1; |
||
119 | if (is_int($this->iHeightFactor)) { |
||
120 | $m = $this->iHeightFactor; |
||
121 | } |
||
122 | |||
123 | if ($this->leftMark->show) { |
||
124 | $m = max($m, $this->leftMark->width * 2); |
||
125 | } |
||
126 | |||
127 | if ($this->rightMark->show) { |
||
128 | $m = max($m, $this->rightMark->width * 2); |
||
129 | } |
||
130 | |||
131 | return $m; |
||
132 | } |
||
133 | |||
134 | return -1; |
||
135 | } |
||
136 | |||
137 | public function SetPattern($aPattern, $aColor = 'blue', $aDensity = 95) |
||
138 | { |
||
139 | $this->iPattern = $aPattern; |
||
140 | $this->iPatternColor = $aColor; |
||
141 | $this->iPatternDensity = $aDensity; |
||
142 | } |
||
143 | |||
144 | public function Stroke($aImg, $aScale) |
||
145 | { |
||
146 | $factory = new Graph\RectPatternFactory(); |
||
147 | $prect = $factory->Create($this->iPattern, $this->iPatternColor); |
||
148 | $prect->SetDensity($this->iPatternDensity); |
||
149 | |||
150 | // If height factor is specified as a float between 0,1 then we take it as meaning |
||
151 | // percetage of the scale width between horizontal line. |
||
152 | // If it is an integer > 1 we take it to mean the absolute height in pixels |
||
153 | if ($this->iHeightFactor > -0.0 && $this->iHeightFactor <= 1.1) { |
||
154 | $vs = $aScale->GetVertSpacing() * $this->iHeightFactor; |
||
155 | } elseif (is_int($this->iHeightFactor) && $this->iHeightFactor > 2 && $this->iHeightFactor < 200) { |
||
156 | $vs = $this->iHeightFactor; |
||
157 | } else { |
||
158 | Util\JpGraphError::RaiseL(6028, $this->iHeightFactor); |
||
159 | // ("Specified height (".$this->iHeightFactor.") for gantt bar is out of range."); |
||
160 | } |
||
161 | |||
162 | // Clip date to min max dates to show |
||
163 | $st = $aScale->NormalizeDate($this->iStart); |
||
164 | $en = $aScale->NormalizeDate($this->iEnd); |
||
165 | |||
166 | $limst = max($st, $aScale->iStartDate); |
||
167 | $limen = min($en, $aScale->iEndDate); |
||
168 | |||
169 | $xt = round($aScale->TranslateDate($limst)); |
||
170 | $xb = round($aScale->TranslateDate($limen)); |
||
171 | $yt = round($aScale->TranslateVertPos($this->iVPos) - $vs - ($aScale->GetVertSpacing() / 2 - $vs / 2)); |
||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||
172 | $yb = round($aScale->TranslateVertPos($this->iVPos) - ($aScale->GetVertSpacing() / 2 - $vs / 2)); |
||
173 | $middle = round($yt + ($yb - $yt) / 2); |
||
174 | $this->StrokeActInfo($aImg, $aScale, $middle); |
||
175 | |||
176 | // CSIM for title |
||
177 | if (!empty($this->title->csimtarget)) { |
||
178 | $colwidth = $this->title->GetColWidth($aImg); |
||
179 | $colstarts = []; |
||
180 | $aScale->actinfo->GetColStart($aImg, $colstarts, true); |
||
181 | $n = min(safe_count($colwidth), safe_count($this->title->csimtarget)); |
||
182 | for ($i = 0; $i < $n; ++$i) { |
||
183 | $title_xt = $colstarts[$i]; |
||
184 | $title_xb = $title_xt + $colwidth[$i]; |
||
185 | $coords = "${title_xt},${yt},${title_xb},${yt},${title_xb},${yb},${title_xt},${yb}"; |
||
186 | |||
187 | if (!empty($this->title->csimtarget[$i])) { |
||
188 | $this->csimarea .= "<area shape=\"poly\" coords=\"${coords}\" href=\"" . $this->title->csimtarget[$i] . '"'; |
||
189 | |||
190 | if (!empty($this->title->csimwintarget[$i])) { |
||
191 | $this->csimarea .= 'target="' . $this->title->csimwintarget[$i] . '" '; |
||
192 | } |
||
193 | |||
194 | if (!empty($this->title->csimalt[$i])) { |
||
195 | $tmp = $this->title->csimalt[$i]; |
||
196 | $this->csimarea .= " title=\"${tmp}\" alt=\"${tmp}\" "; |
||
197 | } |
||
198 | $this->csimarea .= " />\n"; |
||
199 | } |
||
200 | } |
||
201 | } |
||
202 | |||
203 | // Check if the bar is totally outside the current scale range |
||
204 | if ($en < $aScale->iStartDate || $st > $aScale->iEndDate) { |
||
205 | return; |
||
206 | } |
||
207 | |||
208 | // Remember the positions for the bar |
||
209 | $this->SetConstrainPos($xt, $yt, $xb, $yb); |
||
210 | |||
211 | $prect->ShowFrame(false); |
||
212 | $prect->SetBackground($this->iFillColor); |
||
213 | if ($this->iBreakStyle) { |
||
214 | $aImg->SetColor($this->iFrameColor); |
||
215 | $olds = $aImg->SetLineStyle($this->iBreakLineStyle); |
||
216 | $oldw = $aImg->SetLineWeight($this->iBreakLineWeight); |
||
217 | $aImg->StyleLine($xt, $yt, $xb, $yt); |
||
218 | $aImg->StyleLine($xt, $yb, $xb, $yb); |
||
219 | $aImg->SetLineStyle($olds); |
||
220 | $aImg->SetLineWeight($oldw); |
||
221 | } else { |
||
222 | if ($this->iShadow) { |
||
223 | $aImg->SetColor($this->iFrameColor); |
||
224 | $aImg->ShadowRectangle($xt, $yt, $xb, $yb, $this->iFillColor, $this->iShadowWidth, $this->iShadowColor); |
||
225 | $prect->SetPos(new Util\Rectangle($xt + 1, $yt + 1, $xb - $xt - $this->iShadowWidth - 2, $yb - $yt - $this->iShadowWidth - 2)); |
||
226 | $prect->Stroke($aImg); |
||
227 | } else { |
||
228 | $prect->SetPos(new Util\Rectangle($xt, $yt, $xb - $xt + 1, $yb - $yt + 1)); |
||
229 | $prect->Stroke($aImg); |
||
230 | $aImg->SetColor($this->iFrameColor); |
||
231 | $aImg->Rectangle($xt, $yt, $xb, $yb); |
||
232 | } |
||
233 | } |
||
234 | // CSIM for bar |
||
235 | if (!empty($this->csimtarget)) { |
||
236 | $coords = "${xt},${yt},${xb},${yt},${xb},${yb},${xt},${yb}"; |
||
237 | $this->csimarea .= "<area shape=\"poly\" coords=\"${coords}\" href=\"" . $this->csimtarget . '"'; |
||
238 | |||
239 | if (!empty($this->csimwintarget)) { |
||
240 | $this->csimarea .= ' target="' . $this->csimwintarget . '" '; |
||
241 | } |
||
242 | |||
243 | if ($this->csimalt != '') { |
||
244 | $tmp = $this->csimalt; |
||
245 | $this->csimarea .= " title=\"${tmp}\" alt=\"${tmp}\" "; |
||
246 | } |
||
247 | $this->csimarea .= " />\n"; |
||
248 | } |
||
249 | |||
250 | // Draw progress bar inside activity bar |
||
251 | if ($this->progress->iProgress > 0) { |
||
252 | $xtp = $aScale->TranslateDate($st); |
||
253 | $xbp = $aScale->TranslateDate($en); |
||
254 | $len = ($xbp - $xtp) * $this->progress->iProgress; |
||
255 | |||
256 | $endpos = $xtp + $len; |
||
257 | if ($endpos > $xt) { |
||
258 | // Take away the length of the progress that is not visible (before the start date) |
||
259 | $len -= ($xt - $xtp); |
||
260 | |||
261 | // Is the the progress bar visible after the start date? |
||
262 | if ($xtp < $xt) { |
||
263 | $xtp = $xt; |
||
264 | } |
||
265 | |||
266 | // Make sure that the progess bar doesn't extend over the end date |
||
267 | if ($xtp + $len - 1 > $xb) { |
||
268 | $len = $xb - $xtp; |
||
269 | } |
||
270 | |||
271 | $prog = $factory->Create($this->progress->iPattern, $this->progress->iColor); |
||
272 | $prog->SetDensity($this->progress->iDensity); |
||
273 | $prog->SetBackground($this->progress->iFillColor); |
||
274 | $barheight = ($yb - $yt + 1); |
||
275 | if ($this->iShadow) { |
||
276 | $barheight -= $this->iShadowWidth; |
||
277 | } |
||
278 | |||
279 | $progressheight = floor($barheight * $this->progress->iHeight); |
||
280 | $marg = ceil(($barheight - $progressheight) / 2); |
||
281 | $pos = new Util\Rectangle($xtp, $yt + $marg, $len, $barheight - 2 * $marg); |
||
282 | $prog->SetPos($pos); |
||
283 | $prog->Stroke($aImg); |
||
284 | } |
||
285 | } |
||
286 | |||
287 | // We don't plot the end mark if the bar has been capped |
||
288 | if ($limst == $st) { |
||
289 | $y = $middle; |
||
290 | // We treat the RIGHT and LEFT triangle mark a little bi |
||
291 | // special so that these marks are placed right under the |
||
292 | // bar. |
||
293 | if ($this->leftMark->GetType() == MARK_LEFTTRIANGLE) { |
||
294 | $y = $yb; |
||
295 | } |
||
296 | $this->leftMark->Stroke($aImg, $xt, $y); |
||
297 | } |
||
298 | if ($limen == $en) { |
||
299 | $y = $middle; |
||
300 | // We treat the RIGHT and LEFT triangle mark a little bi |
||
301 | // special so that these marks are placed right under the |
||
302 | // bar. |
||
303 | if ($this->rightMark->GetType() == MARK_RIGHTTRIANGLE) { |
||
304 | $y = $yb; |
||
305 | } |
||
306 | $this->rightMark->Stroke($aImg, $xb, $y); |
||
307 | |||
308 | $margin = $this->iCaptionMargin; |
||
309 | if ($this->rightMark->show) { |
||
310 | $margin += $this->rightMark->GetWidth(); |
||
311 | } |
||
312 | |||
313 | $this->caption->Stroke($aImg, $xb + $margin, $middle); |
||
314 | } |
||
315 | } |
||
316 | } |
||
317 |