This release enhances 3D rendering with GraphicsComplex
now supporting VertexNormals
for improved lighting accuracy and dynamic geometry options in Polygon
for creating complex shapes. Developer tools are improved with shared JavaScript libraries, a global CLI for easy notebook access, and streamlined cell evaluation and management features.
const balloonContainer = document.getElementById("balloon-container"); function random(num) { return Math.floor(Math.random() * num); } function getRandomStyles() { var r = random(255); var g = random(255); var b = random(255); var mt = random(200); var ml = random(50); var dur = random(5) + 5; return ` background-color: rgba(${r},${g},${b},0.7); color: rgba(${r},${g},${b},0.7); box-shadow: inset -7px -3px 10px rgba(${r - 10},${g - 10},${b - 10},0.7); margin: ${mt}px 0 0 ${ml}px; animation: float ${dur}s ease-in infinite `; } function createBalloons(num) { for (var i = num; i > 0; i--) { var balloon = document.createElement("div"); balloon.className = "balloon"; balloon.style.cssText = getRandomStyles(); balloonContainer.append(balloon); } } function removeBalloons() { balloonContainer.style.opacity = 0; setTimeout(() => { balloonContainer.remove() }, 500) } createBalloons(10); setTimeout(removeBalloons, 15000); return '';
GraphicsComplex now supports VertexNormals
It was an old bug, that provided normals were ignored, which leaded to some visual artifacts in terms of lighting. Looks at the differece now
(*GB[*){{(*VB[*)(FrontEndRef["f9b15308-6ceb-467a-a96d-df7639cc80ad"])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRCeEJBwK8rPK3HNS3GtSE0uLUlMykkNVgEKp1kmGZoaG1jomiWnJumamJkn6iZamqXopqSZmxlbJidbGCSmAACH2hYR"*)(*]VB*)(*|*),(*|*)(*VB[*)(Graphics[Arrow[{{0, 0}, {1, 0}}], ImageSize -> 50, ImagePadding -> None])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KWnMIB4HkHAvSizIyEwuhsizAgnHoqL88jQmmHKfzOISVF4mkGYAE2jijJjiQaU5qcWcQIZnbmJ6anBmVWqmEaYCHpiCgMSUlMy8dLCMX35eKgCWRCRi"*)(*]VB*)(*|*),(*|*)(*VB[*)(FrontEndRef["37227be2-25df-4d3c-a000-35796369ef66"])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRCeEJBwK8rPK3HNS3GtSE0uLUlMykkNVgEKG5sbGZknpRrpGpmmpOmapBgn6yYaGBjoGpuaW5oZm1mmppmZAQB7PxUY"*)(*]VB*)}}(*]GB*)
Dynamic indexed & non-indexed geometry for Polygon
There is no such thing defined in standard library of Wolfram Language, but we did bring it to you from WebGL world, since it may come handy for complex dynamic 3D geometry.
Non-indexed
The idea now, it that you can define starting and ending index for all your triangles using Polygon
inside GraphicsComplex
maxIndex = 312; EventHandler[InputRange[1, 312, 1, 312], (maxIndex = #) &] With[{vertices = (*VB[*)(Get[FileNameJoin[{".iconized", "iconized-3957.wl"}]])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KWlMIB4fkPBMzs/LrEp1y8xJdcqvyCyQY2CASIKUBpXmpAazAhk+iUmpOcEcQFZYalFJZnJqMQADMRNT"*)(*]VB*)}, GraphicsComplex[vertices, { Polygon[1, maxIndex // Offload] }] // Graphics3D ]
(*VB[*)(EventObject[<|"Id" -> "1febcf3d-7f14-4e31-a718-4d1768d67879", "Initial" -> 312, "View" -> "78b77dc8-871b-480a-a4a7-44576564267a"|>])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRCeEJBwK8rPK3HNS3GtSE0uLUlMykkNVgEKm1skmZunJFvoWpgbJumaWBgk6iaaJJrrmpiYmpuZmpkYmZknAgB9RhT2"*)(*]VB*)
(*VB[*)(FrontEndRef["83dd6324-65b2-4296-814d-1d9ccb12fd8d"])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRCeEJBwK8rPK3HNS3GtSE0uLUlMykkNVgEKWxinpJgZG5nompkmGemaGFma6VoYmqToGqZYJicnGRqlpVikAAB30xV5"*)(*]VB*)
It means you can specify only the range of your geometry, without indexes. The benifit of this approach, you can use fixed length buffer for vertices and limit your drawing range using two arguments of Polygon.
Indexed geometry
This is more a traditional way of defining 3D shapes. One can morph one complicated shape into another!
Have a look at this dynamic parameteric plot
sample[t_] := With[{ complex = ParametricPlot3D[ (1 - t) * { (2 + Cos[v]) * Cos[u], (2 + Cos[v]) * Sin[u], Sin[v] } + t * { 1.16^v * Cos[v] * (1 + Cos[u]), -1.16^v * Sin[v] * (1 + Cos[u]), -2 * 1.16^v * (1 + Sin[u]) + 1.0 }, {u, 0, 2\[Pi]}, {v, -\[Pi], \[Pi]}, MaxRecursion -> 2, Mesh -> None ][[1, 1]] }, { complex[[1]], Cases[complex[[2]], _Polygon, 6] // First // First, complex[[3, 2]] } ] (* Now make a scene *) LeakyModule[{ vertices, normals, indices }, { EventHandler[InputRange[0,1,0.1,0], Function[value, With[{res = sample[value]}, normals = res[[3]]; indices = res[[2]]; vertices = res[[1]]; ]; ]], {vertices, indices, normals} = sample[0]; Graphics3D[{ MeshMaterial[MeshToonMaterial[]], Gray, SpotLight[Red, 5 {1,1,1}], SpotLight[Blue, 5 {-1,-1,1}], SpotLight[Green, 5 {1,-1,1}], PointLight[Magenta, {10,10,10}], GraphicsComplex[vertices // Offload, { Polygon[indices // Offload] }, VertexNormals->Offload[normals, "Static"->True]] }, Lighting->None] } // Column // Panel ]
(*BB[*)(Panel[(*GB[*){{(*VB[*)(EventObject[<|"Id" -> "83d20c8f-e5b4-4fbb-a44d-d015185217fe", "Initial" -> 0, "View" -> "bf44ef25-0eee-4d64-8a64-03abf5de810a"|>])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRCeEJBwK8rPK3HNS3GtSE0uLUlMykkNVgEKJ6WZmKSmGZnqGqSmpuqapJiZ6FokAgkD48SkNNOUVAtDg0QAkN8WLQ=="*)(*]VB*)}(*||*),(*||*){(*VB[*)(Graphics3D[{MeshMaterial[MeshToonMaterial[]], GrayLevel[0.5], SpotLight[RGBColor[1, 0, 0], {5, 5, 5}], SpotLight[RGBColor[0, 0, 1], {-5, -5, 5}], SpotLight[RGBColor[0, 1, 0], {5, -5, 5}], PointLight[RGBColor[1, 0, 1], {10, 10, 10}], GraphicsComplex[Offload[vertices$2642384], {Polygon[Offload[indices$2642384]]}, VertexNormals -> Offload[normals$2642384, "Static" -> True]]}, Lighting -> None])(*,*)(*"1:eJyVkd9OwjAUxgv+AY1GX8HEJwBivDMRE7wYYAbxvrJ2a1J6lrYQeFvfw4vZ07pNBjHIxRf2nbPf+XbO3QfEvEUIMadOXkEmvI1Pl05GmuaZWJj+C++UHZEwNvRfORkzk42pZVpQyQm6tz/uHEBVFd9/EYjbiK2Z1MT/Pp/CNKzNcrCRSDPLT9DqOolHz0OQoAUCBCklNJRpxBn6lRwHrFge3QB+FUUR5J/A1l8JG0Bc8BsIdcQ3H4iIb9cSije/TjaEZS7ZJmwejzflXAJN/H3WTFuxYOa+9zDo9R8H9f3r63Z8PLlNQe1DcJJQyQ6jXTLilWTm2v15d2PYZgJ6SaUJ9SZEheJhyOwcd26py+q9uV6xxpiuz+z2J1Tq3Qko9g0K1J5I"*)(*]VB*)}}(*]GB*)])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KWlMIB4/kAgoyi/LTElN8S8oyczPK05jAElwgCQS81JznPIrIEpBGoNKc1KDwSakJqYEs8LUAADiShU/"*)(*]BB*)
See new examples!
Shared JS libraries
D3.js, THREE.js, KaTeX.js, Marked.js used by many modules of WLJS system are now available as sort of shared libraries. It reduces the size of Javascript code for different plugins as well as gives an access to them from JS cells (you to play with them)
.js const dom = document.createElement('div'); let animation; async function buildScene() { await interpretate.shared.THREE.load(); //here const THREE = interpretate.shared.THREE.THREE; const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera( 25, window.innerWidth / window.innerHeight, 0.1, 1000 ); const renderer = new THREE.WebGLRenderer(); renderer.setSize( 400, 300); dom.appendChild( renderer.domElement ); const geometry = new THREE.BoxGeometry( 1, 1, 1 ); const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } ); const cube = new THREE.Mesh( geometry, material ); scene.add( cube ); camera.position.z = 5 function animate() { cube.rotation.x += 0.01; cube.rotation.y += 0.01; renderer.render( scene, camera ); animation = requestAnimationFrame(animate); } animate(); } this.ondestroy = () => { cancelAnimationFrame(animation); console.log('removed'); } buildScene(); return dom;
const dom = document.createElement('div'); let animation; async function buildScene() { await interpretate.shared.THREE.load(); const THREE = interpretate.shared.THREE.THREE; const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera( 25, window.innerWidth / window.innerHeight, 0.1, 1000 ); const renderer = new THREE.WebGLRenderer(); renderer.setSize( 400, 300); dom.appendChild( renderer.domElement ); const geometry = new THREE.BoxGeometry( 1, 1, 1 ); const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } ); const cube = new THREE.Mesh( geometry, material ); scene.add( cube ); camera.position.z = 5 function animate() { cube.rotation.x += 0.01; cube.rotation.y += 0.01; renderer.render( scene, camera ); animation = requestAnimationFrame(animate); } animate(); } this.ondestroy = () => { cancelAnimationFrame(animation); console.log('removed'); } buildScene(); return dom;
It won't cause double loading, shared libraries manager will take care about loading process
Custom shaders
Using Javascript one can define custom vertex/fragment shader materials
.js function vertexShader() { return ` varying vec3 vUv; void main() { vUv = position; vec4 modelViewPosition = modelViewMatrix * vec4(position, 1.0); gl_Position = projectionMatrix * modelViewPosition; } `; } function fragmentShader() { return ` uniform vec3 colorA; uniform vec3 colorB; varying vec3 vUv; void main() { gl_FragColor = vec4(mix(colorA, colorB, vUv.z), 1.0); } `; } let THREE; interpretate.shared.THREE.load().then(() => { THREE = interpretate.shared.THREE.THREE; }) core.CustomMaterial = async (args, env) => { let uniforms = { colorB: {type: 'vec3', value: new THREE.Color(0xACB6E5)}, colorA: {type: 'vec3', value: new THREE.Color(0x74ebd5)} } return (function() { return new THREE.ShaderMaterial({ uniforms: uniforms, fragmentShader: fragmentShader(), vertexShader: vertexShader(), }); }) }
function vertexShader() { return ` varying vec3 vUv; void main() { vUv = position; vec4 modelViewPosition = modelViewMatrix * vec4(position, 1.0); gl_Position = projectionMatrix * modelViewPosition; } `; } function fragmentShader() { return ` uniform vec3 colorA; uniform vec3 colorB; varying vec3 vUv; void main() { gl_FragColor = vec4(mix(colorA, colorB, vUv.z), 1.0); } `; } let THREE; interpretate.shared.THREE.load().then(() => { THREE = interpretate.shared.THREE.THREE; }) core.CustomMaterial = async (args, env) => { let uniforms = { colorB: {type: 'vec3', value: new THREE.Color(0xACB6E5)}, colorA: {type: 'vec3', value: new THREE.Color(0x74ebd5)} } return (function() { return new THREE.ShaderMaterial({ uniforms: uniforms, fragmentShader: fragmentShader(), vertexShader: vertexShader(), }); }) }
a as it material normal now use
Graphics3D[{ Translate[PolyhedronData["Dodecahedron"][[1]]//N , {-2,-3,0}], MeshMaterial[CustomMaterial[]], Translate[PolyhedronData["Dodecahedron"][[1]]//N , {2,3,0}] }]
(*VB[*)(FrontEndRef["b69687c3-703f-4f4d-80dc-f117c1e6f420"])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRCeEJBwK8rPK3HNS3GtSE0uLUlMykkNVgEKJ5lZmlmYJxvrmhsYp+mapJmk6FoYpCTrphkamicbppqlmRgZAACATxV6"*)(*]VB*)
Support for Image3D preview
We use MarchingCubes method to preview 3D images
Image3D[#] & /@ CellularAutomaton[{14, {2, 1}, {1, 1, 1}}, {{{{1}}}, 0}, 3]; Partition[%, 2] // Grid
(*GB[*){{(*VB[*)(Image3DDump42)(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRCeEJBwK8rPK3HNS3GtSE0uLUlMykkNVgEKm1ikGFlYJCbqGpqnpeiapFkk6SaZJlroppmkppkZpKSkGhgaAwCNNhY5"*)(*]VB*)(*|*),(*|*)(*VB[*)(Image3DDump43)(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRCeEJBwK8rPK3HNS3GtSE0uLUlMykkNVgEKpySaJ5obWCbpmhonp+qaGKWZ6lqkGiXqGpmaJxkkp6QkGhkZAQCMUxX1"*)(*]VB*)}(*||*),(*||*){(*VB[*)(Image3DDump44)(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRCeEJBwK8rPK3HNS3GtSE0uLUlMykkNVgEKWyYbmRsbmZjpJqaYm+qaJJom6lqkmVvomhknmZonJ1saGKWZAQB8FRVU"*)(*]VB*)(*|*),(*|*)(*VB[*)(Image3DDump45)(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRCeEJBwK8rPK3HNS3GtSE0uLUlMykkNVgEKG5gZmVqYWSTrGhgYmOuaGJsZ6Saam1voJpqmJSabpFqmmaaaAwBtjhUf"*)(*]VB*)}}(*]GB*)
CLI
After 2.6.0 version WLJS Notebook Desktop application is available globally from the terminal. Simply type
wljs .
like on the screenshot here
to open the an app in the current directory
Inbox
To show all accumulated messages (prints, warnings, errors) click on a inbox button
EvaluateCell
There is a programmatic way of evaluating printed cells
cell = CellPrint["Red", "Type"->"Input"]; EvaluateCell[cell];
Red
(*VB[*)(RGBColor[1, 0, 0])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRCeGJAIcndyzs/JLwouTyxJzghJzS3ISSxJTWMGyXMgyRcxgMEHeyiDgQHOAAALpBNd"*)(*]VB*)
CellPrint to a new window
There is a new option of CellPrint
to specify the target
cell = CellPrint[ToString[Plot[x,{x,0,1}], StandardForm], "Target"->_ ];
Then you can delete it as normal cell, which will cause closing of a window.
Delete[cell];
Ballon animation by Jemima (codepen)