Test Setup Failed
Push — master ( c7183e...00c715 )
by Julito
30:43
created

geometry.lib.php ➔ poly_get_max()   C

Complexity

Conditions 7
Paths 25

Size

Total Lines 22
Code Lines 14

Duplication

Lines 16
Ratio 72.73 %

Importance

Changes 0
Metric Value
cc 7
eloc 14
nc 25
nop 2
dl 16
loc 22
rs 6.9811
c 0
b 0
f 0
1
<?php
2
/* For licensing terms, see /license.txt */
3
/**
4
 * @author Arnaud Ligot (CBlue SPRL) <[email protected]>
5
 * @package chamilo.include.geometry
6
 */
7
8
define('DEBUG', false);
9
/**
10
 * poly_init -    build the array which will store the image of the polygone
11
 * @param max[x]    X resolution
12
 * @param max[y]    Y resolution
13
 * @returns an array such as: for all i in [0..max[x][ : for all j in [0..max[y][ : array[i][j] = FALSE
14
 */
15
function poly_init($max)
16
{
17
    return array_fill(
18
        0,
19
        $max["x"] - 1,
20
        array_fill(0, $max["y"] - 1, false)
21
    );
22
}
23
24
25
/**
26
 * poly_compile - return an array which holds the image of the polygone
27
 *            FALSE = blank pixel
28
 *            TRUE = black pixel
29
 *
30
 * @param poly        points from the polygone
31
 *            example:
32
 *                poly[0]['x'] = ...
33
 *                poly[0]['y'] = ...
34
 *                poly[1]['x'] = ...
35
 *                poly[1]['y'] = ...
36
 *                ...
37
 *                poly[n]['x'] = <empty>
38
 *                poly[n]['y'] = <empty>
39
 *                poly[n+1]['x'] = <empty>
40
 *                poly[n+1]['y'] = <empty>
41
 *                ...
42
 * @param max        see poly_init
43
 * @param boolean    print or not a debug
44
 *
45
 * @returns an array such as: for all i in [0..max[x][ : for all j in [0..max[y][ : array[i][j] = in_poly(poly, i,j)
46
 *                in_poly(poly,i,j) = true iff (i,j) is inside the polygon defined by poly
47
 */
