当前位置:网站首页>A tree - the vector of the graphical basis of visualization

A tree - the vector of the graphical basis of visualization

2020-12-07 12:40:36 Xiaoqiantou

author : Xiao Jianhua


  • Visualization is front-end Visualization
  • Graphics is computer graphics
  • Vector is that vector , I learned from high school , You'll see
  • The tree is the ugly tree

result


First, let's look at the final result of this article .



Is it a thief or not ! Is it possible to sell at a good price at the art exhibition !

The process


Okay , Don't talk much , Let's see how the ugly tree was born .

Coordinate system


Coordinate system , Or say Plane rectangular coordinate system , It's the basis of geometric graphics , The second is spot 、 Line 、 Noodles These elements .


We are all familiar with the coordinate system , The initial contact coordinate system should be junior high school , I don't know if you have any impression of the coordinate system at that time .


The origin is in the middle , The horizontal axis is x Axis , The vertical axis is y Axis , It's divided into four quadrants .


But what? , html canvas This product , The default origin is in the upper left corner , x The axis is consistent with the plane rectangular coordinate system , y The axis is down !!
I believe this kind of coordinate axis is used in daily work canvas Drawing has caused a lot of trouble to the front-end people , It's a lot of work to calculate , It's easy to get out of bug.


So how to put canvas The coordinate system of the plane becomes a rectangular coordinate system

Maaaaaaaaagic!
const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')
//  We're going to have the origin here canvas The lower left corner 
ctx.translate(0, canvas.height)
//  Key steps :  take canvasY The axis flip 
ctx.scale(1, -1)


Two lines of code , You're done flipping the coordinate system .


We use one So let's verify that


hypothesis , We need to be wide 512 * high 256 One of the Canvas The following visual effects can be achieved on the canvas . among , The height of the mountain is 100, the base 200, The distance from the center of the two mountains to the center line is 80, The height of the center of the sun is 150.


Here we use rough.js Make it more interesting

<canvas
  width="512"
  height="256"
  style="display: block;margin: 0 auto;background-color: #ccc"
></canvas>
const canvas = document.querySelector('canvas')
const rc = rough.canvas(canvas)
rc.ctx.translate(0, canvas.height)
rc.ctx.scale(1, -1)

const cSun = [canvas.width / 2, 106]
const diameter = 100 //  The diameter of 

const hill1Points = {
  start: [76, 0], //  The starting point 
  top: [176, 100], //  The vertices 
  end: [276, 0] //  End 
}

const hill2Points = {
  start: [236, 0], //  The starting point 
  top: [336, 100], //  The vertices 
  end: [436, 0] //  End 
}

const hill1Options = {
  roughness: 0.8,
  stokeWidth: 2,
  fill: 'pink'
}

const hill2Options = {
  roughness: 0.8,
  stokeWidth: 2,
  fill: 'chocolate'
}

function createHillPath(point) {
  const { start, top, end } = point

  return `M${start[0]} ${start[1]}L${top[0]} ${top[1]}L${end[0]} ${end[1]}`
}

function paint() {
  rc.path(createHillPath(hill1Points), hill1Options)
  rc.path(createHillPath(hill2Points), hill2Options)

  rc.circle(cSun[0], cSun[1], diameter, {
    stroke: 'red',
    strokeWidth: 4,
    fill: 'rgba(255, 255, 0, 0.4)',
    fillStyle: 'solid'
  })
}

paint()


Here we flip the coordinate system , Defined mountain1,mountain2, The sun The coordinates of the points in the , It's completely a Cartesian coordinate system .


The final results are as follows





( Is it possible to sell at a good price in the art exhibition )

vector

Definition


Finish the transformation of rectangular coordinate system , Let's talk about the Lord of today , vector (Vector)


The general definition of vector is a quantity with size and direction , The vector we're talking about here is Geometric vectors , It is represented by the coordinates of a set of plane rectangular coordinates
for example (1, 1), intend , The vertex coordinates are x by 1,y by 0 A directed line segment of , The direction of the vector is determined by origin (0, 0) Point to the top (1,1) The direction of .


