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

痞子三分冷 提交于 2020-07-23 08:45:10

问题


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


回答1:


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>


来源:https://stackoverflow.com/questions/61949310/i-just-know-how-to-use-for-to-draw-the-tree-but-now-i-want-to-use-recursion-to

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!