lambdaway
::
pf
5
|
list
|
login
|
load
|
|
{{hide} {def W3D.open {lambda {:w :h} svg {@ width=":w" height=":h" style="box-shadow:0 0 8px #000; background:#666; border:0px solid #fff;"}}} {def W3D.frame {lambda {:w :h} g {@ id="forms" transform="translate({/ :w 2},{/ :h 2})"}}} {def W3D.parameter {lambda {:name :id :val :min :max :step} {center :name : {input {@ id=":id" type="text" value=":val" size="5" style="background:#444; color:#fff; border:0px solid"}} {input {@ type="range" min=":min" max=":max" value=":val" step=":step" style="width:110px; vertical-align:middle;" oninput="W3D.camera(':id', this.value)"}}}}} {def W3D.control {pre {@ style="position:absolute; top:40px; left:30px; background:transparent; "} {W3D.parameter R/ox rox 320 0 360 10} {W3D.parameter R/oy roy 0 0 360 10} {W3D.parameter R/oz roz 40 0 360 10} {W3D.parameter scale scale 1.0 0.1 2.0 0.1} }} {def W3D.addForm {lambda {:w :col :m :points} {path {@ d="{W3D.polyline :m :points}" fill="transparent" stroke-width=":w" stroke=":col"}}}} {def myW3D {lambda {:rx :ry :rz :sc :pers} ;; {myW3D -40 0 75 1.0 500} {div {@ id="W3D"} {W3D :rx :ry :rz :sc :pers} } {W3D.control} }} } {script var W3D = (function() { var CAM = []; // W3D global : 3D camera var camera = function(id,val) { var rox = (id === 'rox')? val : document.getElementById("rox").value; var roy = (id === 'roy')? val : document.getElementById("roy").value; var roz = (id === 'roz')? val : document.getElementById("roz").value; var sca = (id === 'scale')? val : document.getElementById("scale").value; document.getElementById(id).value = val; var newWorld = "{W3D " + rox + " " + roy + " " + roz + " " + sca + " " + " 500}"; document.getElementById('W3D').innerHTML = LAMBDATALK.eval_forms( newWorld ) }; function init( rox,roy,roz,scal,pers ) { CAM = [ [1,0,0,0], [0,1,0,0], [0,0,1,0], [0,0,0,1] ]; CAM = product( CAM, perspective( pers ) ); CAM = product( CAM, rotate( 'OX', rox ) ); CAM = product( CAM, rotate( 'OY', roy ) ); CAM = product( CAM, rotate( 'OZ', roz ) ); CAM = product( CAM, scale( scal, -scal, -scal ) ); } function rotate( axe, a ) { // R4 -> R4 var a = 2*Math.PI/360*a, cosa = Math.cos(a), sina = Math.sin(a), m = ''; if (axe == 'OX') m = [ [1,0,0,0], [0,cosa,-sina,0], [0,sina,cosa,0], [0,0,0,1] ]; if (axe == 'OY') m = [ [cosa,0,sina,0], [0,1,0,0], [-sina,0,cosa,0], [0,0,0,1] ]; if (axe == 'OZ') m = [ [cosa,-sina,0,0], [sina,cosa,0,0], [0,0,1,0], [0,0,0,1] ]; return m; } function scale( sx, sy, sz ) { // R4 -> R4 return [ [sx,0,0,0], [0,sy,0,0], [0,0,sz,0], [0,0,0,1] ]; } function translate( tx, ty, tz ) { // R4 -> R4 return [ [1,0,0,tx], [0,1,0,ty], [0,0,1,tz], [0,0,0,1] ]; } function perspective ( f ) { // R4 -> R4 ( x=X, y=Y, z=Z, w=Z/f+1 ) return [ [1,0,0,0], [0,1,0,0], [0,0,1,0], [0,0,1/f,1] ]; } function product ( m1, m2 ) { // 4x4 matrix * 4x4 matrix -> 4x4 matrix var m = []; for (var i=0; i< 4; i++) { var mm = []; for (var j=0; j< 4; j++) { var s = 0; for (var k=0; k< 4; k++) { s += m1[i][k]*m2[k][j]; } mm[j] = s; } m[i] = mm; } return m; } function transform ( m, p0 ) { // apply m on a 4D point var p1 = []; for (var i=0; i< 4; i++) { var temp = 0; for (var j=0; j< 4; j++) temp += m[i][j]*p0[j]; p1[i] = temp; } return p1 // return a R4 point } function polyline ( t , f ) { var NEAR = -400, FAR = 500; var m = CAM; m = product( m, rotate( 'OX', t[0] ) ); m = product( m, rotate( 'OY', t[1] ) ); m = product( m, rotate( 'OZ', t[2] ) ); // console.log( t ) ; // m = product( m, scale( t[3], t[3], t[3] ) ); var tab = []; for (var i=0; i< f.length; i++) { var p = transform( m, f[i] ); tab[i] = [p[0]/p[3], p[1]/p[3], p[2]/p[3]]; } // draw only segments entirely in clipping planes for (var poly ='', i=0; i< tab.length-1; i++) { if ( tab[i][2] > NEAR && tab[i][2] < FAR && tab[i+1][2] > NEAR && tab[i+1][2] < FAR ) poly += 'M ' + tab[i][0] + ',' + tab[i][1] + ' ' + tab[i+1][0] + ',' + tab[i+1][1] + ' '; } return poly // SVG formated } // de casteljau var interpol = function(a,b,t) { return [ a[0]*(1-t)+b[0]*t, a[1]*(1-t)+b[1]*t, a[2]*(1-t)+b[2]*t, a[3]*(1-t)+b[3]*t ] }; var sub = function(arr,acc,t) { if (arr.length < 2) return acc; else { acc.push( interpol(arr[0],arr[1],t) ); return sub( arr.slice(1,),acc,t ) } }; var split = function(arr,acc,t,lr) { if (arr.length < 1) return acc else { if (lr) acc.push( arr[0] ) else acc.unshift( arr[arr.length-1] ) return split( sub(arr,[],t), acc, t, lr ) } }; var stretch = function(arr,t0,t1) { return split( split(arr,[],t0,false), [], t1, true) }; var blossom = function(arr,n) { if (n < 1) return arr else return [blossom( split(arr,[],0.5,true), n-1 ), blossom( split(arr,[],0.5,false), n-1 )] }; // PUBLIC FUNCTIONS return { init:init, camera:camera, polyline:polyline, stretch:stretch, blossom:blossom }; })(); // end W3D LAMBDATALK.DICT['W3D.init'] = function() { // {W3D.init3D ox oy oz sc pers} var args = LAMBDATALK.supertrim(arguments[0]).split(" "); return W3D.init( args[0], args[1], args[2], args[3], args[4] ) }; LAMBDATALK.DICT['W3D.polyline'] = function() { // {W3D.polyline [[...]] p} var args = LAMBDATALK.supertrim(arguments[0]).split(" "); var m = JSON.parse(args.shift()); var p = JSON.parse( args.join(" ")); return W3D.polyline(m,p) }; LAMBDATALK.DICT['W3D.stretch'] = function() { // {stretch t0 t1 [[...]]} var args = LAMBDATALK.supertrim(arguments[0]).split(" "); var t0 = parseFloat( args.shift()); var t1 = parseFloat( args.shift()); var arr = JSON.parse(args.join(" ")); return JSON.stringify(W3D.stretch(arr,t0,t1)) }; LAMBDATALK.DICT['W3D.blossom'] = function() { // {blossom n [[...]]} var args = LAMBDATALK.supertrim(arguments[0]).split(" "); var n = parseFloat(args.shift()); return "["+JSON.stringify(W3D.blossom( JSON.parse(args.join(" ")), n )) .replace( /\[{2,}/g, "[" ) .replace( /\]{2,}/g, "]" )+"]" }; } ;; fin lib_W3D {myW3D -40 0 75 1.0 500} _h2 1) creating the 3D World _p The picture on top of this page is created using this code: {pre '{myW3D -40 0 75 1.0 500} // ox oy oz scal pers } _p where {code myW3D, W3D & W3D.control} functions are defined in the [[lib_W3D]] library and used as follows: {pre '{def myW3D {lambda {:rx :ry :rz :sc :pers} ;; {myW3D -40 0 75 1.0 500} {div {@ id="W3D"} {W3D :rx :ry :rz :sc :pers} } {W3D.control} }} '{def W3D {lambda {:ox :oy :oz :scal :pers} {{W3D.open 580 580} {{W3D.frame 580 580} {W3D.init :ox :oy :oz :scal :pers} {W3D.addForm 1 #fff [0,0,0,1] {W3D.point 0 0 0 1 150}} {W3D.addForm 2 #888 [0,0,0,1] {W3D.cube 150}} {W3D.addForm 2 #fff [0,0,0,1] {W3D.point -170 -170 -150 1 10}} {S.map {lambda {:i} {W3D.addForm 1 rgb(:i,{- 250 :i},{- 250 :i}) [0,0,:i,1] {W3D.blossom 4 {cubic}}} } {S.serie 0 270 3}} {W3D.addForm 9 rgba(255,255,255,0.5) [0,0,90,1] {W3D.blossom 5 {W3D.stretch -0.1 1.8 {cubic}}}} }}}} -> {def W3D {lambda {:ox :oy :oz :scal :pers} {{W3D.open 580 580} {{W3D.frame 580 580} {W3D.init :ox :oy :oz :scal :pers} {W3D.addForm 1 #fff [0,0,0,1] {W3D.point 0 0 0 1 150}} {W3D.addForm 2 #888 [0,0,0,1] {W3D.cube 150}} {W3D.addForm 2 #fff [0,0,0,1] {W3D.point -170 -170 -150 1 10}} {S.map {lambda {:i} {W3D.addForm 1 rgb(:i,{- 250 :i},{- 250 :i}) [0,0,:i,1] {W3D.blossom 4 {cubic}}} } {S.serie 0 270 3}} {W3D.addForm 9 rgba(255,255,255,0.5) [0,0,90,1] {W3D.blossom 5 {W3D.stretch -0.1 1.8 {cubic}}}} }}}} } _p The {code W3D.addForm} function is used as follows: {pre '{W3D.addForm width // line width/thickness color // line color #rgb or rgb(r,g,b) [rox,roy,roz,scale] // transformations [p0,p1,...,pn] // an array of points [x,y,z,w] } } _h2 2) creating shapes _h3 2.1) 3D points _p Points are defined as {code [x,y,z,w]} arrays and displayed as three small crossing segments. By default the fourth value {code w} is set to {b 1}. Rational curves, for instance conics, have this fourth value different from 1. {pre '{def W3D.point {lambda {:x :y :z :w :s} [[{- :x :s},:y,:z,:w], [{+ :x :s},:y,:z,:w], [:x,:y,:z,:w], [:x,{- :y :s},:z,:w], [:x,{+ :y :s},:z,:w], [:x,:y,:z,:w], [:x,:y,{- :z :s},:w], [:x,:y,{+ :z :s},:w]]}} -> {def W3D.point {lambda {:x :y :z :w :s} [[{- :x :s},:y,:z,:w],[{+ :x :s},:y,:z,:w],[:x,:y,:z,:w], [:x,{- :y :s},:z,:w],[:x,{+ :y :s},:z,:w],[:x,:y,:z,:w], [:x,:y,{- :z :s},:w],[:x,:y,{+ :z :s},:w]]}} } _h3 2.2) 3D polylines _p Polylines are defined as arrays of points, {code [p0,p1,...,pn]}. For instance the "world" cube is a polyline built on 9 segments. {pre '{def W3D.cube {lambda {:d} [[-:d,-:d,-:d,1], [:d,-:d,-:d,1], [:d,:d,-:d,1], [-:d,:d,-:d,1], [-:d,:d,:d,1], [:d,:d,:d,1], [:d,-:d,:d,1], [-:d,-:d,:d,1], [-:d,-:d,-:d,1]] }} -> {def W3D.cube {lambda {:d} [[-:d,-:d,-:d,1], [:d,-:d,-:d,1], [:d,:d,-:d,1], [-:d,:d,-:d,1], [-:d,:d,:d,1], [:d,:d,:d,1], [:d,-:d,:d,1], [-:d,-:d,:d,1], [-:d,-:d,-:d,1]] }} } _h3 2.3) 3D rational bézier curves _p Rational Bézier curves are controlled by an array of control points, {code [p0,p1,...,pn]} and a number defining the level of recursion. The {code W3D.blossom} function applies the {b de Casteljau} algorithm, blossoms a tree of polylines and returns the concatenation of these polylines. Bézier curves are defined in an interval set to {code [0,1]}. The {code W3D.stretch} function modifies this interval to any {code [a,b]} values. {pre '{def cubic [ [-150,-150,-150,1], [ 150,-150,-150,1], [ 150,-150, 150,1], [ 150, 150, 150,1] ] } -> {def cubic [[-150,-150,-150,1],[150,-150,-150,1],[150,-150,150,1],[150,150,150,1]] } } °°° _p It's useful to precalculate once the blossomed cubic: {pre '{def blossomed_cubic4 {W3D.blossom 4 {cubic}}} -> '{def blossomed_cubic4 {W3D.blossom 4 {cubic}}} '{def blossomed_cubic5 {W3D.blossom 5 {W3D.stretch -0.1 1.8 {cubic}}}} -> '{def blossomed_cubic5 {W3D.blossom 5 {W3D.stretch -0.1 1.8 {cubic}}}} } °°° {cubic} {hr} {S.replace \],\[ by ] [ in {W3D.blossom 2 {cubic}} } _p More to come, pSurfaces, diagonales, ... _p {i alain marty 2025/01/28} {style body, #page_frame, #page_content, .page_menu { border:0 solid #444; box-shadow:0 0 0; background:#444; color:#fff; } pre { background:#666; padding:5px;} }
lambdaway v.20211111