In other words , You know the vertex of the vector , You know the size and direction of the vector

Modules of vectors


The size of a vector is also called the modulus of a vector , It's the arithmetic square root of the sum of squares of vector coordinates , length = Math.pow((x2 + y2), 0.5).

The direction of the vector


On the one hand, the direction of a vector can be represented by its vertices .


On the other hand, we use vectors and x Angle between axes , It can also represent a vector .


Use javascript Math You can get , Calculation method :

//  Constructors are introduced later in this article 
const v = new Vector2D(1, 10)
const dir = Math.atan2(v.y, v.x)

arithmetic

Addition and subtraction


Sketch Map :



As shown in the figure : vector v1(x1, y1) Sum vector v2(x2, y2) The new vector added is the sum of the corresponding coordinates of the two vectors , To express by formula is to
v1(x1, y1) + v2(x2, y2) = v3(x1 + x2, y1 + y2)


The opposite is subtraction v3(x1 + x2, y1 + y2) - v2 (x2, y2)= v1(x1, y1)

Multiplication and division


Vector multiplication has Cross product and dot product

Dot multiplication diagram :





The physical meaning is , Direction is va Direction , The size is va.length Force , Along the vb Pull in the direction of vb.length The work done by distance


va vb = va.length vb.length * cos(rad)

Cross multiplication diagram :





va vb = va.length va.length * sin(rad)


It can also be understood that the length is va.length Along the line of vb Move in the direction of vb The area swept by the vertex , The opposite is division


Multiplication and division is only a conceptual introduction

Unit vector


The length is 1 The vector of is called the unit vector , There are countless vectors that satisfy this condition , A non 0 Divided by his modulus , It's the unit vector of this vector , We take with x The angle between the axes is 0 Vector :[1, 0] As a unit vector

The rotation of the vector


Turn a vector a certain angle rad How to calculate the vector after that .
There's something more complicated here Derivation process , So you can remember the conclusion directly .


The specific code is shown in the following constructor

Constructors

//  Use a length of 2 An array of represents a vector ,  Subscript to be 0 The position of the x  Subscript to be 1 The position of the  y
class Vector2D extends Array {
  constructor(x = 1, y = 0) {
    super(x, y)
  }

  get x() {
    return this[0]
  }

  get y() {
    return this[1]
  }

  set x(v) {
    this[0] = v
  }

  set y(v) {
    this[1] = v
  }

  add(v) {
    this.x = this.x + v.x
    this.y = this.y + v.y
    return this
  }

  length() {
    return Math.hypot(this.x, this.y)
  }

  rotate(rad) {
    const c = Math.cos(rad)
    const s = Math.sin(rad)
    const [x, y] = this
    this.x = x * c + y * -s
    this.y = x * s + y * c
    return this
  }
}


thus , The basic elements of the figure at the beginning of the article are all ready .
below , Let's witness the birth of world famous paintings .

Start drawing

  1. Prepare one 512 * 512 Canvas of
<html>
  ...
  <canvas
    width="512"
    height="512"
    style="display:block;margin:0 auto;background-color: #ccc"
  ></canvas>
  ...
</html>
  1. Flip canvas Coordinate system
const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')
ctx.translate(0, canvas.height)
ctx.scale(1, -1)
  1. Define how to draw branches
/**
 * 1. ctx canvas ctx  Context object 
 * 2.  Starting vector 
 * 3. length  Vector length ( Branch length )
 * 4. thickness  Line width 
 * 5.  Unit vector  dir  Rotation Angle 
 * 6. bias  Random factors 
 */
const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')
ctx.translate(0, canvas.height)
ctx.scale(1, -1)
ctx.lineCap = 'round'
console.log(canvas.width)
const v0 = new Vector2D(canvas.width / 2, 0)

function drawBranch(ctx, v0, length, thickness, rad, bias) {
  const v = new Vector2D().rotate(rad).scale(length)
  console.log(v, rad, length)
  const v1 = v0.copy().add(v)
  ctx.beginPath()
  ctx.lineWidth = thickness
  ctx.moveTo(...v0)
  ctx.lineTo(...v1)
  ctx.stroke()
  ctx.closePath()
}
//  After defining it, let's draw a branch and try it 
drawBranch(ctx, v0, 50, 10, Math.PI / 2, 1)
  1. Drawing recursively