48
function poly_compile($poly, $max, $test = false)
49
{
50
    $res = poly_init($max);
51
52
    // looking for EDGES
53
        // may be optimized by a dynamic choice
54
        // between Y and X based on max[y]<max[x]
55
56
    /*
57
     * bords    cointains the edges of the polygone
58
     *        it is an array of array,
59
     *            there are an array for each collon of the image
60
     *
61
     *        for all j in [O..max[y][ : for all i in bords[$j] :
62
     *            (i,j) is a point inside an edge of the polygone
63
     */
64
    $bord_lenght = $max['x'];
65
    if ($max['y'] > $bord_lenght) {
66
        $bord_lenght = $max['y'];
67
    }
68
69
    //$bords = array_fill(0, $bord_lenght-1, array()); // building this array
70
    $bords = array_fill(0, $bord_lenght, array()); // building this array
71
72
    /* adding the first point of the polygone */
73
    if (is_array($bords[$poly[0]['y']])) //avoid warning
74
        array_push($bords[$poly[0]['y']], $poly[0]['x']);
75
76
    $i = 1; // we re-use $i and $old_pente bellow the loop
77
    $old_pente = 0;
78
    for (;    // for each points of the polygon but the first
79
        $i < sizeof($poly) && (!empty($poly[$i]['x']) && !empty($poly[$i]['y'])); $i++) {
80
81
        /* special cases */
82
        if ($poly[$i - 1]['y'] == $poly[$i]['y']) {
83
            if ($poly[$i - 1]['x'] == $poly[$i]['x'])
84
                continue; // twice the same point
85 View Code Duplication
            else {    //  infinite elevation of the edge
86
                if (is_array($bords[$poly[$i]['y']]))
87
                    array_push($bords[$poly[$i]['y']], $poly[$i]['x']);
88
                $old_pente = 0;
89
                continue;
90
            }
91
        }
92
93
        //echo 'point:'.$poly[$i]['y']; bug here
94
        // adding the point as a part of an edge
95 View Code Duplication
        if (is_array($bords[$poly[$i]['y']])) //avoid warning
96
        array_push($bords[$poly[$i]['y']], $poly[$i]['x']);
97 View Code Duplication
        if (DEBUG) echo '('.$poly[$i]['x'].';'.$poly[$i]['y'].')   ';
98
99
        /* computing the elevation of the edge going */
100
        //        from $poly[$i-1] to $poly[$i]
101
        $pente = ($poly[$i - 1]['x'] - $poly[$i]['x']) /
102
                 ($poly[$i - 1]['y'] - $poly[$i]['y']);
103
104
        // if the sign of the elevation change from the one of the
105
        // previous edge, the point must be added a second time inside
106
        // $bords
107
        if ($i > 1)
108
            if (($old_pente < 0 && $pente > 0)
109
                    || ($old_pente > 0 && $pente < 0)) {
110 View Code Duplication
                if (is_array($bords[$poly[$i]['y']])) //avoid warning
111
                    array_push($bords[$poly[$i]['y']], $poly[$i]['x']);
112
113 View Code Duplication
                if (DEBUG)
114
                    echo '*('.$poly[$i]['x'].
115
                        ';'.$poly[$i]['y'].')   ';
116
            }
117
118
        /* detect the direction of the elevation in Y */
119
        $dy_inc = ($poly[$i]['y'] - $poly[$i - 1]['y']) > 0 ? 1 : -1;
120
        $x = $poly[$i - 1]['x'];
121
//        if (DEBUG) echo "init: ".$poly[$i-1]['y']."  dy_inc: ".$dy_inc.
122
//            "   end: ".$poly[$i]['y']."   pente:".$pente;
123
124
125
        /* computing points between $poly[$i-1]['y'] and $poly[$i-1]['y'] */
126
127
        // we iterate w/ $dy in ]$poly[$i-1]['y'],$poly[$i-1]['y'][
128
        //    w/ $dy_inc as increment
129 View Code Duplication
        for ($dy = $poly[$i - 1]['y'] + $dy_inc;
130
            $dy != $poly[$i]['y'];
131
            $dy += $dy_inc) {
132
            $x += $pente * $dy_inc;
133
            array_push($bords[$dy], $x);
134
//            if (DEBUG) echo '/('.$x.';'.$dy.')   ';
135
        }
136
        $old_pente = $pente;
137
    }
138
139
    // closing the polygone (the edge between $poly[$i-1] and $poly[0])
140
    if ($poly[$i - 1]['y'] != $poly[0]['y']) {// droite--> rien à faire
141
142
        // elevation between $poly[0]['x'] and $poly[1]['x'])
143
        $rest = $poly[0]['y'] - $poly[1]['y'];
144
        if ($rest != 0)
145
            $pente1 = ($poly[0]['x'] - $poly[1]['x']) / ($rest);
146
        else
147
            $pente1 = 0;
148
149
        // elevation between $poly[$i-1]['x'] and $poly[0]['x'])
150
        $pente = ($poly[$i - 1]['x'] - $poly[0]['x']) /
151
            ($poly[$i - 1]['y'] - $poly[0]['y']);
152
153
//        if (DEBUG) echo 'start('.$poly[$i-1]['x'].','.$poly[$i-1]['y'].
154
//                ')-end('.$poly[0]['x'].','.$poly[0]['y'].
155
//                ')-pente'.$pente;
156
157
        // doubling the first point if needed (see above)
158 View Code Duplication
        if (($pente1 < 0 && $pente > 0) || ($pente1 > 0 && $pente < 0)) {
159
            if (is_array($bords[$poly[$i - 1]['y']]))
160
                array_push($bords[$poly[$i - 1]['y']], round($poly[$i - 1]['x']));
161
            //if (DEBUG) echo '('.$poly[$i-1]['x'].';'.$poly[$i-1]['y'].')   ';
162
        }
163
        //  doubling the last point if neededd
164 View Code Duplication
        if (($old_pente < 0 && $pente > 0) || ($old_pente > 0 && $pente < 0)) {
165
            if (is_array($bords[$poly[$i - 1]['y']])) //avoid warning
166
                array_push($bords[$poly[$i - 1]['y']], round($poly[$i - 1]['x']));
167
            //if (DEBUG) echo '*('.$poly[$i-1]['x'].';'.$poly[$i-1]['y'].')   ';
168
        }
169
170
171
        $dy_inc = ($poly[0]['y'] - $poly[$i - 1]['y']) > 0 ? 1 : -1;
172
        $x = $poly[$i - 1]['x'];
173
//        if (DEBUG) echo "init: ".$poly[$i-1]['y']."  dy_inc: ".$dy_inc.
174
//            "   end: ".$poly[0]['y'];
175
176 View Code Duplication
        for ($dy = $poly[$i - 1]['y'] + $dy_inc;
177
            $dy != $poly[0]['y'];
178
            $dy += $dy_inc)
179
        {
180
            $x += $pente * $dy_inc;
181
            array_push($bords[$dy], round($x));
182
//            if (DEBUG) echo '/('.$x.';'.$dy.')   ';
183
        }
184
    }
185
186
    /* filling the polygon */
187
    /* basic idea: we sort a column of edges.
188
        For each pair of point, we color the points in between */
189
    $n = count($bords);
190
    for ($i = 0; $i < $n; $i++) {  // Y
191
        //error_log(__FILE__.' - Border Num '.$i,0);
192
        if (is_array($bords[$i])) {
193
            sort($bords[$i]);
194
        }
195
196
        for ($j = 0; $j < sizeof($bords[$i]); $j += 2) { // bords
197
            if (!isset($bords[$i][$j + 1])) {
198
                continue;
199
            }
200
201
            for ($k = round($bords[$i][$j]); $k <= $bords[$i][$j + 1]; $k++) {
202
                $res[$k][$i] = true; //filling the array with trues
203
                if ($test == 1) {
204
                    /*how to draw the polygon in a human way:
205
                    In ubuntu : sudo apt-get install gnuplot
206
                    Create an empty file with all points with the result of this echos (No commas, no point, no headers)
207
                    In gnuplot:
208
                    For 1 polygon:  plot "/home/jmontoya/test"
209
                    For 2 polygons:  plot "/home/jmontoya/test", "/home/jmontoya/test2"
210
                    A new window will appear with the plot
211
                    */
212
                    echo $k.'  '.$i; echo '<br />';
213
                }
214
            }
215
        }
216
    }
217
218
    return $res;
219
}
220
221
/**
222
 * poly_dump - dump an image on the screen
223
 *
224
 * @param array       the polygone as output by poly_compile()
225
 * @param array       see above (poly_init)
226
 * @param string      Format ('raw' text or 'html')
227
 *
228
 * @return string     html code of the representation of the polygone image
229
 */
