! Copyright (C) 2008 Jeff Bigot ! See http://factorcode.org/license.txt for BSD license. USING: accessors arrays assocs combinators kernel fry math math.constants math.functions math.libm math.order math.vectors math.matrices math.parser namespaces prettyprint sequences sequences.deep sets slots sorting tools.time vars continuations words opengl opengl.gl colors adsoda.solution2 adsoda.combinators opengl.demo-support values tools.walker ; IN: adsoda DEFER: combinations VAR: pv ! ------------------------------------------------------------- ! global values VALUE: remove-hidden-solids? VALUE: VERY-SMALL-NUM VALUE: ZERO-VALUE VALUE: MAX-FACE-PER-CORNER t \ remove-hidden-solids? set-value 0.0000001 \ VERY-SMALL-NUM set-value 0.0000001 \ ZERO-VALUE set-value 4 \ MAX-FACE-PER-CORNER set-value ! ------------------------------------------------------------- ! sequence complement : with-pv ( i quot -- ) [ swap >pv call ] with-scope ; inline : dimension ( array -- x ) length 1 - ; inline : change-last ( seq quot -- ) [ [ dimension ] keep ] dip change-nth ; inline ! ------------------------------------------------------------- ! light ! ------------------------------------------------------------- TUPLE: light name { direction array } color ; : ( -- tuple ) light new ; ! ------------------------------------------------------------- ! halfspace manipulation ! ------------------------------------------------------------- : constant+ ( v x -- w ) '[ [ _ + ] change-last ] keep ; : translate ( u v -- w ) dupd v* sum constant+ ; : transform ( u matrix -- w ) [ swap m.v ] 2keep ! compute new normal vector [ [ [ abs ZERO-VALUE > ] find ] keep ! find a point on the frontier ! be sure it's not null vector last ! get constant swap /f neg swap ! intercept value ] dip flip nth [ * ] with map ! apply intercep value over v* sum neg suffix ! add value as constant at the end of equation ; : position-point ( halfspace v -- x ) -1 suffix v* sum ; inline : point-inside-halfspace? ( halfspace v -- ? ) position-point VERY-SMALL-NUM > ; : point-inside-or-on-halfspace? ( halfspace v -- ? ) position-point VERY-SMALL-NUM neg > ; : project-vector ( seq -- seq ) pv> [ head ] [ 1 + tail ] 2bi append ; : get-intersection ( matrice -- seq ) [ 1 tail* ] map flip first ; : islenght=? ( seq n -- seq n ? ) 2dup [ length ] [ = ] bi* ; : compare-nleft-to-identity-matrix ( seq n -- ? ) [ [ head ] curry map ] keep identity-matrix m- flatten [ abs ZERO-VALUE < ] all? ; : valid-solution? ( matrice n -- ? ) islenght=? [ compare-nleft-to-identity-matrix ] [ 2drop f ] if ; inline : intersect-hyperplanes ( matrice -- seq ) [ solution dup ] [ first dimension ] bi valid-solution? [ get-intersection ] [ drop f ] if ; ! ------------------------------------------------------------- ! faces ! ------------------------------------------------------------- TUPLE: face { halfspace array } touching-corners adjacent-faces ; : ( v -- tuple ) face new swap >>halfspace ; : flip-face ( face -- face ) [ vneg ] change-halfspace ; : erase-face-touching-corners ( face -- face ) f >>touching-corners ; : erase-face-adjacent-faces ( face -- face ) f >>adjacent-faces ; : faces-intersection ( faces -- v ) [ halfspace>> ] map intersect-hyperplanes ; : face-translate ( face v -- face ) [ translate ] curry change-halfspace ; inline : face-transform ( face m -- face ) [ transform ] curry change-halfspace ; inline : face-orientation ( face -- x ) pv> swap halfspace>> nth sgn ; : backface? ( face -- face ? ) dup face-orientation 0 <= ; : pv-factor ( face -- f face ) halfspace>> [ pv> swap nth [ * ] curry ] keep ; inline : suffix-touching-corner ( face corner -- face ) [ suffix ] curry change-touching-corners ; inline : real-face? ( face -- ? ) [ touching-corners>> length ] [ halfspace>> dimension ] bi >= ; : (add-to-adjacent-faces) ( face face -- face ) over adjacent-faces>> 2dup member? [ 2drop ] [ swap suffix >>adjacent-faces ] if ; : add-to-adjacent-faces ( face face -- face ) 2dup = [ drop ] [ (add-to-adjacent-faces) ] if ; : update-adjacent-faces ( faces corner -- ) '[ [ _ suffix-touching-corner drop ] each ] keep 2 among [ [ first ] keep second [ add-to-adjacent-faces drop ] 2keep swap add-to-adjacent-faces drop ] each ; inline : face-project-dim ( face -- x ) halfspace>> length 2 - ; : apply-light ( color light normal -- u ) over direction>> v. neg dup 0 > [ [ color>> swap ] dip [ * ] curry map v+ [ 1 min ] map ] [ 2drop ] if ; : enlight-projection ( array face -- color ) ! array = lights + ambient color [ [ third ] [ second ] [ first ] tri ] [ halfspace>> project-vector normalize ] bi* [ apply-light ] curry each v* ; : (intersection-into-face) ( face-init face-adja quot -- face ) [ [ [ pv-factor ] bi@ roll [ map ] 2bi@ v- ] 2keep [ touching-corners>> ] bi@ [ swap [ = ] curry find nip f = ] curry find nip ] dip over [ call dupd point-inside-halfspace? [ vneg ] unless ] [ 3drop f ] if ; inline : intersection-into-face ( face-init face-adja -- face ) [ [ project-vector ] bi@ ] (intersection-into-face) ; : intersection-into-silhouette-face ( face-init face-adja -- face ) [ ] (intersection-into-face) ; : intersections-into-faces ( face -- faces ) clone dup adjacent-faces>> [ intersection-into-face ] with map sift ; : (face-silhouette) ( face -- faces ) clone dup adjacent-faces>> [ backface? [ intersection-into-silhouette-face ] [ 2drop f ] if ] with map sift ; inline : face-silhouette ( face -- faces ) backface? [ drop f ] [ (face-silhouette) ] if ; ! -------------------------------- ! solid ! ------------------------------------------------------------- TUPLE: solid dimension silhouettes faces corners adjacencies-valid color name ; : ( -- tuple ) solid new ; : suffix-silhouettes ( solid silhouette -- solid ) [ suffix ] curry change-silhouettes ; : suffix-face ( solid face -- solid ) [ suffix ] curry change-faces ; : suffix-corner ( solid corner -- solid ) [ suffix ] curry change-corners ; : erase-solid-corners ( solid -- solid ) f >>corners ; : erase-silhouettes ( solid -- solid ) dup dimension>> f >>silhouettes ; : filter-real-faces ( solid -- solid ) [ [ real-face? ] filter ] change-faces ; : initiate-solid-from-face ( face -- solid ) face-project-dim swap >>dimension ; : erase-old-adjacencies ( solid -- solid ) erase-solid-corners [ dup [ erase-face-touching-corners erase-face-adjacent-faces drop ] each ] change-faces ; : point-inside-or-on-face? ( face v -- ? ) [ halfspace>> ] dip point-inside-or-on-halfspace? ; : point-inside-face? ( face v -- ? ) [ halfspace>> ] dip point-inside-halfspace? ; : point-inside-solid? ( solid point -- ? ) [ faces>> ] dip [ point-inside-face? ] curry all? ; inline : point-inside-or-on-solid? ( solid point -- ? ) [ faces>> ] dip [ point-inside-or-on-face? ] curry all? ; inline : unvalid-adjacencies ( solid -- solid ) erase-old-adjacencies f >>adjacencies-valid erase-silhouettes ; : add-face ( solid face -- solid ) suffix-face unvalid-adjacencies ; : cut-solid ( solid halfspace -- solid ) add-face ; : slice-solid ( solid face -- solid1 solid2 ) [ [ clone ] bi@ flip-face add-face [ "/outer/" append ] change-name ] 2keep add-face [ "/inner/" append ] change-name ; ! ------------- : add-silhouette ( solid -- solid ) dup ! find-adjacencies faces>> { } [ face-silhouette append ] reduce sift swap >>faces over dimension>> >>dimension over name>> " silhouette " append pv> number>string append >>name ! ensure-adjacencies suffix-silhouettes ; inline : find-silhouettes ( solid -- solid ) { } >>silhouettes dup dimension>> [ [ add-silhouette ] with-pv ] each ; : ensure-silhouettes ( solid -- solid ) dup silhouettes>> [ f = ] all? [ find-silhouettes ] when ; ! ------------ : corner-added? ( solid corner -- ? ) ! add corner to solid if it is inside solid [ ] [ point-inside-or-on-solid? ] [ swap corners>> member? not ] 2tri and [ suffix-corner drop t ] [ 2drop f ] if ; : process-corner ( solid faces corner -- ) swapd [ corner-added? ] keep swap ! test if corner is inside solid [ update-adjacent-faces ] [ 2drop ] if ; : compute-intersection ( solid faces -- ) dup faces-intersection dup f = [ 3drop ] [ process-corner ] if ; : test-faces-combinaisons ( solid n -- ) [ dup faces>> ] dip among [ compute-intersection ] with each ; : compute-adjacencies ( solid -- solid ) dup dimension>> [ >= ] curry [ keep swap ] curry MAX-FACE-PER-CORNER swap [ [ test-faces-combinaisons ] 2keep 1 - ] while drop ; : find-adjacencies ( solid -- solid ) erase-old-adjacencies compute-adjacencies filter-real-faces t >>adjacencies-valid ; : ensure-adjacencies ( solid -- solid ) dup adjacencies-valid>> [ find-adjacencies ] unless ensure-silhouettes ; : (non-empty-solid?) ( solid -- ? ) [ dimension>> ] [ corners>> length ] bi < ; : non-empty-solid? ( solid -- ? ) ensure-adjacencies (non-empty-solid?) ; : compare-corners-roughly ( corner corner -- ? ) 2drop t ; ! : remove-inner-faces ( -- ) ; : face-project ( array face -- seq ) backface? [ 2drop f ] [ [ enlight-projection ] [ initiate-solid-from-face ] [ intersections-into-faces ] tri >>faces swap >>color ] if ; : solid-project ( lights ambient solid -- solids ) ensure-adjacencies [ color>> ] [ faces>> ] bi [ 3array ] dip [ face-project ] with map sift [ ensure-adjacencies ] map ; : (solid-move) ( solid v move -- solid ) curry [ map ] curry [ dup faces>> ] dip call drop unvalid-adjacencies ; inline : solid-translate ( solid v -- solid ) [ face-translate ] (solid-move) ; : solid-transform ( solid m -- solid ) [ face-transform ] (solid-move) ; : find-corner-in-silhouette ( s1 s2 -- elt bool ) pv> swap silhouettes>> nth swap corners>> [ point-inside-solid? ] with find swap ; : valid-face-for-order ( solid point -- face ) [ point-inside-face? not ] [ drop face-orientation 0 = not ] 2bi and ; : check-orientation ( s1 s2 pt -- int ) [ nip faces>> ] dip [ valid-face-for-order ] curry find swap [ face-orientation ] [ drop f ] if ; : (order-solid) ( s1 s2 -- int ) 2dup find-corner-in-silhouette [ check-orientation ] [ 3drop f ] if ; : order-solid ( solid solid -- i ) 2dup (order-solid) [ 2nip ] [ swap (order-solid) [ neg ] [ f ] if* ] if* ; : subtract ( solid1 solid2 -- solids ) faces>> swap clone ensure-adjacencies ensure-silhouettes [ swap slice-solid drop ] curry map [ non-empty-solid? ] filter [ ensure-adjacencies ] map ; inline ! ------------------------------------------------------------- ! space ! ------------------------------------------------------------- TUPLE: space name dimension solids ambient-color lights ; : ( -- space ) space new ; : suffix-solids ( space solid -- space ) [ suffix ] curry change-solids ; inline : suffix-lights ( space light -- space ) [ suffix ] curry change-lights ; inline : clear-space-solids ( space -- space ) f >>solids ; : space-ensure-solids ( space -- space ) [ [ ensure-adjacencies ] map ] change-solids ; : eliminate-empty-solids ( space -- space ) [ [ non-empty-solid? ] filter ] change-solids ; : projected-space ( space solids -- space ) swap dimension>> 1 - swap >>dimension swap >>solids ; : get-silhouette ( solid -- silhouette ) silhouettes>> pv> swap nth ; : solid= ( solid solid -- ? ) [ corners>> ] same? ; : space-apply ( space m quot -- space ) curry [ map ] curry [ dup solids>> ] dip [ call ] [ 2drop ] recover drop ; inline : space-transform ( space m -- space ) [ solid-transform ] space-apply ; : space-translate ( space v -- space ) [ solid-translate ] space-apply ; : describe-space ( space -- ) solids>> [ [ corners>> [ pprint ] each ] [ name>> . ] bi ] each ; : clip-solid ( solid solid -- solids ) [ ] [ solid= not ] [ order-solid -1 = ] 2tri and [ get-silhouette subtract ] [ drop 1array ] if ; : (solids-silhouette-subtract) ( solids solid -- solids ) [ clip-solid append ] curry { } -rot each ; inline : solids-silhouette-subtract ( solids i solid -- solids ) ! solids is an array of 1 solid arrays [ (solids-silhouette-subtract) ] curry map-but ; inline : remove-hidden-solids ( space -- space ) ! We must include each solid in a sequence because ! during substration ! a solid can be divided in more than on solid [ [ [ 1array ] map ] [ length ] [ ] tri [ solids-silhouette-subtract ] 2each { } [ append ] reduce ] change-solids eliminate-empty-solids ! TODO include into change-solids ; : space-project ( space i -- space ) [ [ clone remove-hidden-solids? [ remove-hidden-solids ] when dup [ solids>> ] [ lights>> ] [ ambient-color>> ] tri [ rot solid-project ] 2curry map [ append ] { } -rot each ! TODO project lights projected-space ! remove-inner-faces ! eliminate-empty-solids ] with-pv ] [ 3drop ] recover ; inline : middle-of-space ( space -- point ) solids>> [ corners>> ] map concat [ [ ] [ v+ ] map-reduce ] [ length ] bi v/n ; ! ------------------------------------------------------------- ! 3D rendering ! ------------------------------------------------------------- : face-reference ( face -- halfspace point vect ) [ halfspace>> ] [ touching-corners>> first ] [ touching-corners>> second ] tri over v- ; : theta ( v halfspace point vect -- v x ) [ [ over ] dip v- ] dip [ cross dup norm >float ] [ v. >float ] 2bi fatan2 -rot v. 0 < [ neg ] when ; : ordered-face-points ( face -- corners ) [ touching-corners>> 1 head ] [ touching-corners>> 1 tail ] [ face-reference [ theta ] 3curry ] tri { } map>assoc sort-values keys append ; inline : point->GL ( point -- ) gl-vertex ; : points->GL ( array -- ) do-cycle [ point->GL ] each ; : face->GL ( face color -- ) [ ordered-face-points ] dip [ first3 1.0 glColor4d GL_POLYGON [ [ point->GL ] each ] do-state ] curry [ 0 0 0 1 glColor4d GL_LINE_LOOP [ [ point->GL ] each ] do-state ] bi ; inline : solid->GL ( solid -- ) [ faces>> ] [ color>> ] bi [ face->GL ] curry each ; inline : space->GL ( space -- ) solids>> [ solid->GL ] each ;