Visual Servoing Platform  version 3.6.1 under development (2024-11-15)
vpClipping.cpp
1 /****************************************************************************
2  *
3  * ViSP, open source Visual Servoing Platform software.
4  * Copyright (C) 2005 - 2023 by Inria. All rights reserved.
5  *
6  * This software is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  * See the file LICENSE.txt at the root directory of this source
11  * distribution for additional information about the GNU GPL.
12  *
13  * For using ViSP with software that can not be combined with the GNU
14  * GPL, please contact Inria about acquiring a ViSP Professional
15  * Edition License.
16  *
17  * See https://visp.inria.fr for more information.
18  *
19  * This software was developed at:
20  * Inria Rennes - Bretagne Atlantique
21  * Campus Universitaire de Beaulieu
22  * 35042 Rennes Cedex
23  * France
24  *
25  * If you have questions regarding the use of this file, please contact
26  * Inria at visp@inria.fr
27  *
28  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
29  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
30  *
31  * Description:
32  * Le module "clipping.c" contient les procedures de decoupage
33  * d'une scene 3D par l'algorithme de Sutherland et Hodgman.
34  * Pour plus de reseignements, voir :
35  * I. Sutherland, E. Hodgman, W. Gary.
36  * "Reentrant Polygon Clipping".
37  * Communications of the ACM,
38  * Junary 1974, Volume 17, Number 1, pp 32-44.
39  *
40  * Authors:
41  * Jean-Luc CORRE
42  *
43 *****************************************************************************/
44 
45 #include <visp3/core/vpConfig.h>
46 #include <visp3/core/vpException.h>
47 
48 #ifndef DOXYGEN_SHOULD_SKIP_THIS
49 #include "vpClipping.h"
50 #include "vpView.h"
51 
52 #include <cmath>
53 #include <limits>
54 #include <stdio.h>
55 #include <stdlib.h>
56 
57 BEGIN_VISP_NAMESPACE
58 static void inter(Byte mask, Index v0, Index v1);
59 static void point_4D_3D(Point4f *p4, int size, Byte *cp, Point3f *p3);
60 
61 /*
62  * Variables utilisees par le decoupage :
63  *
64  * CLIP :
65  * Surface resultat apres le decoupage.
66  * La surface est adaptee pour la reception de tous les types de surfaces.
67  * Sa taille est definie par les macros "..._NBR" de "bound.h".
68  *
69  * FACE_NBR : son nombre de faces
70  * POINT_NBR : son nombre de points
71  * VECTOR_NBR : son monbre de vecteurs
72  * VERTEX_NBR : son nombre de sommets par face.
73  *
74  * La surface recoit une a une les faces decoupees.
75  * La surface recoit en une fois tous les points 3D de la surface decoupee
76  * par rapport aux 6 plans de la pyramide tronquee de vision.
77  *
78  * CODE :
79  * Tableau de booleens durant le decoupage.
80  * Le tableau est initialise par les booleens indiquant le positionnement des
81  * points 4D par rapport aux 6 plans de decoupage.
82  * Le tableau recoit ensuite un a un les booleans des points 4D d'intersection
83  * de la surface avec les 6 plans de decoupage.
84  *
85  * POINT4F :
86  * Tableau de points durant le decoupage.
87  * Le tableau est initialise par les points de la surface en entree apres
88  * transforation en coordonnees homogenes 4D.
89  * Le tableau recoit ensuite un a un les points 4D d'intersection de la
90  * surface avec les 6 plans de la pyramide tronquee de vision.
91  */
92 static Bound clip; /* surface a decouper */
93 static Byte *code; /* tableau de bits */
94 static Point4f *point4f; /* tableau de points 4D */
95 static Index point4f_nbr; /* nombre de points 4D */
96 
97 #if clip_opt
98 static Index *poly0, *poly1; /* polygones temporaires*/
99 #else
100 static Index *poly_tmp; /* polygone temporaire */
101 #endif /* clip_opt */
102 
103 /*
104  * La procedure "open_clipping" alloue et initialise les variables utilisees
105  * par le mode "clipping".
106  */
107 void open_clipping(void)
108 {
109  /* alloue la surface de travail */
110  malloc_huge_Bound(&clip);
111 
112  /* alloue les tableaux */
113  if ((code = (Byte *)malloc(POINT_NBR * sizeof(Byte))) == NULL ||
114  (point4f = (Point4f *)malloc(POINT_NBR * sizeof(Point4f))) == NULL
115 #ifdef clip_opt
116  || (poly0 = (Index *)malloc(VERTEX_NBR * sizeof(Index))) == NULL ||
117  (poly1 = (Index *)malloc(VERTEX_NBR * sizeof(Index))) == NULL) {
118  static char proc_name[] = "open_clipping";
119  perror(proc_name);
120  throw vpException(vpException::fatalError, "Error in open_clipping");
121  }
122 #else
123  || (poly_tmp = (Index *)malloc(VERTEX_NBR * sizeof(Index))) == NULL) {
124  static char proc_name[] = "open_clipping";
125  perror(proc_name);
126  throw vpException(vpException::fatalError, "Error in open_clipping");
127  }
128 #endif /* clip_opt */
129 }
130 
131 /*
132  * La procedure "close_clipping" libere les variables utilisees par
133  * le mode "clipping".
134  */
135 void close_clipping(void)
136 {
137  free_huge_Bound(&clip);
138  free((char *)code);
139  free((char *)point4f);
140 #ifdef clip_opt
141  free((char *)poly0);
142  free((char *)poly1);
143 #else
144  free((char *)poly_tmp);
145 #endif /* clip_opt */
146 }
147 
148 /*
149  * La procedure "clipping" decoupe un polygone par rapport a un plan
150  * suivant l'algorithme de Sutherland et Hodgman.
151  * Entree :
152  * mask Masque du plan de decoupage pour le code.
153  * vni Nombre de sommets du polygone en entree.
154  * pi Polygone en entree.
155  * po Polygone resultat du decoupage.
156  * Sortie :
157  * Nombre de sommets du polygone resultat "po".
158  */
159 static Index clipping(Byte mask, Index vni, Index *pi, Index *po)
160 {
161  /*
162  * vno Nombre de sommets du polygone "po".
163  * vs Premier sommet de l'arete a decouper.
164  * vp Second sommet de l'arete a decouper.
165  * ins TRUE si le sommet "vs" est interieur, FALSE sinon.
166  * inp TRUE si le sommet "vp" est interieur, FALSE sinon.
167  */
168  Index vno = vni; /* nombre de sommets */
169  Index vs = pi[vni - 1]; /* premier sommet */
170  Byte ins = code[vs] & mask; /* code de "vs" */
171 
172  while (vni--) { /* pour tous les sommets */
173  Index vp = *pi++; /* second sommet */
174  Byte inp = code[vp] & mask; /* code du plan courant */
175 
176  if (ins == IS_INSIDE) {
177  if (inp == IS_INSIDE) { /* arete interieure */
178  *po++ = vp;
179  }
180  else { /* intersection */
181  inter(mask, vs, vp);
182  *po++ = point4f_nbr++;
183  }
184  }
185  else {
186  if (inp == IS_INSIDE) { /* intersection */
187  inter(mask, vs, vp);
188  *po++ = point4f_nbr++;
189  *po++ = vp;
190  vno++;
191  }
192  else { /* arete exterieure */
193  vno--;
194  }
195  }
196  vs = vp;
197  ins = inp;
198  }
199  return (vno);
200 }
201 
202 /*
203  * La procedure "clipping_Face" decoupe une face par rapport aux 6 plans
204  * de decoupage de la pyramide tronquee de vision.
205  * Entree :
206  * fi Face a decouper.
207  * fo Face resultat du decoupage.
208  * Sortie :
209  * Le nombre de sommets de la face resultat.
210  */
211 static Index clipping_Face(Face *fi, Face *fo)
212 {
213  Index *flip = poly_tmp; /* polygone temporaire */
214  Index *flop = fo->vertex.ptr; /* polygone resultat */
215  Index vn = fi->vertex.nbr; /* nombre de sommets */
216 
217  if ((vn = clipping(IS_ABOVE, vn, fi->vertex.ptr, flip)) != 0)
218  if ((vn = clipping(IS_BELOW, vn, flip, flop)) != 0)
219  if ((vn = clipping(IS_RIGHT, vn, flop, flip)) != 0)
220  if ((vn = clipping(IS_LEFT, vn, flip, flop)) != 0)
221  if ((vn = clipping(IS_BACK, vn, flop, flip)) != 0)
222  if ((vn = clipping(IS_FRONT, vn, flip, flop)) != 0) {
223  /* recopie de "fi" dans "fo" */
224  /* fo->vertex.ptr == flop */
225  fo->vertex.nbr = vn;
226  fo->is_polygonal = fi->is_polygonal;
227  fo->is_visible = fi->is_visible;
228 #ifdef face_normal
229  fo->normal = fi->normal;
230 #endif /* face_normal */
231  return (vn);
232  }
233  return (0);
234 }
235 
236 /*
237  * La procedure "clipping_Bound" decoupe une surface par rapport aux 6 plans
238  * de decoupage de la pyramide tronquee de vision.
239  * Les calculs geometriques sont effectues en coordonnees homogenes.
240  * Note : Les points invisibles de la surface "clip" ont une profondeur
241  *negative c'est a dire une coordonnee Z negative. Entree :
242  * bp Surface a decouper.
243  * m Matrice de projection dans le volume canonique.
244  * Sortie :
245  * Pointeur de la surface resultat "clip" si elle est visible,
246  * NULL sinon.
247  */
248 Bound *clipping_Bound(Bound *bp, Matrix m)
249 {
250  Face *fi = bp->face.ptr; /* 1ere face */
251  Face *fend = fi + bp->face.nbr; /* borne de "fi"*/
252  Face *fo = clip.face.ptr; /* face clippee */
253 
254  /* recopie de "bp" dans les tableaux intermediaires */
255 
256  point4f_nbr = bp->point.nbr;
257  point_3D_4D(bp->point.ptr, (int)point4f_nbr, m, point4f);
258  set_Point4f_code(point4f, (int)point4f_nbr, code);
259 #ifdef face_normal
260  if (!(clip.is_polygonal = bp->is_polygonal))
261  // bcopy (bp->normal.ptr, clip.normal.ptr,
262  // bp->normal.nbr * sizeof (Vector));
263  memmove(clip.normal.ptr, bp->normal.ptr, bp->normal.nbr * sizeof(Vector));
264 #endif /* face_normal */
265  for (; fi < fend; fi++) { /* pour toutes les faces*/
266  if (clipping_Face(fi, fo) != 0) {
267  fo++; /* ajoute la face a "clip" */
268  /*
269  * Construction a la volee du future polygone.
270  * dont l'espace memoire est deja alloue (voir
271  * la procedure "malloc_huge_Bound").
272  */
273  fo->vertex.ptr = (fo - 1)->vertex.ptr + (fo - 1)->vertex.nbr;
274  }
275  }
276 
277  if (fo == clip.face.ptr)
278  return (NULL); /* Rien a voir, circulez... */
279 
280  /* recopie des tableaux intermediaires dans "clip" */
281 
282  point_4D_3D(point4f, (int)point4f_nbr, code, clip.point.ptr);
283  clip.type = bp->type;
284  clip.face.nbr = (Index)(fo - clip.face.ptr);
285  clip.point.nbr = point4f_nbr;
286 #ifdef face_normal
287  if (!bp->is_polygonal)
288  clip.normal.nbr = point4f_nbr;
289 #endif /* face_normal */
290  return (&clip);
291 }
292 
293 /*
294  * La procedure "inter" calcule le point d'intersection "point4f[point4f_nbr]"
295  * de l'arete (v0,v1) avec le plan "mask".
296  * Entree :
297  * mask Mask du plan de decoupage.
298  * v0 Permier sommet de l'arete.
299  * v1 Second sommet de l'arete.
300  */
301 static void inter(Byte mask, Index v0, Index v1)
302 {
303  Point4f *p = point4f + point4f_nbr;
304  Point4f *p0 = point4f + v0;
305  Point4f *p1 = point4f + v1;
306  float t; /* parametre entre 0 et 1 */
307 
308  /* calcule le point d'intersection */
309 
310  switch (mask) {
311 
312  case IS_ABOVE:
313  /* t = (p0->w - p0->y) / ((p0->w - p0->y) - (p1->w - p1->y)); */
314  t = (p0->w - p0->y) - (p1->w - p1->y);
315  // t = (t == 0) ? (float)1.0 : (p0->w - p0->y) / t;
316  t = (std::fabs(t) <= std::numeric_limits<double>::epsilon()) ? (float)1.0 : (p0->w - p0->y) / t;
317  PAR_COORD3(*p, t, *p0, *p1);
318  p->w = p->y; /* propriete du point d'intersection */
319  break;
320 
321  case IS_BELOW:
322  /* t = (p0->w + p0->y) / ((p0->w + p0->y) - (p1->w + p1->y)); */
323  t = (p0->w + p0->y) - (p1->w + p1->y);
324  // t = (t == 0) ? (float)1.0 : (p0->w + p0->y) / t;
325  t = (std::fabs(t) <= std::numeric_limits<double>::epsilon()) ? (float)1.0 : (p0->w + p0->y) / t;
326  PAR_COORD3(*p, t, *p0, *p1);
327  p->w = -p->y; /* propriete du point d'intersection */
328  break;
329 
330  case IS_RIGHT:
331  /* t = (p0->w - p0->x) / ((p0->w - p0->x) - (p1->w - p1->x)); */
332  t = (p0->w - p0->x) - (p1->w - p1->x);
333  // t = (t == 0) ? (float)1.0 : (p0->w - p0->x) / t;
334  t = (std::fabs(t) <= std::numeric_limits<double>::epsilon()) ? (float)1.0 : (p0->w - p0->x) / t;
335  PAR_COORD3(*p, t, *p0, *p1);
336  p->w = p->x; /* propriete du point d'intersection */
337  break;
338 
339  case IS_LEFT:
340  /* t = (p0->w + p0->x) / ((p0->w + p0->x) - (p1->w + p1->x)); */
341  t = (p0->w + p0->x) - (p1->w + p1->x);
342  // t = (t == 0) ? (float)1.0 : (p0->w + p0->x) / t;
343  t = (std::fabs(t) <= std::numeric_limits<double>::epsilon()) ? (float)1.0 : (p0->w + p0->x) / t;
344  PAR_COORD3(*p, t, *p0, *p1);
345  p->w = -p->x; /* propriete du point d'intersection */
346  break;
347 
348  case IS_BACK:
349  /* t = (p0->w - p0->z) / ((p0->w - p0->z) - (p1->w - p1->z)); */
350  t = (p0->w - p0->z) - (p1->w - p1->z);
351  // t = (t == 0) ? (float)1.0 : (p0->w - p0->z) / t;
352  t = (std::fabs(t) <= std::numeric_limits<double>::epsilon()) ? (float)1.0 : (p0->w - p0->z) / t;
353  PAR_COORD3(*p, t, *p0, *p1);
354  p->w = p->z; /* propriete du point d'intersection */
355  break;
356 
357  case IS_FRONT:
358  /* t = p0->z / (p0->z - p1->z); */
359  t = (p0->z - p1->z);
360  // t = (t == 0) ? (float)1.0 : p0->z / t;
361  t = (std::fabs(t) <= std::numeric_limits<double>::epsilon()) ? (float)1.0 : p0->z / t;
362  p->x = (p1->x - p0->x) * t + p0->x;
363  p->y = (p1->y - p0->y) * t + p0->y;
364  p->w = (p1->w - p0->w) * t + p0->w;
365  p->z = (float)M_EPSILON; /* propriete du point d'intersection */
366  break;
367  }
368  /* resout les problemes d'arrondis pour "where_is_Point4f" */
369  /* p->w += (p->w < 0) ? (- M_EPSILON) : M_EPSILON; */
370  p->w += (float)M_EPSILON;
371  code[point4f_nbr] = where_is_Point4f(p); /* localise "p" */
372 #ifdef face_normal
373  if (!clip.is_polygonal) {
374  Vector *n0 = clip.normal.ptr + v0;
375  Vector *n1 = clip.normal.ptr + v1;
376  Vector *n = clip.normal.ptr + point4f_nbr;
377 
378  SET_COORD3(*n, (n1->x - n0->x) * t + n0->x, (n1->y - n0->y) * t + n0->y, (n1->z - n0->z) * t + n0->z);
379  }
380 #endif /* face_normal */
381 }
382 
383 /*
384  * La procedure "point_4D_3D" transforme les points homogenes 4D visibles
385  * en points 3D par projection.
386  * Note : On marque un point 3D invisible par une profondeur negative.
387  * Entree :
388  * p4 Tableau de points 4D a transformer.
389  * size Taille du tableau "p4".
390  * cp Tableau de code indiquant la visibilite des points 4D.
391  * p3 Tableau de points 3D issus de la transformation.
392  */
393 static void point_4D_3D(Point4f *p4, int size, Byte *cp, Point3f *p3)
394 {
395  Point4f *pend = p4 + size; /* borne de p4 */
396  float w;
397 
398  for (; p4 < pend; p4++, p3++) {
399  if (*cp++ == IS_INSIDE) { /* point visible */
400  w = p4->w;
401 
402  p3->x = p4->x / w; /* projection 4D en 3D */
403  p3->y = p4->y / w;
404  p3->z = p4->z / w;
405  }
406  else { /* marque invisible */
407  p3->z = -1.0;
408  }
409  }
410 }
411 
412 /*
413  * La procedure "set_Point4f_code" initialise la position des points 4D
414  * par rapport a 6 plans de la pyramide tronquee de vision.
415  * A chaque point est associe un code indiquant la position respective du
416  * point. Entree : p4 Tableau de points 4D a localiser. size
417  * Taille du tableau "p4". cp Tableau de codes de localisation
418  * resultat.
419  */
420 void set_Point4f_code(Point4f *p4, int size, Byte *cp)
421 {
422  Point4f *pend = p4 + size; /* borne de p4 */
423  Byte b; /* code de p4 */
424 
425  for (; p4 < pend; p4++, *cp++ = b) {
426  b = IS_INSIDE;
427  if (p4->w < p4->y)
428  b |= IS_ABOVE;
429  else if (-p4->w > p4->y)
430  b |= IS_BELOW;
431  if (p4->w < p4->x)
432  b |= IS_RIGHT;
433  else if (-p4->w > p4->x)
434  b |= IS_LEFT;
435  if (p4->w < p4->z)
436  b |= IS_BACK;
437  else if (-0.9 > p4->z)
438  b |= IS_FRONT;
439  }
440 }
441 
442 /*
443  * La procedure "where_is_Point4f" localise un point 4D par rapport aux 6
444  * plans de decoupage de la pyramide tronquee de vision. Entree : p4
445  * Point homogene 4D a localiser. Sortie : Code indiquant le position de "p4"
446  * par rapport aux 6 plans.
447  */
448 Byte where_is_Point4f(Point4f *p4)
449 {
450  Byte b = IS_INSIDE; /* code de "p4" */
451 
452  if (p4->w < p4->y)
453  b |= IS_ABOVE;
454  else if (-p4->w > p4->y)
455  b |= IS_BELOW;
456  if (p4->w < p4->x)
457  b |= IS_RIGHT;
458  else if (-p4->w > p4->x)
459  b |= IS_LEFT;
460  if (p4->w < p4->z)
461  b |= IS_BACK;
462  else if (-0.9 > p4->z)
463  b |= IS_FRONT;
464  return (b);
465 }
466 END_VISP_NAMESPACE
467 #endif
error that can be emitted by ViSP classes.
Definition: vpException.h:60
@ fatalError
Fatal error.
Definition: vpException.h:72