230
function poly_dump(&$poly, $max, $format = 'raw')
231
{
232
    if ($format == 'html') {
233
        $s = "<div style='font-size: 8px; line-height:3px'><pre>\n";
234
    }
235
    for ($i = 0; $i < $max['y']; $i++) {
236
        for ($j = 0; $j < $max['x']; $j++)
237
            if ($poly[$j][$i] == TRUE)
238
                $s .= ($format == 'html' ? "<b>1</b>" : '1');
239
            else
240
                $s .= "0";
241
        $s .= ($format == 'html' ? "<br />\n" : "\n");
242
    }
243
    $s .= ($format == 'html' ? "</pre></div>\n" : "\n");
244
    return $s;
245
}
246
247
/**
248
 * poly_result    -    compute statis for two polygones
249
 *
250
 * @param poly1        first polygone as returned by poly_compile
251
 * @param poly2        second ....
252
 * @param max        resolution as specified for poly_init
253
 *
254
 * @returns (see below, UTSL)
255
 */
256
function poly_result(&$poly1, &$poly2, $max)
257
{
258
    $onlyIn1 = 0;
259
    $surfaceOf1 = 0;
260
    $surfaceOf2 = 0;
261
262
    for ($i = 0; $i < $max['x']; $i++)
263
        for ($j = 0; $j < $max['y']; $j++) {
264
            if (isset($poly1[$i][$j]) && ($poly1[$i][$j] == true)) {
265
                $surfaceOf1++;
266 View Code Duplication
                if (isset($poly2[$i][$j]) && ($poly2[$i][$j] == false))
267
                    $onlyIn1++;
268
            }
269 View Code Duplication
            if (isset($poly2[$i][$j]) && ($poly2[$i][$j] == true))
270
                $surfaceOf2++;
271
        }
272
273
    return array(
274
        "s1" => $surfaceOf1,
275
        "s2" => $surfaceOf2,
276
        "both" => $surfaceOf1 - $onlyIn1,
277
        "s1Only" => $onlyIn1,
278
        "s2Only" => $surfaceOf2 - ($surfaceOf1 - $onlyIn1));
279
}
280
281
/**
282
 * poly_touch    -    compute statis for two polygones
283
 *
284
 * @param poly1        first polygone as returned by poly_compile
285
 * @param poly2        second ....
286
 * @param max        resolution as specified for poly_init
287
 *
288
 * @returns (see below, UTSL)
289
 */
