ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/hd/sm_del.c
Revision: 3.6
Committed: Tue Oct 6 18:16:53 1998 UTC (25 years, 5 months ago) by gwlarson
Content type: text/plain
Branch: MAIN
Changes since 3.5: +286 -159 lines
Log Message:
new triangulate routine
added smTestSample to check for occlusion
added frustum culling before rebuild
changed base quadtree to use octahedron and created new point locate
added "sample active" flags and implemented LRU replacement
started handling case of too many triangles
set sizes are now unbounded
changed all quadtree pointers to quadtrees

File Contents

# Content
1 /* Copyright (c) 1998 Silicon Graphics, Inc. */
2
3 #ifndef lint
4 static char SCCSid[] = "$SunId$ SGI";
5 #endif
6
7 /*
8 * sm_del.c
9 */
10 #include "standard.h"
11 #include "sm_flag.h"
12 #include "sm_list.h"
13 #include "sm_geom.h"
14 #include "sm_qtree.h"
15 #include "sm_stree.h"
16 #include "sm.h"
17
18 static int Max_edges=200;
19 static EDGE *Edges=NULL;
20 static int Ecnt=0;
21
22 #define remove_tri_compress remove_tri
23 remove_tri(qtptr,fptr,tptr)
24 QUADTREE *qtptr;
25 int *fptr;
26 int *tptr;
27 {
28 int n;
29
30 if(QT_IS_EMPTY(*qtptr))
31 return;
32 if(QT_LEAF_IS_FLAG(*qtptr))
33 return;
34
35 n = QT_SET_CNT(qtqueryset(*qtptr))-1;
36 *qtptr = qtdelelem(*qtptr,*tptr);
37 if(n == 0)
38 (*fptr) |= QT_COMPRESS;
39 if(!QT_FLAG_FILL_TRI(*fptr))
40 (*fptr)++;
41 }
42
43
44 smLocator_remove_tri(sm,t_id,v0_id,v1_id,v2_id)
45 SM *sm;
46 int t_id;
47 int v0_id,v1_id,v2_id;
48 {
49 STREE *st;
50 FVECT v0,v1,v2;
51
52 st = SM_LOCATOR(sm);
53
54 VSUB(v0,SM_NTH_WV(sm,v0_id),SM_VIEW_CENTER(sm));
55 VSUB(v1,SM_NTH_WV(sm,v1_id),SM_VIEW_CENTER(sm));
56 VSUB(v2,SM_NTH_WV(sm,v2_id),SM_VIEW_CENTER(sm));
57
58 qtClearAllFlags();
59
60 stApply_to_tri(st,v0,v1,v2,remove_tri,remove_tri_compress,&t_id);
61
62 }
63
64 smFree_tri(sm,id)
65 SM *sm;
66 int id;
67 {
68 TRI *tri;
69
70 tri = SM_NTH_TRI(sm,id);
71 /* Add to the free_list */
72 T_NEXT_FREE(tri) = SM_FREE_TRIS(sm);
73 SM_FREE_TRIS(sm) = id;
74 T_VALID_FLAG(tri) = -1;
75 }
76
77 /* Assumes mesh pointers have been cleaned up appropriately: just deletes from
78 Point location and triangle data structure
79 */
80 smDelete_tri(sm,t_id)
81 SM *sm;
82 int t_id;
83 {
84
85 /* NOTE: Assumes that a new triangle adjacent to each vertex
86 has been added- before the deletion: replacing
87 the old tri- and therefore dont need to dereference any pointers
88 to id because the vertices can no longer
89 point to tri id as being the first triangle pointer
90 */
91 if(!SM_IS_NTH_T_BASE(sm,t_id))
92 {
93 SM_SAMPLE_TRIS(sm)--;
94 if(SM_IS_NTH_T_NEW(sm,t_id))
95 smNew_tri_cnt--;
96 }
97 smClear_tri_flags(sm,t_id);
98
99 smFree_tri(sm,t_id);
100 }
101
102
103 int
104 eNew_edge()
105 {
106 if(!Edges)
107 if(!(Edges = (EDGE *)realloc(NULL,(Max_edges+1)*sizeof(EDGE))))
108 goto memerr;
109
110 if(Ecnt >= Max_edges)
111 {
112 if(Max_edges > 10000)
113 error(CONSISTENCY,"Too many edges in vertex loop\n");
114 Max_edges += 100;
115 if(!(Edges = (EDGE *)realloc(Edges,(Max_edges+1)*sizeof(EDGE))))
116 goto memerr;
117 }
118 return(++Ecnt);
119
120 memerr:
121 error(SYSTEM,"eNew_edge(): Unable to allocate memory");
122 }
123
124 LIST
125 *smVertex_star_polygon(sm,id,delptr)
126 SM *sm;
127 int id;
128 QUADTREE *delptr;
129 {
130 TRI *tri,*t_next;
131 LIST *elist,*end;
132 int t_id,v_next,t_next_id;
133 int e;
134 OBJECT del_set[2];
135
136 elist = end = NULL;
137 /* Get the first triangle adjacent to vertex id */
138 t_id = SM_NTH_VERT(sm,id);
139 tri = SM_NTH_TRI(sm,t_id);
140
141 if((e = eNew_edge()) == INVALID)
142 return(NULL);
143
144 v_next = (T_WHICH_V(tri,id)+1)%3;
145 SET_E_NTH_VERT(e,0,T_NTH_V(tri,v_next));
146 SET_E_NTH_TRI(e,0,INVALID);
147 SET_E_NTH_TRI(e,1,T_NTH_NBR(tri,v_next));
148 v_next = (T_WHICH_V(tri,id)+2)%3;
149 SET_E_NTH_VERT(e,1,T_NTH_V(tri,v_next));
150 elist = add_data_to_circular_list(elist,&end,e);
151
152 t_next_id = t_id;
153 t_next = tri;
154
155 del_set[0] =1; del_set[1] = t_id;
156 *delptr = qtnewleaf(del_set);
157
158 while((t_next_id = T_NTH_NBR(t_next,v_next)) != t_id)
159 {
160 if((e = eNew_edge()) == INVALID)
161 return(NULL);
162
163 t_next = SM_NTH_TRI(sm,t_next_id);
164 v_next = (T_WHICH_V(t_next,id)+1)%3;
165
166 SET_E_NTH_VERT(e,0,T_NTH_V(t_next,v_next));
167 SET_E_NTH_TRI(e,0,INVALID);
168 SET_E_NTH_TRI(e,1,T_NTH_NBR(t_next,v_next));
169 v_next = (T_WHICH_V(t_next,id)+2)%3;
170 SET_E_NTH_VERT(e,1,T_NTH_V(t_next,v_next));
171 elist = add_data_to_circular_list(elist,&end,e);
172
173
174 if(qtinset(*delptr,t_next_id))
175 {
176 #ifdef DEBUG
177 eputs("smVertex_star_polygon(): id already in set\n");
178 #endif
179 free_list(elist);
180 return(NULL);
181 }
182 else
183 qtaddelem(*delptr,t_next_id);
184 }
185 return(elist);
186 }
187
188 int
189 smEdge_intersect_polygon(sm,v0,v1,l)
190 SM *sm;
191 FVECT v0,v1;
192 LIST *l;
193 {
194 FVECT e0,e1;
195 int e,id_e0,id_e1;
196 LIST *el,*eptr;
197
198 /* Test the edges in l against v0v1 to see if v0v1 intersects
199 any other edges
200 */
201
202 el = l;
203
204 while(el)
205 {
206 e = (int)LIST_DATA(el);
207 id_e0 = E_NTH_VERT(e,0);
208 id_e1 = E_NTH_VERT(e,1);
209
210 VSUB(e0,SM_NTH_WV(sm,id_e0),SM_VIEW_CENTER(sm));
211 VSUB(e1,SM_NTH_WV(sm,id_e1),SM_VIEW_CENTER(sm));
212 if(sedge_intersect(v0,v1,e0,e1))
213 return(TRUE);
214
215 el = LIST_NEXT(el);
216 if(el == l)
217 break;
218 }
219 return(FALSE);
220 }
221
222 int
223 smFind_next_convex_vertex(sm,id0,id1,v0,v1,l)
224 SM *sm;
225 int id0,id1;
226 FVECT v0,v1;
227 LIST *l;
228 {
229 int e,id;
230 LIST *el;
231 FVECT v;
232
233 /* starting with the end of edge at head of l, search sequentially for
234 vertex v such that v0v1v is a convex angle, and the edge v1v does
235 not intersect any other edges
236 */
237 id = INVALID;
238 el = l;
239 while(id != id0)
240 {
241 e = (int)LIST_DATA(el);
242 id = E_NTH_VERT(e,1);
243
244 smDir(sm,v,id);
245
246 if(convex_angle(v0,v1,v) && !smEdge_intersect_polygon(sm,v1,v,l))
247 return(id);
248
249 el = LIST_NEXT(el);
250 if(el == l)
251 break;
252 }
253 return(INVALID);
254 }
255
256 int
257 split_edge_list(id0,id_new,l,lnew)
258 int id0,id_new;
259 LIST **l,**lnew;
260 {
261 LIST *list,*lptr,*end;
262 int e,e1,e2,new_e;
263
264 e2 = INVALID;
265 list = lptr = *l;
266
267 if((new_e = eNew_edge())==INVALID)
268 {
269 #ifdef DEBUG
270 eputs("split_edge_list():Too many edges\n");
271 #endif
272 return(FALSE);
273 }
274 SET_E_NTH_VERT(new_e,0,id0);
275 SET_E_NTH_VERT(new_e,1,id_new);
276 SET_E_NTH_TRI(new_e,0,INVALID);
277 SET_E_NTH_TRI(new_e,1,INVALID);
278
279 while(e2 != id_new)
280 {
281 lptr = LIST_NEXT(lptr);
282 e = (int)LIST_DATA(lptr);
283 e2 = E_NTH_VERT(e,1);
284 if(lptr == list)
285 {
286 #ifdef DEBUG
287 eputs("split_edge_list():cant find vertex\n");
288 #endif
289 *lnew = NULL;
290 return(FALSE);
291 }
292
293 }
294 end = lptr;
295 lptr = LIST_NEXT(lptr);
296 list = add_data_to_circular_list(list,&end,-new_e);
297 *lnew = list;
298
299 /* now follow other cycle */
300
301 list = lptr;
302 e2 = INVALID;
303 while(e2 != id0)
304 {
305 lptr = LIST_NEXT(lptr);
306 e = (int)LIST_DATA(lptr);
307 e2 = E_NTH_VERT(e,1);
308 if(lptr == list)
309 {
310 #ifdef DEBUG
311 eputs("split_edge_list():cant find intial vertex\n");
312 #endif
313 *l = NULL;
314 return(FALSE);
315 }
316
317 }
318 end = lptr;
319 list = add_data_to_circular_list(list,&end,new_e);
320 *l = list;
321 return(TRUE);
322 }
323
324
325 int
326 smTriangulate_convex(sm,plist,add_ptr)
327 SM *sm;
328 LIST *plist,**add_ptr;
329 {
330 int t_id,e_id0,e_id1,e_id2;
331 int v_id0,v_id1,v_id2;
332 LIST *lptr;
333 int cnt;
334
335 lptr = plist;
336 e_id0 = (int)LIST_DATA(lptr);
337 v_id0 = E_NTH_VERT(e_id0,0);
338 lptr = LIST_NEXT(lptr);
339 while(LIST_NEXT(lptr) != plist)
340 {
341 e_id1 = (int)LIST_DATA(lptr);
342 v_id1 = E_NTH_VERT(e_id1,0);
343 v_id2 = E_NTH_VERT(e_id1,1);
344 /* form a triangle for each triple of with v0 as base of star */
345 t_id = smAdd_tri(sm,v_id0,v_id1,v_id2);
346 *add_ptr = push_data(*add_ptr,t_id);
347
348 /* add which pointer?*/
349
350 lptr = LIST_NEXT(lptr);
351
352 if(LIST_NEXT(lptr) != plist)
353 {
354 e_id2 = eNew_edge();
355 SET_E_NTH_VERT(e_id2,0,v_id2);
356 SET_E_NTH_VERT(e_id2,1,v_id0);
357 }
358 else
359 e_id2 = (int)LIST_DATA(lptr);
360
361 /* set appropriate tri for each edge*/
362 SET_E_NTH_TRI(e_id0,0,t_id);
363 SET_E_NTH_TRI(e_id1,0,t_id);
364 SET_E_NTH_TRI(e_id2,0,t_id);
365
366 e_id0 = -e_id2;
367 }
368 free_list(plist);
369 return(TRUE);
370 }
371 int
372 smTriangulate_elist(sm,plist,add_ptr)
373 SM *sm;
374 LIST *plist,**add_ptr;
375 {
376 LIST *l,*el1;
377 FVECT v0,v1,v2;
378 int id0,id1,id2,e,id_next;
379 char flipped;
380 int done;
381
382 l = plist;
383
384 while(l)
385 {
386 /* get v0,v1,v2 */
387 e = (int)LIST_DATA(l);
388 id0 = E_NTH_VERT(e,0);
389 id1 = E_NTH_VERT(e,1);
390 l = LIST_NEXT(l);
391 e = (int)LIST_DATA(l);
392 id2 = E_NTH_VERT(e,1);
393
394 smDir(sm,v0,id0);
395 smDir(sm,v1,id1);
396 smDir(sm,v2,id2);
397 /* determine if convex (left turn), or concave(right turn) angle */
398 if(convex_angle(v0,v1,v2))
399 {
400 if(l == plist)
401 break;
402 else
403 continue;
404 }
405 /* if concave: add edge and recurse on two sub polygons */
406 id_next = smFind_next_convex_vertex(sm,id0,id1,v0,v1,LIST_NEXT(l));
407 if(id_next == INVALID)
408 {
409 #ifdef DEBUG
410 eputs("smTriangulate_elist():Unable to find convex vertex\n");
411 #endif
412 return(FALSE);
413 }
414 /* add edge */
415 el1 = NULL;
416 /* Split edge list l into two lists: one from id1-id_next-id1,
417 and the next from id2-id_next-id2
418 */
419 split_edge_list(id1,id_next,&l,&el1);
420 /* Recurse and triangulate the two edge lists */
421 done = smTriangulate_elist(sm,l,add_ptr);
422 if(done)
423 done = smTriangulate_elist(sm,el1,add_ptr);
424 return(done);
425 }
426 done = smTriangulate_convex(sm,plist,add_ptr);
427 return(done);
428 }
429
430 int
431 smTriangulate_add_tri(sm,id0,id1,id2,e0,e1,e2ptr)
432 SM *sm;
433 int id0,id1,id2,e0,e1,*e2ptr;
434 {
435 int t_id;
436 int e2;
437
438 t_id = smAdd_tri(sm,id0,id1,id2);
439 if(*e2ptr == 0)
440 {
441 e2 = eNew_edge();
442 SET_E_NTH_VERT(e2,0,id2);
443 SET_E_NTH_VERT(e2,1,id0);
444 }
445 else
446 e2 = *e2ptr;
447 /* set appropriate tri for each edge*/
448 SET_E_NTH_TRI(e0,0,t_id);
449 SET_E_NTH_TRI(e1,0,t_id);
450 SET_E_NTH_TRI(e2,0,t_id);
451
452 *e2ptr = e2;
453 return(t_id);
454 }
455 int
456 smTriangulate_elist_new(sm,id,plist,add_ptr)
457 SM *sm;
458 int id;
459 LIST *plist,**add_ptr;
460 {
461 LIST *l,*prev,*t;
462 FVECT v0,v1,v2,n,p;
463 int is_tri,loop,t_id,id0,id1,id2,e2,eprev,enext;
464 double dp;
465
466 smDir(sm,p,id);
467 enext=0;
468 is_tri= loop = FALSE;
469 l = prev = plist;
470 /* get v0,v1,v2 */
471 eprev = (int)LIST_DATA(l);
472 id0 = E_NTH_VERT(eprev,0);
473 id1 = E_NTH_VERT(eprev,1);
474 smDir(sm,v0,id0);
475 smDir(sm,v1,id1);
476 while(l)
477 {
478 l = LIST_NEXT(l);
479 e2 = (int)LIST_DATA(l);
480 id2 = E_NTH_VERT(e2,1);
481 /* Check if have a triangle */
482 if(LIST_NEXT(LIST_NEXT(l)) == prev)
483 {
484 is_tri = TRUE;
485 break;
486 }
487 if(LIST_NEXT(l) == plist)
488 {
489 if(!loop)
490 loop = 1;
491 else
492 loop++;
493 if(loop > 3)
494 break;
495 }
496 smDir(sm,v2,id2);
497 /* determine if convex (left turn), or concave(right turn) angle */
498 if(!convex_angle(v0,v1,v2))
499 {
500 VCOPY(v0,v1);
501 VCOPY(v1,v2);
502 id0 = id1;
503 id1 = id2;
504 prev = l;
505 eprev = e2;
506 continue;
507 }
508 VCROSS(n,v0,v2);
509 dp = DOT(n,p);
510 if(loop <=1 && (!ZERO(dp) && dp < 0.0))
511 {
512 VCOPY(v0,v1);
513 VCOPY(v1,v2);
514 id0 = id1;
515 id1 = id2;
516 eprev = e2;
517 prev = l;
518 continue;
519 }
520 loop = FALSE;
521
522 enext = 0;
523 t_id = smTriangulate_add_tri(sm,id0,id1,id2,eprev,e2,&enext);
524 *add_ptr = push_data(*add_ptr,t_id);
525
526 LIST_NEXT(prev) = LIST_NEXT(l);
527 LIST_DATA(prev) = eprev = -enext;
528 LIST_NEXT(l)=NULL;
529 if(l== plist)
530 plist = prev;
531 free_list(l);
532 l = prev;
533 VCOPY(v1,v2);
534 id1 = id2;
535 }
536 if(is_tri)
537 {
538 l = LIST_NEXT(l);
539 enext = (int)LIST_DATA(l);
540 t_id = smTriangulate_add_tri(sm,id0,id1,id2,eprev,e2,&enext);
541 *add_ptr = push_data(*add_ptr,t_id);
542 free_list(l);
543 }
544 else
545 {
546 #ifdef DEBUG
547 eputs("smTriangulate_elist()Unable to triangulate\n");
548 #endif
549 return(FALSE);
550 }
551 return(TRUE);
552 }
553
554 int
555 smTriangulate(sm,p_id,plist,add_ptr)
556 SM *sm;
557 int p_id;
558 LIST *plist,**add_ptr;
559 {
560 int e,id_t0,id_t1,e0,e1;
561 int test;
562
563 test = smTriangulate_elist_new(sm,p_id,plist,add_ptr);
564 #if 0
565 test = smTriangulate_elist(sm,plist,add_ptr);
566 #endif
567
568 if(!test)
569 return(test);
570
571 FOR_ALL_EDGES(e)
572 {
573 id_t0 = E_NTH_TRI(e,0);
574 id_t1 = E_NTH_TRI(e,1);
575 if((id_t0==INVALID) || (id_t1==INVALID))
576 {
577 #ifdef DEBUG
578 eputs("smTriangulate(): Unassigned edge neighbor\n");
579 #endif
580 continue;
581 }
582
583 e0 = T_WHICH_V(SM_NTH_TRI(sm,id_t0),E_NTH_VERT(e,0));
584 T_NTH_NBR(SM_NTH_TRI(sm,id_t0),e0) = id_t1;
585
586 e1 = T_WHICH_V(SM_NTH_TRI(sm,id_t1),E_NTH_VERT(e,1));
587 T_NTH_NBR(SM_NTH_TRI(sm,id_t1),e1) = id_t0;
588 }
589 return(test);
590 }
591
592 eIn_tri(e,t)
593 int e;
594 TRI *t;
595 {
596
597 if(T_NTH_V(t,0)==E_NTH_VERT(e,0))
598 return(T_NTH_V(t,1)==E_NTH_VERT(e,1)||T_NTH_V(t,2)==E_NTH_VERT(e,1));
599 else
600 if(T_NTH_V(t,1)==E_NTH_VERT(e,0))
601 return(T_NTH_V(t,0)==E_NTH_VERT(e,1)||T_NTH_V(t,2)==E_NTH_VERT(e,1));
602 else if(T_NTH_V(t,2)==E_NTH_VERT(e,0))
603 return(T_NTH_V(t,0)==E_NTH_VERT(e,1)||T_NTH_V(t,1)==E_NTH_VERT(e,1));
604 return(FALSE);
605 }
606
607 smFix_edges(sm,add_list,delptr)
608 SM *sm;
609 LIST *add_list;
610 QUADTREE *delptr;
611
612 {
613 int e,t0_id,t1_id,e_new,e0,e1,e0_next,e1_next;
614 int i,v0_id,v1_id,v2_id,p_id,t0_nid,t1_nid;
615 FVECT v0,v1,v2,p,np,v;
616
617 FOR_ALL_EDGES(e)
618 {
619 t0_id = E_NTH_TRI(e,0);
620 t1_id = E_NTH_TRI(e,1);
621 if((t0_id==INVALID) || (t1_id==INVALID))
622 {
623 #ifdef DEBUG
624 eputs("smFix_edges: Unassigned edge nbr\n");
625 #endif
626 continue;
627 }
628 e0 = T_WHICH_V(SM_NTH_TRI(sm,t0_id),E_NTH_VERT(e,0));
629 e1 = T_WHICH_V(SM_NTH_TRI(sm,t1_id),E_NTH_VERT(-e,0));
630 e0_next = (e0+2)%3;
631 e1_next = (e1+2)%3;
632 v0_id = E_NTH_VERT(e,0);
633 v1_id = E_NTH_VERT(e,1);
634 v2_id = T_NTH_V(SM_NTH_TRI(sm,t0_id),e0_next);
635 p_id = T_NTH_V(SM_NTH_TRI(sm,t1_id),e1_next);
636
637 smDir_in_cone(sm,v0,v0_id);
638 smDir_in_cone(sm,v1,v1_id);
639 smDir_in_cone(sm,v2,v2_id);
640
641 VCOPY(p,SM_NTH_WV(sm,p_id));
642 VSUB(p,p,SM_VIEW_CENTER(sm));
643 if(point_in_cone(p,v0,v1,v2))
644 {
645 smTris_swap_edge(sm,t0_id,t1_id,e0,e1,&t0_nid,&t1_nid,&add_list,
646 delptr);
647
648 FOR_ALL_EDGES_FROM(e,i)
649 {
650 if(E_NTH_TRI(i,0)==t0_id || E_NTH_TRI(i,0)==t1_id)
651 {
652 if(eIn_tri(i,SM_NTH_TRI(sm,t0_nid)))
653 SET_E_NTH_TRI(i,0,t0_nid);
654 else
655 SET_E_NTH_TRI(i,0,t1_nid);
656 }
657
658 if(E_NTH_TRI(i,1)==t0_id || E_NTH_TRI(i,1)==t1_id)
659 {
660 if(eIn_tri(i,SM_NTH_TRI(sm,t0_nid)))
661 SET_E_NTH_TRI(i,1,t0_nid);
662 else
663 SET_E_NTH_TRI(i,1,t1_nid);
664 }
665 }
666 t0_id = t0_nid;
667 t1_id = t1_nid;
668 e_new = eNew_edge();
669 SET_E_NTH_VERT(e_new,0,p_id);
670 SET_E_NTH_VERT(e_new,1,v2_id);
671 SET_E_NTH_TRI(e_new,0,t0_id);
672 SET_E_NTH_TRI(e_new,1,t1_id);
673 }
674 }
675 smUpdate_locator(sm,add_list,qtqueryset(*delptr));
676 }
677
678 int
679 smMesh_remove_vertex(sm,id)
680 SM *sm;
681 int id;
682 {
683 int t_id;
684 LIST *elist,*add_list;
685 int cnt,debug;
686 QUADTREE delnode;
687 /* generate list of vertices that form the boundary of the
688 star polygon formed by vertex id and all of its adjacent
689 triangles
690 */
691 eClear_edges();
692 elist = smVertex_star_polygon(sm,id,&delnode);
693
694 if(!elist)
695 {
696 #ifdef DEBUG
697 eputs("smMesh_remove_vertex(): Unable to remove vertex");
698 #endif
699 qtfreeleaf(delnode);
700 return(FALSE);
701 }
702 add_list = NULL;
703 /* Triangulate spherical polygon */
704 if(!smTriangulate(sm,id,elist,&add_list))
705 {
706 while(add_list)
707 {
708 t_id = pop_list(&add_list);
709 smDelete_tri(sm,t_id);
710 }
711 qtfreeleaf(delnode);
712 return(FALSE);
713 }
714 /* Fix up new triangles to be Delaunay */
715 smFix_edges(sm,add_list,&delnode);
716
717 qtfreeleaf(delnode);
718 return(TRUE);
719 }
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734