PixiJS: Affine projection vs bilinear one - projection

Let's say there is an arbitrary composite shape in PixiJS:
I need to project a texture onto it by some kind of a grid warp in a way, so the result would be:
I have tried to do it via PIXI.SimplePlane and it seems that default projection algorithm at this framework is affine transformation. And the result have to be based on quad bi-linear projection.
Yes, PixiJS has additional projections plugin (https://pixijs.io/examples/#/plugin-projection/quad-homo.js), but it seems that output could be controlled just by four corner points. In my case, I need more. At least 10 or even 15.
If I couldn't use projection plugin, maybe there is a way to implement a custom shader?!
let pattern64 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAEACAIAAABK8lkwAAAACXBIWXMAAAAnAAAAJwEqCZFPAAAFIGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDAgNzkuMTYwNDUxLCAyMDE3LzA1LzA2LTAxOjA4OjIxICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOCAoTWFjaW50b3NoKSIgeG1wOkNyZWF0ZURhdGU9IjIwMjItMTAtMDdUMTk6MDI6NTErMDM6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMjItMTAtMDdUMTk6MDI6NTErMDM6MDAiIHhtcDpNb2RpZnlEYXRlPSIyMDIyLTEwLTA3VDE5OjAyOjUxKzAzOjAwIiBkYzpmb3JtYXQ9ImltYWdlL3BuZyIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo0Mjc0YWNkZS05OTYwLTQ3YTQtOTQyOS1kMGQ0MmZkNzAwZDIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NDI3NGFjZGUtOTk2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";
const app = new PIXI.Application({ width: 512, height: 324, antialias: true });
document.body.appendChild(app.view);
app.loader.add('pattern', pattern64).load(inits);
function inits() {
const graphics = new PIXI.Graphics();
let path = [34, 100, 176, 100, 176, 224, 34, 224];
graphics.lineStyle(0);
graphics.beginFill(0xBCBCBC, 1);
graphics.drawPolygon(path);
graphics.endFill();
path = [176, 100, 258, 60, 258, 264, 176, 224];
graphics.lineStyle(0);
graphics.beginFill(0xCDCDCD, 1);
graphics.drawPolygon(path);
graphics.endFill();
path = [258, 60, 422, 104, 422, 220, 256, 264];
graphics.lineStyle(0);
graphics.beginFill(0x606060, 1);
graphics.drawPolygon(path);
graphics.endFill();
path = [422, 104, 478, 104, 478, 220, 422, 220];
graphics.lineStyle(0);
graphics.beginFill(0x909090, 1);
graphics.drawPolygon(path);
graphics.endFill();
app.stage.addChild(graphics);
const texture = app.loader.resources.pattern.texture;
const plane = new PIXI.SimplePlane(texture, 5, 3);
app.stage.addChild(plane);
let buffer = plane.geometry.getBuffer('aVertexPosition');
// graphics.beginFill(0xFF0000);
// graphics.drawRect(buffer.data[20], buffer.data[21], 8, 8);
// graphics.endFill();
//manually set just for this demo
buffer.data[0] = 34;
buffer.data[1] = 100;
buffer.data[2] = 176;
buffer.data[3] = 100;
buffer.data[4] = 258;
buffer.data[5] = 60;
buffer.data[6] = 422;
buffer.data[7] = 104;
buffer.data[8] = 478;
buffer.data[9] = 104;
buffer.data[10] = 34;
buffer.data[11] = 162;
buffer.data[12] = 176;
buffer.data[13] = 162;
buffer.data[14] = 258;
buffer.data[15] = 162;
buffer.data[16] = 422;
buffer.data[17] = 162;
buffer.data[18] = 478;
buffer.data[19] = 162;
buffer.data[20] = 34;
buffer.data[21] = 224;
buffer.data[22] = 176;
buffer.data[23] = 224;
buffer.data[24] = 258;
buffer.data[25] = 264;
buffer.data[26] = 422;
buffer.data[27] = 220;
buffer.data[28] = 478;
buffer.data[29] = 220;
buffer.update();
}
body { margin: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/6.5.5/browser/pixi.js"></script>

Related

Console log showing mismatched value

When I select the first image, the "out" shows undefined & "in" shows the byte array result.
But after selecting the second image, "out" prints the byte array value of previous image & "in" shows the result of second(current) image.
upload(files: any) {
console.log('jj');
const file = files.files[0];
if (!file) return;
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = (event) => {
this.url = reader.result;
var base64Index = this.url.indexOf(';base64,') + ';base64,'.length;
var base64 = this.url.substring(base64Index);
var raw = window.atob(base64);
var rawLength = raw.length;
let imgArray = new Uint8Array(new ArrayBuffer(rawLength));
for (let i = 0; i < rawLength; i++) {
imgArray[i] = raw.charCodeAt(i);
}
this.imgArrayy = imgArray;
console.log("in", this.imgArrayy);
}
console.log("out", this.imgArrayy);
}
Console output:-
How can I get the correct value for each corresponding image?
The readAsDataURL is an async function. That means the code may not execute in order.
The sequence of your code is:
schedule the readAsDataURL
print console.log out
execute the onload
print console.log in
console.log out is run before onLoad. Therefore the console.log out is undefined.
Is it typescript? I am not sure the syntax.
But the idea is change the upload function to async and use await to wait for the completion of file read.
Or you can change upload to async but use FileReaderSync instead. https://www.javascripture.com/FileReaderSync
Then, the sequence of code will be:
schedule the readAsDataURL
wait until readAsDataURL completed
execute the onload & print console.log in
print console.log out
To fix this, you can try:
const obj = {hello: 'world'};
const blob = new Blob([JSON.stringify(obj, null, 2)], {type : 'application/json'});
async function upload(files) {
console.log('jj');
const file = files.files[0];
if (!file) return;
await new Promise((resolve) => {
var reader = new FileReader();
reader.onload = (event) => {
this.url = reader.result;
var base64Index = this.url.indexOf(';base64,') + ';base64,'.length;
var base64 = this.url.substring(base64Index);
var raw = window.atob(base64);
var rawLength = raw.length;
let imgArray = new Uint8Array(new ArrayBuffer(rawLength));
for (let i = 0; i < rawLength; i++) {
imgArray[i] = raw.charCodeAt(i);
}
this.imgArrayy = imgArray;
console.log("in", this.imgArrayy);
resolve(reader.result);
};
reader.readAsDataURL(file);
});
console.log("out", this.imgArrayy);
}
upload({files:[blob]});
output
jj
VM532:20 in Uint8Array(22) [123, 10, 32, 32, 34, 104, 101, 108, 108, 111, 34, 58, 32, 34, 119, 111, 114, 108, 100, 34, 10, 125]
VM532:25 out Uint8Array(22) [123, 10, 32, 32, 34, 104, 101, 108, 108, 111, 34, 58, 32, 34, 119, 111, 114, 108, 100, 34, 10, 125]

I just know how to use for to draw the tree, but now I want to use recursion to draw the tree

I just know how to use for to draw a tree (the tree data is the picture one, the result is picture two), but now I want to use recursion to draw the tree.
Please tell me how change writing style from for to recursive
first input point
//input point
const line_point =[0, 0, 0,
2, 151, 2,
2, 151, 2,
-62, 283, 63,
2, 151, 2,
62, 297, -58,
-62, 283, 63,
-104, 334, 74,
-62, 283, 63,
-58, 338, 45,
62, 297, -58,
67, 403, -55,
62, 297, -58,
105, 365, -86];
take out star point and end point
const star_line_x= new Array();
const star_line_y= new Array();
const star_line_z= new Array();
const end_line_x= new Array();
const end_line_y= new Array();
const end_line_z= new Array();
for (var q=0; q < line_point.length; q+=6){
star_line_x.push(line_point[q]);
}
for (var r=1; r < line_point.length; r+=6){
star_line_y.push(line_point[r]);
}
for (var s=2; s < line_point.length; s+=6){
star_line_z.push(line_point[s]);
}
for (var t=3; t < line_point.length; t+=6){
end_line_x.push(line_point[t]);
}
for (var u=4; u < line_point.length; u+=6){
end_line_y.push(line_point[u]);
}
for (var v=5; v < line_point.length; v+=6){
end_line_z.push(line_point[v]);
}
var cylinder_star_point = new Array();
var cylinder_end_point = new Array();
//star_point end_point
for (var w=0; w < line_point.length/6; w++){
var star_point = new THREE.Vector3 (star_line_x[w],star_line_y[w],star_line_z[w]);
var end_point = new THREE.Vector3 (end_line_x[w],end_line_y[w],end_line_z[w]);
cylinder_star_point.push( star_point);
cylinder_end_point.push( end_point);
}
calculation cylinder high
//calculation cylinder high
var line_len = new Array();
for (var dd=0; dd < line_point.length/6; dd++){
var len_x = Math.pow(end_line_x[dd]-star_line_x[dd],2);
var len_y = Math.pow(end_line_y[dd]-star_line_y[dd],2);
var len_z = Math.pow(end_line_z[dd]-star_line_z[dd],2);
var len_direction = Math.sqrt(len_x+len_y+len_z);
line_len.push(len_direction);//Cylinder high
}
calculation center point
//center_point
const cylinder_center_point= new Array();
for (var bb=0; bb< cylinder_end_point.length; bb++){
var star_set_point = cylinder_star_point[bb];
var end_set_point = cylinder_end_point[bb];
var center_point = end_set_point.clone().add(star_set_point).divideScalar(2);
cylinder_center_point.push(center_point);
}
calculation cylinder direction vector
//cylinder direction
const cylinder_direction= new Array();
for (var cc=0; cc < cylinder_end_point.length; cc++){
var star_direction = cylinder_star_point[cc];
var end_direction = cylinder_end_point[cc];
var center_direction = end_direction.clone().sub(star_direction);
cylinder_direction.push(center_direction);
}
draw cylinder
for (var dd=0; dd <cylinder_direction.length;dd++){
var material = new THREE.MeshPhongMaterial({color:'#ff0000'});
let upVector = new THREE.Vector3(0, 1, 0);
var geometry = new THREE.CylinderGeometry(5, 5, line_len[dd], 20, 1, false);
var mesh = new THREE.Mesh(geometry, material);
mesh.position.set(0, line_len[dd]/2, 0);
var group = new THREE.Group();
group.position.set(star_line_x[dd],star_line_y[dd],star_line_z[dd]);
group.add(mesh);
let targetVector =cylinder_direction[dd];
let quaternion = new THREE.Quaternion().setFromUnitVectors(upVector, targetVector.normalize());
group.setRotationFromQuaternion(quaternion)
scene.add(group)
}
picture two: use for to draw the tree
For a tree the simplest method is to start with just a tree depth and assume 2 children. The function makes one branch and if depth > 0 then it recursively calls itself to make 2 more branches.
const numBranches = 2;
const spread = 1.5;
const branchShrinkFactor = 0.8;
const branchSpreadFactor = 0.8;
function addBranch(parent, depth, offset, angle, branchLength, spread) {
const material = new THREE.MeshPhongMaterial({color:'#ff0000'});
const geometry = new THREE.CylinderBufferGeometry(5, 5, branchLength, 20, 1, false);
geometry.translate(0, branchLength / 2, 0);
const mesh = new THREE.Mesh(geometry, material);
mesh.position.y = offset;
mesh.rotation.z = angle;
parent.add(mesh);
if (depth > 1) {
for (let i = 0; i < numBranches; ++i) {
const a = i / (numBranches - 1) - 0.5;
addBranch(mesh, depth - 1, branchLength, a * spread, branchLength * branchShrinkFactor, spread * branchSpreadFactor)
}
}
}
addBranch(scene, 5, 0, 0, 100, 1.5);
body {
margin: 0;
}
#c {
width: 100vw;
height: 100vh;
display: block;
}
<canvas id="c"></canvas>
<script type="module">
import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r115/build/three.module.js';
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 75;
const aspect = 2; // the canvas default
const near = 1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(0, 150, 300);
const scene = new THREE.Scene();
scene.background = new THREE.Color('lightskyblue');
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(-1, 2, 4);
scene.add(light);
}
const numBranches = 2;
const spread = 1.5;
const branchShrinkFactor = 0.8;
const branchSpreadFactor = 0.8;
function addBranch(parent, depth, offset, angle, branchLength, spread) {
const material = new THREE.MeshPhongMaterial({color:'#ff0000'});
const geometry = new THREE.CylinderBufferGeometry(5, 5, branchLength, 20, 1, false);
geometry.translate(0, branchLength / 2, 0);
const mesh = new THREE.Mesh(geometry, material);
mesh.position.y = offset;
mesh.rotation.z = angle;
parent.add(mesh);
if (depth > 1) {
for (let i = 0; i < numBranches; ++i) {
const a = i / (numBranches - 1) - 0.5;
addBranch(mesh, depth - 1, branchLength, a * spread, branchLength * branchShrinkFactor, spread * branchSpreadFactor)
}
}
}
addBranch(scene, 5, 0, 0, 100, 1.5);
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
renderer.render(scene, camera);
// requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
</script>
If you want specific data for each branch then you need to pass that in. For example
const tree = [
{ length: 100, angle: 0, branches: 2 }, // root
{ length: 40, angle: -1, branches: 3 }, // first branch
{ length: 50, angle: 0.8, branches: 0 }, // 1st child branch
{ length: 40, angle: 0.3, branches: 0 }, // 2nd child branch
{ length: 30, angle: -0.3, branches: 0 }, // 3rd child branch
{ length: 50, angle: 0.8, branches: 2 }, // second branch
{ length: 50, angle: 0.5, branches: 0 }, // 1st child branch
{ length: 40, angle: -0.6, branches: 2 }, // 2nd child branch
{ length: 40, angle: -0.3, branches: 0 }, // 1st grandchild branch
{ length: 95, angle: 0.3, branches: 0 }, // 2st grandchild branch
];
and then walk the tree description, if a branches for a particular branch is > 0 then it recursively calls itself to add those branches. Each branches consumes a row in the array of branches so we pass back ndx so we can tell how many rows were consumed.
function addBranch(parent, offset, tree, ndx = 0) {
const {length, angle, branches} = tree[ndx];
const material = new THREE.MeshPhongMaterial({color:'#ff0000'});
const geometry = new THREE.CylinderGeometry(5, 5, length, 20, 1, false);
geometry.translate(0, length / 2, 0);
const mesh = new THREE.Mesh(geometry, material);
mesh.position.y = offset;
mesh.rotation.z = angle;
parent.add(mesh);
for (let i = 0; i < branches; ++i) {
ndx = addBranch(mesh, length, tree, ++ndx);
}
return ndx;
}
addBranch(scene, 0, tree);
body {
margin: 0;
}
#c {
width: 100vw;
height: 100vh;
display: block;
}
<canvas id="c"></canvas>
<script type="module">
import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r115/build/three.module.js';
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 75;
const aspect = 2; // the canvas default
const near = 1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(0, 150, 300);
const scene = new THREE.Scene();
scene.background = new THREE.Color('lightskyblue');
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(-1, 2, 4);
scene.add(light);
}
const tree = [
{ length: 100, angle: 0, branches: 2 }, // root
{ length: 40, angle: -1, branches: 3 }, // first branch
{ length: 50, angle: 0.8, branches: 0 }, // 1st child branch
{ length: 40, angle: 0.3, branches: 0 }, // 2nd child branch
{ length: 30, angle: -0.3, branches: 0 }, // 3rd child branch
{ length: 50, angle: 0.8, branches: 2 }, // second branch
{ length: 50, angle: 0.5, branches: 0 }, // 1st child branch
{ length: 40, angle: -0.6, branches: 2 }, // 2nd child branch
{ length: 40, angle: -0.3, branches: 0 }, // 1st grandchild branch
{ length: 95, angle: 0.3, branches: 0 }, // 2st grandchild branch
];
function addBranch(parent, offset, tree, ndx = 0) {
const {length, angle, branches} = tree[ndx];
const material = new THREE.MeshPhongMaterial({color:'#ff0000'});
const geometry = new THREE.CylinderGeometry(5, 5, length, 20, 1, false);
geometry.translate(0, length / 2, 0);
const mesh = new THREE.Mesh(geometry, material);
mesh.position.y = offset;
mesh.rotation.z = angle;
parent.add(mesh);
for (let i = 0; i < branches; ++i) {
ndx = addBranch(mesh, length, tree, ++ndx);
}
return ndx;
}
addBranch(scene, 0, tree);
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
renderer.render(scene, camera);
// requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
</script>
It's not clear to me what your input data is. Your tree has a depth of 3 and 2 branches per level so this data would work
const endPoints = [
[ 0, 0, 0], // A
[ 2, 151, 2], // B
[ -62, 283, 63], // C
[-104, 334, 74], // E
[ -58, 338, 45], // F
[ 62, 296, -58], // D
[ 67, 403, -55], // G
[ 105, 365, -86], // H
];
using this code
// assumes there are 2 branches per
function addBranch(parent, depth, offset, tree, parentNdx = 0, childNdx = 1) {
const start = tree[parentNdx];
const end = tree[childNdx];
const length = start.distanceTo(end);
const material = new THREE.MeshPhongMaterial({color:'#ff0000'});
const geometry = new THREE.CylinderGeometry(5, 5, length, 20, 1, false);
geometry.translate(0, length / 2, 0);
geometry.rotateX(Math.PI / 2);
const mesh = new THREE.Mesh(geometry, material);
mesh.position.z = offset;
parent.add(mesh);
mesh.lookAt(end);
let ndx = childNdx + 1;
if (depth > 1) {
const numBranches = 2;
for (let i = 0; i < numBranches; ++i) {
ndx = addBranch(mesh, depth - 1, length, tree, childNdx, ndx);
}
}
return ndx;
}
addBranch(scene, 3, 0, tree);
I pointed the cylinders in the positive Z direction which means I can use lookAt to point the cylinder from its start to its end point.
body {
margin: 0;
}
#c {
width: 100vw;
height: 100vh;
display: block;
}
<canvas id="c"></canvas>
<script type="module">
import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r115/build/three.module.js';
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 75;
const aspect = 2; // the canvas default
const near = 1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(250, 170, 250);
camera.lookAt(0, 170, 0);
const scene = new THREE.Scene();
scene.background = new THREE.Color('lightskyblue');
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(-1, 2, 4);
scene.add(light);
}
const tree = [
[ 0, 0, 0], // A
[ 2, 151, 2], // B
[ -62, 283, 63], // C
[-104, 334, 74], // E
[ -58, 338, 45], // F
[ 62, 296, -58], // D
[ 67, 403, -55], // G
[ 105, 365, -86], // H
].map(v => new THREE.Vector3().fromArray(v));
// assumes there are 2 branches per
function addBranch(parent, depth, offset, tree, parentNdx = 0, childNdx = 1) {
const start = tree[parentNdx];
const end = tree[childNdx];
const length = start.distanceTo(end);
const material = new THREE.MeshPhongMaterial({color:'#ff0000'});
const geometry = new THREE.CylinderGeometry(5, 5, length, 20, 1, false);
geometry.translate(0, length / 2, 0);
geometry.rotateX(Math.PI / 2);
const mesh = new THREE.Mesh(geometry, material);
mesh.position.z = offset;
parent.add(mesh);
mesh.lookAt(end);
let ndx = childNdx + 1;
if (depth > 1) {
const numBranches = 2;
for (let i = 0; i < numBranches; ++i) {
ndx = addBranch(mesh, depth - 1, length, tree, childNdx, ndx);
}
}
return ndx;
}
addBranch(scene, 3, 0, tree);
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
renderer.render(scene, camera);
// requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
</script>
note: this only one of infinite ways to create the tree recursively. Rather than an array in depth first order you could also create a tree structure to pass into the algorithm
const E = {
pos: [-104, 334, 74],
};
const F = {
pos: [ -58, 338, 45],
};
const C = {
pos: [ -62, 283, 63],
children: [E, F],
};
const G = {
pos: [ 67, 403, -55],
};
const H = {
pos: [ 105, 365, -86],
};
const D = {
pos: [ 62, 296, -58],
children: [G, H],
};
const B = {
pos: [ 2, 151, 2],
children: [C, D],
};
const A = {
pos: [0, 0, 0],
children: [B],
};
function addBranch(parent, branch, offset = 0) {
const {pos, children} = branch;
const start = new THREE.Vector3().fromArray(pos);
for (const child of children) {
const end = new THREE.Vector3().fromArray(child.pos);
const length = start.distanceTo(end);
const geometry = new THREE.CylinderGeometry(5, 5, length, 20, 1, false);
geometry.translate(0, length / 2, 0);
geometry.rotateX(Math.PI / 2);
const material = new THREE.MeshPhongMaterial({color: 'red'});
const mesh = new THREE.Mesh(geometry, material);
mesh.position.z = offset;
parent.add(mesh);
mesh.lookAt(end);
if (child.children) {
addBranch(mesh, child, length);
}
}
}
addBranch(scene, A);
body {
margin: 0;
}
#c {
width: 100vw;
height: 100vh;
display: block;
}
<canvas id="c"></canvas>
<script type="module">
import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r115/build/three.module.js';
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 75;
const aspect = 2; // the canvas default
const near = 1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(250, 170, 250);
camera.lookAt(0, 170, 0);
const scene = new THREE.Scene();
scene.background = new THREE.Color('lightskyblue');
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(-1, 2, 4);
scene.add(light);
}
const E = {
pos: [-104, 334, 74],
};
const F = {
pos: [ -58, 338, 45],
};
const C = {
pos: [ -62, 283, 63],
children: [E, F],
};
const G = {
pos: [ 67, 403, -55],
};
const H = {
pos: [ 105, 365, -86],
};
const D = {
pos: [ 62, 296, -58],
children: [G, H],
};
const B = {
pos: [ 2, 151, 2],
children: [C, D],
};
const A = {
pos: [0, 0, 0],
children: [B],
};
function addBranch(parent, branch, offset = 0) {
const {pos, children} = branch;
const start = new THREE.Vector3().fromArray(pos);
for (const child of children) {
const end = new THREE.Vector3().fromArray(child.pos);
const length = start.distanceTo(end);
const geometry = new THREE.CylinderGeometry(5, 5, length, 20, 1, false);
geometry.translate(0, length / 2, 0);
geometry.rotateX(Math.PI / 2);
const material = new THREE.MeshPhongMaterial({color: 'red'});
const mesh = new THREE.Mesh(geometry, material);
mesh.position.z = offset;
parent.add(mesh);
mesh.lookAt(end);
if (child.children) {
addBranch(mesh, child, length);
}
}
}
addBranch(scene, A);
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
renderer.render(scene, camera);
// requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
</script>

