File: compose.l

package info (click to toggle)
euslisp 9.31%2Bdfsg-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 55,448 kB
  • sloc: ansic: 41,610; lisp: 3,339; makefile: 286; sh: 238; asm: 138; python: 53
file content (832 lines) | stat: -rw-r--r-- 27,672 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; body composition -- boolean operation between bodies
;;;	Copyright (1988) Toshihiro MATSUI, Electrotechnical Laboratory
;;;	1988-Feb
;;;	Union, intersection, subtraction and cut of bodies
;;;	All operations are unstable when elements of bodies are touching.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;	Nov/1991 (one week)
;;;		Modification to allow bodies to touch on thier faces
;;;

(in-package "GEOMETRY")
(export '(cut-body compose-body 
	body+ body* body- body-interference semi-space body/))

(export '(contacting-faces aligned-faces))

(eval-when (compile) (load "geoclasses.l") )

(defvar  *body*)
(defvar  *bodies*)
(defvar  *faces*)
(defvar  *edges*)
(defvar  *edge-class*)
(defvar  *face-class*)
(defvar  *hole-class*)
(defvar  *body-class*)

;;;
;;;  cut body by plane
;;;

(defun intersecting-edges (pln edges)
   (let ((ie) (p))
      (declare (type float p))
      (dolist (e edges)
	 (setq p (send pln :intersect-edge e))
	 (if (and p (<= 0.0 (car p) 1.0)) (push (cons e p) ie)))
      ie))

(defun cut-body (bod cutting-plane)
  (let ((fac-intersects) (edg-intersects)
        (sort-index) (sort-dir)
	(created-edges) 
	(aface)  (face-list) )
    (setq edg-intersects (intersecting-edges cutting-plane (send bod :edges)))
    (setq fac-intersects (sort-edges-by-face edg-intersects))
    (dolist (fint fac-intersects)
	(when (oddp (length (cdr fint)))   (error "odd intersects"))
	(setq aface (car fint)
	      edg-intersects (cdr fint))
	(setq sort-dir (v* (send aface :normal) (send cutting-plane :normal))
	      sort-index (maxindex sort-dir)
	      edg-intersects  (sort edg-intersects
				   (if (> (aref sort-dir sort-index) 0.0)
					#'> #'<)
				   #'(lambda (e) (aref (caddr e) sort-index))))
	(while edg-intersects
	    (push (instance *edge-class* :init
			 :pvertex (caddr (car edg-intersects))
			 :nvertex (caddr (cadr edg-intersects))
			 :pface   cutting-plane
			 :nface   aface)
		  created-edges)
	    (setq edg-intersects (cddr edg-intersects))) )
    (construct-faces (cons cutting-plane  created-edges)) ))


(defun insert-intersections (intlists faces common-box)
  (flet ((put-face (e int f)
	    (if (null (cddr int))
		(nconc int (list f))
		(let ((edir (send e :direction)))
		   (if  (> (abs (v. edir (send f :normal)))
			   (abs (v. edir (send (caddr int) :normal))))
			(rplaca (cddr int) f) )))))
   (let (p param pplist)
     (dolist (aface faces)
	(if (send (send aface :box 0.01) :intersection-p common-box)
	    (dolist (ie intlists intlists)
		(setq p (send aface :intersect-edge (car ie)))
		(when  (and  (consp p)
			     (null (send aface :coplanar-line (car ie))))
		   (setq param (car p)
		   	 pplist (cdr ie))
		   (while pplist 
		     (cond ((eps= (caar pplist) param)
			    (put-face (car ie) (car pplist) aface)
			    (return nil))
			   ((< (caar pplist) param)
			    (if (eps= (caar pplist) 1.0)
				(progn 
				   (put-face (car ie) (car pplist) aface)
				   (return nil))
			        (pop pplist)))
			   ((> (caar pplist) param)
			    (rplacd pplist (cons (car pplist) (cdr pplist)))
			    (rplaca pplist (nconc p (list aface)) ) 
			    (return nil)) ))
		) ) )
   )) ) )

(defun make-edge-segments (intersects target-body side)
   (let ((created-edges) (new-edge)
	 (mid (float-vector 0 0 0))
	 p1 p2  flag original-edge
	 cutting-face1 cutting-face2)
      (dolist (intlist intersects)
	 ;intlist=(edge (param1 point1 ..) (param2 point2 ...))
	;; (rplacd intlist (sort (cdr intlist) #'< #'car))
	 (setq original-edge (pop intlist))
	 (while (cdr intlist)
	    (setq p1 (pop intlist) p2  (car intlist))
	    (midpoint 0.5 (cadr p1) (cadr p2) mid)
	    (setq flag (send target-body :insidep mid))
	    (setq cutting-face1 (third p1)
		  cutting-face2 (third p2))
	    (when (or (eq flag side) (eq flag ':border))
		(setq new-edge
 		      (instance *edge-class* :init
			      :pvertex (cadr p1)
			      :nvertex (cadr p2)
			      :pface (edge-pface original-edge)
			      :nface (edge-nface original-edge)
			      :flags (edge-flags original-edge)) )
		(push (list original-edge	;1
			    new-edge		;2
			    (caddr p1)		;3 segmenting face
			    (caddr p2)		;4 on both ends
			    flag		;5
			    (car p1)		;6
			    (car p2))		;7
		      created-edges)
		)
	))
      created-edges))


(defun intersecting-segments (segments)
   ;collect segments which have intersections with other faces
   (mapcan
      #'(lambda (s)
	   (if (and (or (third s) (fourth s))
		    (not (eq (fifth s) ':border)))
		(list s))) segments))

(defun sort-edges-by-face (intlist)
    (let* (flist fentry aface e)
      (dolist (int intlist)
	 (setq e (car int)	;original edge
	       fentry (assq (edge-pface e) flist))
	 (if (null fentry)
	     (push (list (edge-pface e) int) flist)
	     (nconc fentry (list int)))
	 (setq fentry (assq (edge-nface e) flist))
	 (if (null fentry)
	     (push (list (edge-nface e) int) flist)
	     (nconc fentry (list int))))
     flist))

(defun make-crossing-edges (intfaces1 intfaces2 first side)
   (let* (f1 f2s ints1 ints2  ex v sort-index sort-func
	  vlist vlist1 vlist2 pv nv f1normal
	  created-edges crossing-edges1 crossing-edges2)
      (dolist (ifac intfaces1)
	(unless (consp ifac) (error "ifac not list ~s" ifac))
	(setq f1 (car ifac)
	      ints1 (cdr ifac)
	      f1normal (send f1 :normal))
        (setq f2s nil)
	(dolist (ix ints1)	;collect faces with which f1 intersects
	   (unless (consp ix) (error "ifac not list ~s" ix))
	   (if (and (third ix)
		    (null (member (third ix) f2s)))
	       (push (third ix) f2s))
	   (if (and (fourth ix)
		    (null (member (fourth ix) f2s)) )
	       (push (fourth ix) f2s)))
	;; edges of f1 intersect with faces in f2s
	(dolist (f2 f2s)
	   (setq vlist1 nil
	         vlist2 nil)
	   (dolist (i1 ints1)
		;find points where edges of f1 intersects with f2
		(setq ex (cadr i1))	;car is the original edge
		(cond ((and (eq (third i1) f2)
;			    (not (send f2 :on-vertex (line-pvert ex)))
			    (eps<> (sixth i1) 0.0))
			(push (line-pvert ex) vlist1))
		      ((and (eq (fourth i1) f2)
;			    (not (send f2 :on-vertex (line-nvert ex)))
			    (eps<>  (seventh i1) 1.0))
			 (push (line-nvert ex) vlist1)) ))
	   (dolist (i2 (cdr (assq f2 intfaces2)))
		;find points where edges of f2 intersects with f1
		(setq ex (cadr i2))
		(cond ((and (eq (third i2) f1)
;			    (not (send f1 :on-vertex (line-pvert ex)))
			    (eps<> (sixth i2) 0.0))
			(push (line-pvert ex) vlist2))
		      ((and (eq (fourth i2) f1)
;			    (not (send f1 :on-vertex (line-nvert ex)))
			    (eps<>  (seventh i2) 1.0))
			(push (line-nvert ex) vlist2)))) 
  	   ; all vertices have been collected for f1 and f2
	   ; sort them along the intersection line
	   (setq v (v* (f2 . normal) (f1 . normal))
		 sort-index  (maxindex v)
		 sort-func   (if (eq side ':outside)
				 (if (< (aref v sort-index) 0.0) #'>= #'<=)
				 (if (< (aref v sort-index) 0.0) #'<= #'>=)))
	   (setq vlist (sort (append vlist1 vlist2)
			     sort-func
			     #'(lambda (vv) (elt vv sort-index))))
	  (setq vlist (remove-duplicates vlist :test #'eps-v=))
	;;  (break "vlist: ")
	;;;	SIMPLER
	;;;	   (setq vlist
	;;;		 (sort (append vlist1 vlist2)
	;;;		       #'<=
	;;;		       #'(lambda (vv)(v. vv v))) )
	   ; create and collect intersecting edges
	   (setq created-edges nil)
	   (while vlist
	      (setq pv (pop vlist)  nv (pop vlist))
	      (when (and pv nv
			;;  (not (eps-v= pv nv))
			 (not (send f1 :on-edge (midpoint 0.5 pv nv)))
			 (not (send f2 :on-edge (midpoint 0.5 pv nv)))
			 (or first (and (memq pv vlist1) (memq nv vlist1))))
	         (setq ex (instance *edge-class* :init :pvertex pv
					   :nvertex nv
					   :pface f1
					   :nface f2
					   :approximated nil))
		 (push ex created-edges)))
	   (when created-edges
	       (push (cons f1 created-edges) crossing-edges1)
	       (push (cons f2 (copy-seq created-edges)) crossing-edges2))
	   )	;for all intersections with body2
	)	;for all intersecting faces of body1
      (list crossing-edges1 crossing-edges2)))

(defun add-alist (key val alist)
   (let ((s (assq key alist)))
      (if (null s)
	  (acons key val alist)
	  (progn (nconc s val) alist))))

(defun merge-segments (segments s2 s3)
   (dolist (seg segments)  (rplacd seg (mapcar #'cadr (cdr seg))))
   (dolist (s s2) (setq segments (add-alist (car s) (cdr s) segments)))
   (dolist (s s3) (setq segments (add-alist (car s) (cdr s) segments)))
   segments)

(defun find-connecting-edge (vert edges &optional (test #'eq))
   (if (eq test #'eq)
	(find-if
		#'(lambda (e) (or (eq (edge-pvert e) vert) (eq (edge-nvert e) vert)))
		edges)
	(find-if #'(lambda (e) (or (funcall test (edge-pvert e) vert)
				   (funcall (edge-nvert e) vert)))
		edges)) )

(defun construct-faces (fac-edges)
   (setq *bug-edges* (copy-seq fac-edges))
   (let*  ( newfaces holes ahole
	   (oldface (car fac-edges))
	   (edges (cdr fac-edges))
	   )
      (while edges
	(let* ( (xedge (pop edges))
        	(fvertex (send xedge :pvertex oldface))
		(pvertex fvertex) (nvertex)  (circuit)
		(newface (instantiate *face-class*))
		primt-face)
           (while xedge
	      (setq nvertex (send xedge :nvertex oldface))
	      (send xedge :set-face pvertex nvertex newface)
	      (push xedge circuit)
	      (setq edges (delete xedge edges :count 1))
	      (setq xedge (find-connecting-edge nvertex edges)
	      	    pvertex nvertex))
	   (setq circuit (nreverse circuit))
	   (if (find-method oldface :primitive-face)
		(setq primt-face (send oldface :primitive-face)))
	   (send newface :init
			:edges circuit
			:primitive-face primt-face
			:body (if primt-face (send primt-face :body))
			:id (send oldface :id))
	   (cond
	      ((< (v. (send oldface :normal) (send newface :normal)) 0.0)
	       (push (cons newface circuit) holes))
	      (t (push newface newfaces) ) )))
      (when holes
	 (if *debug*
	     (format t ";; faces=~s~%holes=~s~%" newfaces holes))
	 (dolist (circuit holes)
	    (block enter-hole
	       (dolist (f newfaces)
	          (when (eq (send f :insidep ((cadr circuit) . pvert)) ':inside)
		     (dolist (c (cdr circuit))
			  (send c :set-face (send c :pvertex (car circuit))
					    (send c :nvertex (car circuit))
					    f))
		     (setq ahole (instance *hole-class* :init
						:edges (cdr circuit) :face f))
		     (send f :enter-hole ahole)
		     (return-from enter-hole nil)))
	       (setq *bug* (list newfaces holes))
	       (error "outer circuit not found~%"))))
      newfaces))

(defun initial-intersection-list (edges htab &aux res)
    (dolist (e edges res)
     (push 
	(list e
	      (list 0.0 (car (gethash (edge-pvert e) htab)))
	      (list 1.0 (car (gethash (edge-nvert e) htab))))
	res)))

;;
;; make a hashtable for retrieving edges by a vertex
;;

(defun make-vertex-edge-htab (bodfacs)	;body or list of faces
   (let* (edges vertices htab pv nv)
      (cond ((bodyp bodfacs)
		(setq vertices (body-vertices bodfacs)
		      edges (body-edges bodfacs)))
	    (t
	        (setq vertices (remove-duplicates
				  (apply #'append
					 (send-all bodfacs :all-vertices)))
		      edges (remove-duplicates
				  (apply #'append
					 (send-all bodfacs :all-edges))))))
      (setq htab (make-hash-table :size (1+ (* 2 (length vertices)))))
      (dolist (e edges)
	 (setq pv (edge-pvert e)
	       nv (edge-nvert e))
	 (setf (gethash pv htab) (cons e (gethash pv htab)))
	 (setf (gethash nv htab) (cons e (gethash nv htab))))
      htab))

(defun copy-add-vertex (htab)
   (maphash #'(lambda (k v) (setf (gethash k htab) (cons (copy-seq k) v)))
	   htab))
   
;;
;; Find contacting faces

(defun find-colinear-int (conint)
   (let (colinears)
      (dolist (con conint)
	 (dolist (c (cdr con))
	   (if (eql (cadr c) 'colinear)
		(push (list* (car con) c) colinears))))
      colinears))

(defun contacting-faces (body1 body2)
   (let* ((faces1 body1)
          (faces2 body2)
	  (con) (con2) (colinear-int)
          (result)
	  (edges1) (edges2))
      (if (derivedp body1 body)
	  (setq faces1 (body-faces body1)) )
      (if (derivedp body2 body)
	  (setq faces2 (body-faces body2)) )
    (dolist (f1 faces1)
      (dolist (f2 faces2)
        (when (setq con (send f1 :contactp  f2))
	  (setq edges1 (mapcar #'car con))
	  (setq edges2
		 (remove-duplicates
			(mapcar #'car (apply #'append (mapcar #'cdr con)))))
	  (cond ((memq con '(:included :including))
		  (push (list f1 f2 con) result))
		 ((or (= (length edges1) 1)     (= (length edges2) 1))
		  (setq colinear-int (find-colinear-int con))
		  (if (= (length colinear-int) 1)
		      (progn
			(format t ";; testing edge-edge contact~%")
			(setq colinear-int (car colinear-int))
		        (cond ((and (eps= (first (fourth colinear-int)) 0.0)
			            (eps= (second (fourth colinear-int)) 1.0)
				    (eql (send f2 :insidep
					       (send (first colinear-int)
						     :next-vertex f1))
					 ':inside) )
			        (push (list* f1 f2 con) result)	)
			       ((and (eps= (first (fifth colinear-int)) 0.0)
				     (eps= (second (fifth colinear-int)) 1.0)
				     (eql  (send f1 :insidep
					        (send (second colinear-int)
							:next-vertex f2))
					  ':inside))
			        (push (list* f1 f2 con) result)	)
				)) ;progn
			(push (list* f1 f2 con) result)) ;if
			)
		(t (push (list* f1 f2 con) result)) )
          ) ;when 
    ))	; dolist, dolist
    result))

(defun aligned-faces (body1 body2)
   (let* ((faces1 body1)
          (faces2 body2)
	  (algn) (algn2) (result)
	  (edges1) (edges2))
      (if (derivedp body1 body)
	  (setq faces1 (body-faces body1)) )
      (if (derivedp body2 body)
	  (setq faces2 (body-faces body2)))
      (dolist (f1 faces1)
        (dolist (f2 faces2)
	   (setq algn (send f1 :aligned-plane f2))
	   (if algn (push (list f1 f2) result))))
      result))


(defun find-equivalent-edge (e1 edges2)
   (dolist (e2 edges2)
      (if (or (and (eps-v= (line-pvert e1) (line-pvert e2))
		   (eps-v= (line-nvert e1) (line-nvert e2)))
	      (and (eps-v= (line-pvert e1) (line-nvert e2))
		   (eps-v= (line-nvert e1) (line-pvert e2))))
	  (return-from find-equivalent-edge e2)  )))

(defun unify-vertex (edges)
  (let ((vertices
	 (remove-duplicates
	    (apply #'append (mapcar #'(lambda (ln) (send ln :vertices)) edges))
	    :test #'eps-v=)))
     (dolist (e edges)
        (setf (line-pvert e)
	      (car (member (line-pvert e) vertices :test #'eps-v=)))
        (setf (line-nvert e)
	      (car (member (line-nvert e) vertices :test #'eps-v=))))))

(defun merge-edges-if-colinear (e1 e2 seg1 seg2)
   (when (and e1 e2 (send e1 :colinear-line e2))
      ;; delete e2 and replace e2's in seg2 with e1 whose vertex is extended
      ;; to cover e2.
      ;; (format t "merging colinear lines~%     ~s~%     ~s~%" e1 e2)
      (cond ((eps-v= (line-pvert e2) (line-pvert e1))
	     (setf (line-pvert e1)  (line-nvert e2)) )
	    ((eps-v= (line-pvert e2) (line-nvert e1))
	     (setf (line-nvert e1)  (line-nvert e2)) )
	    ((eps-v= (line-nvert e2) (line-pvert e1))
	     (setf (line-pvert e1)  (line-pvert e2)) )
	    ((eps-v= (line-nvert e2) (line-nvert e1))
	     (setf (line-nvert e1)  (line-pvert e2)) ) 
	    (t (return-from merge-edges-if-colinear nil) ) )
      (dolist (s seg2) (delete e2 s))
      e2 ) )

(defun merge-contacting-faces (face1 face2 seg1 seg2)
  ;; face2 circuit is deleted and  merged into face1
   (let* ((face1edges (assoc face1 seg1))
	  (face2edges (assoc face2 seg2)) 
	  e1 fx1 fx2 fxs1 fxs2 fxs)
     (dolist (e2 (cdr face2edges))
	(setq e1 (find-equivalent-edge e2 (cdr face1edges)))
        ;; (break "mcf: ")
	(cond (e1 	;duplicated edge found
		; (format t "duplicated edges ~s ~s~%" e1 e2)
		(setq fx1 (send e1 :another-face face1))	;old face
		(setq fx2 (send e2 :another-face face2))	;old face
		(cond ((eps-v= (send fx1 :normal) (send fx2 :normal))
			;; kill both edges
			(setq fxs1 (assoc fx1 seg1)
			      fxs2 (assoc fx2 seg2))
			(delete e1 face1edges)
			(delete e2 face2edges)
			(delete e1 fxs1)
			(delete e2 fxs2)
			;; Are there any singularities happening at the
			;; end-points of e2 and e1?
			(let ((ce2p (find-connecting-edge
					 (line-pvert e2) (cdr fxs2)))
			      (ce2n (find-connecting-edge
					 (line-nvert e2) (cdr fxs2))) 
			      (ce1p (find-connecting-edge
					 (line-pvert e1) (cdr fxs1)))
			      (ce1n (find-connecting-edge
					 (line-nvert e1) (cdr fxs1))) 	)
			   (merge-edges-if-colinear ce1p ce2p seg1 seg2)
			   (merge-edges-if-colinear ce1p ce2n seg1 seg2)
			   (merge-edges-if-colinear ce1n ce2p seg1 seg2)
			   (merge-edges-if-colinear ce1n ce2n seg1 seg2) )
			;; for each edge of fx2, replace fx2 with fx1
			(dolist (e (cdr  fxs2))
			   (if (or (eql (edge-pface e) fx2)
				   (eql (edge-nface e) fx2) )
			       (send e :replace-face fx2 fx1))
			   )
			(nconc fxs1 (cdr fxs2))
			(setq seg2 (delete fxs2 seg2))
			;; fxs2 has disappeared
			(push fxs1 fxs)
			;; (unify-vertex (cdr fxs1))
			)
		      (t 
			;; duplicated edges found, but two adjacent faces
			;; are not coplanar like a cone on a cylinder.
			(delete e2 face2edges)
			(delete e1 face1edges)
			(send e1 :replace-face face1 fx2)
			;; (unify-vertex (cdr face1edges))
			)))
	      ((eql (send face1 :insidep (send e2 :point 0.5))
		    ':outside)
		; do nothing
		)
	      ((null (member e2 face1edges))
		;; (format t ";; no duplicated edges ~s~%" e2)
		(delete e2 face2edges)
		(nconc face1edges (list e2))
		(send e2 :replace-face face2 face1)
		;;(unify-vertex (cdr face1edges))
	        )
	      (t (warn "unknow contacting edge state ~s ~s~%" face1 e2))
	))
    (dolist (e1 (cdr face1edges))
	(cond  ((eql (send face2 :insidep (send e1 :point 0.5))
		    ':inside)
		;;(warn "contacting edge ~s is inside of ~s~%" e1 face2)
		(delete e1 face1edges)
		(nconc face2edges (list e1))
		(send e1 :replace-face face1 face2)
		(push (assoc (send e1 :another-face face2) seg1) fxs)
		;; (unify-vertex (cdr fxs1))
		))
	;; (unify-vertex (cdr face2edges))
	)
    (unify-vertex (apply #'append
			 (cdr face2edges)
			 (cdr face1edges)
			 (mapcar #'cdr fxs)))
    seg2  ))



(defun merge-aligned-faces (face1 face2 seg1 seg2)
   (let* ((face1edges (assoc face1 seg1))
	  (face2edges (assoc face2 seg2)) 
	  e1  f1edges fx fxs)
     (setq f1e face1edges f2e face2edges)
     (if *debug* (break "aln1: "))
     (dolist (e2 (cdr face2edges))
	(setq e1 (find-equivalent-edge e2 (cdr face1edges)))
	(cond (e1 	;duplicated edge found
		(delete e2 face2edges)
		(delete e2
			(assoc (send e2 :another-face face2) seg2))
;		(delete e1 face1edges)
;		(delete e1
;			(assoc (send e1 :another-face face1) seg1) )
		)
	      ((eql (send face1 :insidep (send e2 :point 0.5)) ':inside)
		(delete e2 face2edges)
		(delete e2
			(assoc (send e2 :another-face face2) seg2))
		)
              (t 
		(delete e2 face2edges)
		(nconc face1edges (list e2))
		(send e2 :replace-face face2 face1))) )
     (dolist (e1 (cdr face1edges))
	(cond ((eql (send face2 :insidep (send e1 :point 0.5)) ':inside)
		(delete e1 face1edges)
		(delete e1
			(assoc (send e1 :another-face face1) seg1)) )) )
     (if (cdr face2edges)	;face2 should disappear
	 (error "face2edges left"))
     ;; find colinear edges in face1edges and link them  together
     (setq f1edges (cdr face1edges))
     (setq fx f1edges)
     ;; (break "aln2: ")
     (while (cdr f1edges)
          (setq e1 (pop f1edges))
	  (prog (e2 f2edges)
	    find-colinear
	      (setq f2edges f1edges)
	    find-colinear2
	      (setq e2 (pop f2edges))
	      (cond ((null e2) (return nil))
		    ((merge-edges-if-colinear e1 e2 seg1 seg2)
		     ;(delete e2 face1edges)
		     (setq f1edges (delete e2 f1edges))
		     (dolist (s seg1) (delete e2 s))
		     (go find-colinear) )
		    (t (go find-colinear2)))))
     (dolist (e (cdr face1edges))
	(setq fx (send e :another-face face1))
	(if (setq fx (assoc fx seg1))
	    (push (cdr fx) fxs)
	    (if (setq fx (assoc fx seg2))
	        (push (cdr fx) fxs))))
     (unify-vertex (remove-duplicates
			(apply #'append (cdr face1edges) fxs))     )
     (if *debug* (break "aln3: ") ))
   seg2)


(defun compose-body (body1 body2 side)
   (send body1 :worldcoords)
   (if (find-method body2 :worldcoords) (send body2 :worldcoords))
   (let* ((vertex-edge-htab1 (make-vertex-edge-htab body1))
	  (vertex-edge-htab2 (make-vertex-edge-htab body2))
	  (faces1 (send body1 :faces))
	  (faces2 (send body2 :faces))
	  (edges1 (send body1 :edges))
	  (edges2 (send body2 :edges))
	  (intersects1)
	  (intersects2)
	  (common-box  (send body1 :common-box body2 0.01))
	  (segments1) (segments2) (segments)
	  (crossing-edges1) (crossing-edges2)
	  contacts aligneds
	  ctime0 ctime1 ctime2 ctime3 ctime4 ctime5 ctime6 ctime7
	 )
     (setq ctime0 (runtime))
     (copy-add-vertex vertex-edge-htab1)
     (copy-add-vertex vertex-edge-htab2)
     (setq ctime1 (runtime))
     (setq intersects1 (initial-intersection-list edges1 vertex-edge-htab1))
     (setq intersects2 (initial-intersection-list edges2 vertex-edge-htab2))
     (setq ctime2 (runtime))
     (insert-intersections intersects1 faces2 common-box)
     (insert-intersections intersects2 faces1 common-box)
     (setq ctime3 (runtime))

     (setq i1 intersects1 i2 intersects2)
;;;      (format t ";; intersections in i1 and i2~%")     (break "1: ")

     ;; make-edge-segments is the most time consuming function
     (setq segments1 (make-edge-segments intersects1 body2 side))
     (setq segments2 (make-edge-segments intersects2 body1 side))
     ;; segments is a list of
     ;;  (org-edge new-edge segmenting-face1 segmenting-face2)
     (setq ctime4 (runtime))

     ;;(format t ";; make-edge-segments finished~%")    (break "2: ")

     (setq intersects1 (sort-edges-by-face (intersecting-segments segments1))
	   intersects2 (sort-edges-by-face (intersecting-segments segments2)))
     (setq ctime5 (runtime))
     (setq segments1 (sort-edges-by-face  segments1)
	   segments2 (sort-edges-by-face  segments2))
     (setq ctime6 (runtime))

     (setq crossing-edges1 (make-crossing-edges intersects1 segments2 t side)
 	   crossing-edges2 (make-crossing-edges intersects2 segments1 nil side))
     (setq *seg1* segments1
	   *seg2* segments2)
     (setq *se1* crossing-edges1
	   *se2* crossing-edges2)
     (when *debug* (format t ";; crossing edges~%")    (break "3: "))

     (setq segments1
	   (merge-segments segments1 (car crossing-edges1) (cadr crossing-edges2))
	   segments2
	   (merge-segments segments2 (car crossing-edges2) (cadr crossing-edges1)))
     (setq *seg1* segments1
	   *seg2* segments2)

     (if (derivedp body2 body)
	 (setq contacts (contacting-faces body1 body2)) )
     (when contacts
       (format *error-output* ";;~d face-to-face contact(s) found.~%" (length contacts))
       (if *debug* (break "con: "))
       (dolist (con contacts)
	 (setq segments2 (merge-contacting-faces
			  (car con) (cadr con) segments1 segments2)) )	
	)

     (setq segments1 (delete-if-not #'cdr segments1))
     (setq segments2 (delete-if-not #'cdr segments2))
     (setq *seg1* segments1
	   *seg2* segments2)
     (setq aligneds (aligned-faces (mapcar #'car segments1)
				   (mapcar #'car segments2)))
     (when aligneds
	(format *error-output* ";;~d face-to-face alignment(s) found.~%" (length aligneds))
	(dolist (algn aligneds)
	   (merge-aligned-faces
		(car algn) (cadr algn) segments1 segments2))
	)

     (setq *faces* nil)
     (when *debug*
	(format t ";; merging finished, start constructing faces~%")
	(break "5: ") )
    
     (dolist (s segments1)
	(if *debug* (format t ";; ~d " (length *faces*)))
        (push (construct-faces s) *faces*) )

     (dolist (s segments2)
	(if *debug* (format t ";; ~d " (length *faces*)))
        (push (construct-faces s) *faces*))

     (setq *faces* (flatten (nreverse *faces*)))

     (setq ctime7 (runtime))
     (if *debug*
	(format t ";; ~f ~f ~f ~f ~f ~f ~f~%"
		(* 0.0167 (- ctime1 ctime0))
		(* 0.0167 (- ctime2 ctime1))
		(* 0.0167 (- ctime3 ctime2))
		(* 0.0167 (- ctime4 ctime3))
		(* 0.0167 (- ctime5 ctime4))
		(* 0.0167 (- ctime6 ctime5))
		(* 0.0167 (- ctime7 ctime6))))
     (instance *body-class* :init :faces *faces*)
))

;;
;; toplevel functions for body composition
;;

(defun set-original-face (newbody)
   (let* ((csg-list (send newbody :csg))
	  (new-primitives (send newbody :primitive-bodies))
	  (original-primitives
	    (mapcar #'(lambda (x) (get x :copied-primitive)) new-primitives))
	  (fbody nil) (p nil))
      (dolist (f (send newbody :faces))
	 (setq fbody (send f :body))
	 (cond ((primitive-body-p fbody) 
		   (if (setq p (position fbody original-primitives))
		       (send f :primitive-face
			       (nth (position (send f :primitive-face)
					      (send fbody :faces))
				    (send (nth p new-primitives) :faces)))))
		(t (warn "not a primitive ~A" fbody)))
	 (send f :body newbody) )
      (dolist (p new-primitives)
	 (setf (get p :copied-primitive) t)
	;; (send p :manager newbody)
	 (send newbody :assoc p))
      new-primitives))

(defun body+ (&rest bodies)
   (let* ((rbody (first bodies))
	  (csg-list (list (send rbody :copy-csg))))
      (dolist (bbody (rest bodies))
	  (setq rbody (compose-body rbody bbody ':outside))
	  (push (send bbody :copy-csg)
		csg-list) )
      (send rbody :csg (cons '+ (list (nreverse csg-list))))
      (set-original-face rbody)
      rbody) )

(defun body* (&rest bodies)
   (let* ((abody (pop bodies)) (csg-list (list (send abody :copy-csg))))
      (while bodies
	  (setq abody (compose-body abody (car bodies) ':inside)) 
	  (push (send (pop bodies) :copy-csg) csg-list) )
      (send abody :csg (cons '* (list (nreverse csg-list))))
      (set-original-face abody)
      abody) )

(defun body- (body1 &rest bodies)	;body1 - body2 = -((- body) + body2)
   (let ((newbody body1) (csg-list))
      (send body1 :evert)
      (unwind-protect
	      (while bodies
		 (push (send (car bodies) :copy-csg) csg-list)
	         (setq newbody (compose-body newbody (pop bodies) ':outside))
		 (setq (newbody . evertedp) t) )
	      (send body1 :evert))
      (send newbody :evert)
      (send newbody :init)
      (send newbody :csg
		(cons '- (list (cons (send body1 :copy-csg)
				     (nreverse csg-list)))))
      (set-original-face newbody)
      newbody))

;(defun body-interference (body1 body2)
;    (send body1 :worldcoords)
;    (send body2 :worldcoords)
;    (send body1 :intersectp body2))

;;
;; check body interference for every combination of bodies
;; and return a list of interfering bodies
;;

(defun body-interference (&rest bodies)
   (send-all bodies :worldcoords)
   (let (b1 b2 interference-list)
     (while (rest bodies)
        (setq b1 (pop bodies))
        (dolist (b bodies)
	  (if (send b1 :intersectp b)
	      (push (list b b1) interference-list))) )
     interference-list))

(defmethod plane 
 (:box () (instance bounding-box :init
			 (float-vector -1.0e30 -1.0e30 -1.0e30)
			 (float-vector 1.0e30 1.0e30 1.0e30))))

(defclass semi-space :super plane :slots ())

(defmethod semi-space
 (:edges () nil)
 (:faces () (list self))
 (:box (&optional (tolerance 0.0)) (instance bounding-box :init2
			 (float-vector -1.0e30 -1.0e30 -1.0e30)
			 (float-vector 1.0e30 1.0e30 1.0e30)))
 (:insidep (pnt)
   (if  (< (send self :distance pnt) 0.0) ':inside ':outside))
 (:primitive-face (&optional x) self)
 (:body (&optional x) nil)
 (:id (&optional x) nil)
 (:on-edge (point &optional tol) nil)
)


(defun body/ (body1 pln)
   (let* ((body2 (instance semi-space :init (pln . normal) (pln . distance))))
      (compose-body body1 body2 ':inside)))
 
(provide :compose "@(#)$Id$")