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