290
function poly_touch(&$poly1, &$poly2, $max)
291
{
292
    for ($i = 0; $i < $max['x']; $i++) {
293
        for ($j = 0; $j < $max['y']; $j++) {
294
            if (isset($poly1[$i][$j]) && ($poly1[$i][$j] == true)
295
                && isset($poly2[$i][$j]) && ($poly2[$i][$j] == true)) {
296
                    return true;
297
            }
298
        }
299
    }
300
    return false;
301
}
302
303
/**
304
 * Convert a list of points in x1;y1|x2;y2|x3;y3 or x1;y1/x2;y2 format to
305
 * the format in which the functions in this library are expecting their data
306
 * @param   string  List of points in x1;y1|... format (or /)
307
 * @param   string  The points separator for the list (| or /)
308
 * @return  array   An array of points in the right format to use with the
309
 *                  local functions
310
 */
311
function convert_coordinates($coords, $sep = '|')
312
{
313
    $points = array();
314
    $pairs = explode($sep, $coords);
315
    foreach ($pairs as $idx => $pcoord) {
316
        list($x, $y) = explode(';', $pcoord);
317
        $points[] = array('x'=>$x, 'y'=>$y);
318
    }
319
    return $points;
320
}
321
322
/**
323
 * Returns the maximum coordinates in x,y (from 0,0) that the geometrical form
324
 * can reach
325
 * @param   array   Coordinates of one polygon
326
 * @return  array   ('x'=>val,'y'=>val)
327
 */
328
function poly_get_max(&$coords1, &$coords2)
329
{
330
    $mx = 0;
331
    $my = 0;
332 View Code Duplication
    foreach ($coords1 as $coord) {
333
        if ($coord['x'] > $mx) {
334
            $mx = $coord['x'];
335
        }
336
        if ($coord['y'] > $my) {
337
            $my = $coord['y'];
338
        }
339
    }
340 View Code Duplication
    foreach ($coords2 as $coord) {
341
        if ($coord['x'] > $mx) {
342
            $mx = $coord['x'];
343
        }
344
        if ($coord['y'] > $my) {
345
            $my = $coord['y'];
346
        }
347
    }
348
    return array('x'=>$mx, 'y'=>$my);
349
}
350
351
/**
352
 * Class Geometry
353
 * Utils for decode hotspots and check if the user choices are correct
354
 */