I try to use the answer about recursion to draw a depth of 3 and 3 branches per tree,but answer is very strange?

I according the answer from use recursion to draw the tree and use recursion try to draw a depth of 3 and 3 branches per(like picture one)
but it print a strange answer (like picture three)
Please tell me where is the problem.THANK YOU.
picture one:
picture two: I use for to draw the answer
const tree = [
[0, 0, 0 ], // A // root
[-2, 104, -4 ], //B
[-39, 189, -23],//c
[-4, 230, 19 ], //D
[34, 192, -61 ], //E
[-150, 270, -72], //F
[-115, 296, -63], //G
[-106, 326, 6], //G
[-82, 365, 23 ],//I
[-43, 360, 123],//J
[10, 294, 144 ],//K
[76, 271, -176], //L
[132, 289, -150],//M
[87, 228, -63] //N
].map(v => new THREE.Vector3().fromArray(v));
// assumes there are 3 branches per
function addBranch(parent, depth, offset, tree, parentNdx = 0, childNdx = 1) {
const start = tree[parentNdx];
const end = tree[childNdx];
const length = start.distanceTo(end);
const material = new THREE.MeshPhongMaterial({color:'#ff0000'});
const geometry = new THREE.CylinderGeometry(5, 5, length, 20, 1, false);
geometry.translate(0, length / 2, 0);
geometry.rotateX(Math.PI / 2);
const mesh = new THREE.Mesh(geometry, material);
mesh.position.z = offset;
parent.add(mesh);
mesh.lookAt(end);
let ndx = childNdx + 1;
if (depth > 1) {
const numBranches = 3;
for (let i = 0; i < numBranches; ++i) {
ndx = addBranch(mesh, depth - 1, length, tree, childNdx, ndx);
}
}
return ndx;
}
addBranch(scene, 3, 0, tree)
picture three: strange answer
The algorithm uses depth first order, that means the order of your vertices needs to be
ABCFGHDIJKELMN
body {
margin: 0;
}
#c {
width: 100vw;
height: 100vh;
display: block;
}
<canvas id="c"></canvas>
<script type="module">
import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r115/build/three.module.js';
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 75;
const aspect = 2; // the canvas default
const near = 1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(250, 500, -75);
camera.lookAt(0, 200, 0);
const scene = new THREE.Scene();
scene.background = new THREE.Color('lightskyblue');
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(1, 2, 4);
scene.add(light);
}
//ABCFGHDIJKELMN
const tree = [
[0, 0, 0], // A
[-2, 104, 4], // B
[-39, 183, -23], // C
[-156, 270, -72], // F
[-115, 296, -63], // G
[-106, 326, 6], // H
[-4, 230, 19], // D
[-82, 366, 23], // I
[-43, 360, 123], // J
[10, 294, 144], // K
[34, 192, -61], // E
[76, 271, -176], // L
[132, 289, -150], // M
[97, 228, -63], // N
].map(v => new THREE.Vector3().fromArray(v));
// assumes there are 3 branches per
function addBranch(parent, depth, offset, tree, parentNdx = 0, childNdx = 1) {
const start = tree[parentNdx];
const end = tree[childNdx];
const length = start.distanceTo(end);
const material = new THREE.MeshPhongMaterial({color:'#ff0000'});
const geometry = new THREE.CylinderGeometry(5, 5, length, 20, 1, false);
geometry.translate(0, length / 2, 0);
geometry.rotateX(Math.PI / 2);
const mesh = new THREE.Mesh(geometry, material);
mesh.position.z = offset;
parent.add(mesh);
mesh.lookAt(end);
let ndx = childNdx + 1;
if (depth > 1) {
const numBranches = 3;
for (let i = 0; i < numBranches; ++i) {
ndx = addBranch(mesh, depth - 1, length, tree, childNdx, ndx);
}
}
return ndx;
}
addBranch(scene, 3, 0, tree);
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
renderer.render(scene, camera);
// requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
</script>