//  Let's define the shrinkage coefficient first 
const LENGTH_SHRINK = 0.9
const THICKNESS_SHRINK = 0.8
const RAD_SHRINK = 0.5
const BIAS_SHRINK = 1

function drawBranch(ctx, v0, length, thickness, rad, bias) {
  // ....

  if (thickness > 2) {
    //  Draw the left branch 
    const left =
      Math.PI / 4 +
      RAD_SHRINK * (rad + 0.2) +
      drawBranch(
        ctx,
        v1,
        length * LENGTH_SHRINK,
        thickness * THICKNESS_SHRINK,
        left,
        bias
      )

    //  Draw the right branch 
    const right = Math.PI / 4 + RAD_SHRINK * (rad - 0.2)
    drawBranch(
      ctx,
      v1,
      length * LENGTH_SHRINK,
      thickness * THICKNESS_SHRINK,
      right,
      bias
    )
  }
}

drawBranch(ctx, v0, 50, 10, Math.PI / 2, 1)


This step draws a more regular shape , The code is written to this step , The basic shape of the tree has come out , however To show the effect , Add some randomness to vector flipping to draw a tree that is closer to the natural state . The code is as follows :

function drawBranch(ctx, v0, length, thickness, rad, bias) {
  // ....

  if (thickness > 2) {
    //  Draw the left branch 
    const left =
      Math.PI / 4 + RAD_SHRINK * (rad + 0.2) + bias * (Math.random() - 0.5) //  Add some random numbers 
    drawBranch(
      ctx,
      v1,
      length * LENGTH_SHRINK,
      thickness * THICKNESS_SHRINK,
      left,
      bias
    )

    //  Draw the right branch 
    const right =
      Math.PI / 4 + RAD_SHRINK * (rad - 0.2) + bias * (Math.random() - 0.5) //  Add some random numbers 
    drawBranch(
      ctx,
      v1,
      length * LENGTH_SHRINK,
      thickness * THICKNESS_SHRINK,
      right,
      bias
    )
  }
}

drawBranch(ctx, v0, 50, 10, Math.PI / 2, 1)




Etc., etc. , design sketch : A bare tree





( Isn't it a bit artistic )


All that's left is a little bit of embellishment , Hang the fruit

function drawBranch(ctx, v0, length, thickness, rad, bias) {
  // .....

  if (thickness < 5 && Math.random() < 0.3) {
    const th = 6 + Math.random()

    ctx.save()
    ctx.strokeStyle = '#e4393c'
    ctx.lineWidth = th
    ctx.beginPath()
    ctx.moveTo(...v1)
    ctx.lineTo(v1.x, v1.y + 2)
    ctx.stroke()
    ctx.closePath()
    ctx.restore()
  }
}

drawBranch(ctx, v0, 50, 10, Math.PI / 2, 3) //  Here we increase the random factor ,  Make the branches more dispersed 


At this point, the renderings come out :



( I'll ask again , Isn't it beautiful , Do you really want to spend millions of dollars on it )


about drawBranch The first call , Try adjusting the parameters , See what happens .


Full code address :github

summary


This article first shows how to put canvas The coordinate system is transformed into a rectangular coordinate system


Next, we use an example , Basic operation of vector in graphics .


The meaning of vector operation is not only used to calculate the position of points and construct line segments , It's just a rudimentary usage .


Visualization depends on computer graphics , And vector operation is the mathematical basis of computer graphics . and , In vector operations , In addition to the addition of moving points and drawing line segments , Dot product of vector 、 Cross multiplication also has a special meaning .

We are the front of the blackboard , Welcome to our You know SegmentfaultCSDN Simple books Open source in China account number .

版权声明
本文为[Xiaoqiantou]所创,转载请带上原文链接,感谢
https://chowdera.com/2020/12/20201207123647703r.html