355
class Geometry
356
{
357
    /**
358
     * Decode a user choice as a point
359
     * @param string $coordinates
360
     * @return array The x and y properties for a point
361
     */
362
    public static function decodePoint($coordinates)
363
    {
364
        $coordinates = explode(';', $coordinates);
365
366
        return [
367
            'x' => intval($coordinates[0]),
368
            'y' => intval($coordinates[1])
369
        ];
370
    }
371
372
    /**
373
     * Decode a square info as properties
374
     * @param string $coordinates
375
     * @return array The x, y, width, and height properties for a square
376
     */
377
    public static function decodeSquare($coordinates)
378
    {
379
        $coordinates = explode('|', $coordinates);
380
        $originString = explode(';', $coordinates[0]);
381
382
        return [
383
            'x' => intval($originString[0]),
384
            'y' => intval($originString[1]),
385
            'width' => intval($coordinates[1]),
386
            'height' => intval($coordinates[2])
387
        ];
388
    }
389
390
    /**
391
     * Decode an ellipse info as properties
392
     * @param string $coordinates
393
     * @return array The center_x, center_y, radius_x, radius_x properties for an ellipse
394
     */
395
    public static function decodeEllipse($coordinates)
396
    {
397
        $coordinates = explode('|', $coordinates);
398
        $originString = explode(';', $coordinates[0]);
399
400
        return [
401
            'center_x' => intval($originString[0]),
402
            'center_y' => intval($originString[1]),
403
            'radius_x' => intval($coordinates[1]),
404
            'radius_y' => intval($coordinates[2])
405
        ];
406
    }
407
408
    /**
409
     * Decode a polygon info as properties
410
     * @param string $coordinates
411
     * @return array The array of points for a polygon
412
     */
413
    public static function decodePolygon($coordinates)
414
    {
415
        $coordinates = explode('|', $coordinates);
416
417
        $points = [];
418
419
        foreach ($coordinates as $coordinate) {
420
            $point = explode(';', $coordinate);
421
422
            $points[] = [
423
                intval($point[0]),
424
                intval($point[1])
425
            ];
426
        }
427
428
        return $points;
429
    }
430
431
    /**
432
     * Check if the point is inside of a square
433
     * @param array $properties The hotspot properties
434
     * @param array $point The point properties
435
     * @return boolean
436
     */
437
    public static function pointIsInSquare($properties, $point)
438
    {
439
        $left = $properties['x'];
440
        $right = $properties['x'] + $properties['width'];
441
        $top = $properties['y'];
442
        $bottom = $properties['y'] + $properties['height'];
443
444
        $xIsValid = $point['x'] >= $left && $point['x'] <= $right;
445
        $yIsValid = $point['y'] >= $top && $point['y'] <= $bottom;
446
447
        return $xIsValid && $yIsValid;
448
    }
449
450
    /**
451
     * Check if the point is inside of an ellipse
452
     * @param array $properties The hotspot properties
453
     * @param array $point The point properties
454
     * @return boolean
455
     */
456
    public static function pointIsInEllipse($properties, $point)
457
    {
458
        $dX = $point['x'] - $properties['center_x'];
459
        $dY = $point['y'] - $properties['center_y'];
460
461
        $dividend = pow($dX, 2) / pow($properties['radius_x'], 2);
462
        $divider = pow($dY, 2) / pow($properties['radius_y'], 2);
463
464
        return $dividend + $divider <= 1;
465
    }
466
467
    /**
468
     * Check if the point is inside of a polygon
469
     * @param array $properties The hotspot properties
470
     * @param array $point The point properties
471
     * @return boolean
472
     */
473
    public static function pointIsInPolygon($properties, $point)
474
    {
475
        $points = $properties;
476
        $isInside = false;
477
478
        for ($i = 0, $j = count($points) - 1; $i < count($points); $j = $i++) {
479
            $xi = $points[$i][0];
480
            $yi = $points[$i][1];
481
            $xj = $points[$j][0];
482
            $yj = $points[$j][1];
483
484
            $intersect = (($yi > $point['y']) !== ($yj > $point['y'])) &&
485
                ($point['x'] < ($xj - $xi) * ($point['y'] - $yi) / ($yj - $yi) + $xi);
486
487
            if ($intersect) {
488
                $isInside = !$isInside;
489
            }
490
        }
491
492
        return $isInside;
493
    }
494
}
495