How to define a heavy object in Matter JS

To explain better what I need, think about a tank and a can of soda.
No matter what the speed of that can of soda would be, the tank wouldn't move if it's hit.
But the tank should be able to move when force is applied to it.
Already opened an issue here with more details:
https://github.com/liabru/matter-js/issues/841
And I also made an example here for what I need.
var Engine = Matter.Engine,
Render = Matter.Render,
World = Matter.World,
Bodies = Matter.Bodies,
Body = Matter.Body;
var engine = Engine.create();
engine.positionIterations = 10;
engine.velocityIterations = 8;
var render = Render.create({
element: document.body,
engine: engine,
options: {
width: 800,
height: 400,
wireframes: false
}
});
engine.world.gravity.y = 0;
var topWall = Bodies.rectangle(400, 50, 720, 20, { isStatic: true });
var leftWall = Bodies.rectangle(50, 210, 20, 300, { isStatic: true });
var rightWall = Bodies.rectangle(750, 210, 20, 300, { isStatic: true });
var bottomWall = Bodies.rectangle(400, 350, 720, 20, { isStatic: true });
var ball = Bodies.circle(100, 200, 5, {
friction: 0.05,
frictionAir: 0.05,
density: 0.001,
restitution: 0.2
});
var bigBox = Matter.Bodies.rectangle(500, 200, 120, 120, {
inertia: Infinity,
frictionAir: 1,
friction: 1,
density: 0.1
});
World.add(engine.world, [topWall, leftWall, rightWall, bottomWall, ball, bigBox]);
Engine.run(engine);
Render.run(render);
// CMD + SHIFT + 7 to reload
$('.shoot').on('click', function () {
var speed = 0.02;
var angle = 0;
let vector = Matter.Vector.create(Math.cos(angle) * speed, Math.sin(angle) * speed);
Body.applyForce(ball, ball.position, vector);
});
https://codepen.io/dmerlea/pen/BaoQJYK

how to make curve line using css with animate?

I am trying to create curve line like this using css:
You can try canvas:
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.strokeStyle = '#ff0000';
ctx.beginPath();
ctx.moveTo(20, 20);
ctx.lineTo(100, 20);
ctx.bezierCurveTo(105, 30, 110, 30, 115, 20);
ctx.lineTo(150, 20);
ctx.stroke();
Example code: https://www.w3schools.com/code/tryit.asp?filename=FW7CGRNXNTWQ

Resources