XiaoJianHua

  • Visualization is front-end visualization
  • Graphics is computer graphics
  • The vector is that vector, you know, from high school
  • The tree is the ugly tree

The results of

First, take a look at the final result of this article.

Ugly thief! Is it possible to sell a good price in the art exhibition!

process

Well, without further ado, let’s see how this ugly tree was born.

Coordinate system

The coordinate system, or rectangular coordinate system, is the basis of geometry, followed by the elements of points, lines and planes.

Coordinate system we are very familiar with, the initial contact with the coordinate system should be junior high school, at that time the coordinate system I do not know you have no impression.

The origin is in the middle, the horizontal axis is the X axis, the vertical axis is the Y axis, divided into four quadrants.

However, for HTML canvas, the default origin is in the upper left corner, the X axis is consistent with the plane cartesian coordinate system, and the Y axis is downward!! I believe that the use of canvas drawing of such coordinate axes in daily work has caused untold troubles to the front end, which is laborious to calculate and prone to bugs.

So how to change canvas coordinate system into plane rectangular coordinate system

Maaaaaaaaagic!

const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')
// We place the origin at the bottom left corner of the canvas
ctx.translate(0, canvas.height)
// Key step: flip the canvasY axis
ctx.scale(1, -1)
Copy the code

In two lines of code, we’ve flipped the coordinate system.

Let’s verify this with an 🌰

Suppose we want to implement the following visual effect on a Canvas Canvas 512 by 256. The height of the mountain is 100, the base is 200, the distance between the center of the two mountains and the midline is 80, and the center of the sun is 150.

We’ll use rough.js to spice things up a bit

<canvas
  width="512"
  height="256"
  style="display: block; margin: 0 auto; background-color: #ccc"
></canvas>
Copy the code
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 / / diameter

const hill1Points = {
  start: [76.0]./ / starting point
  top: [176.100]./ / the vertices
  end: [276.0] / / the end
}

const hill2Points = {
  start: [236.0]./ / starting point
  top: [336.100]./ / the vertices
  end: [436.0] / / the 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()
Copy the code

Here we have flipped the coordinate system and defined the coordinates of the sun’s points in mountain1, mountain2, with complete reference to the cartesian coordinate system.

The final result is as follows

(Is it also able to sell a good price in the art exhibition?)

vector

define

So with the cartesian transformation out of the way, let’s talk about today’s positive principal Vector.

The general definition of a vector is something that has magnitude and direction, and the vector we’re talking about here is a geometric vector, which is represented by a set of coordinates in a plane rectangular coordinate system for example, (1, 1), which means a directed line segment with vertex coordinates x equals one, y equals zero, The direction of the vector is from the origin (0, 0) to the vertex (1,1).

In other words, if you know the vertices of a vector, you know the magnitude and direction of the vector

Vector of the mould

The magnitude of a vector, also called the magnitude of a vector, is the arithmetic square root of the sum of squares of vector coordinates, length = Math.pow((x**2 + y**2), 0.5).

Direction of vector

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

On the other hand, you can also represent a vector by using the Angle between the vector and the X-axis.

Using javascript Math’s built-in methods, we can get the calculation as follows:

// Constructors are covered later in this article
const v = new Vector2D(1.10)
const dir = Math.atan2(v.y, v.x)
Copy the code

arithmetic

Addition and subtraction

Schematic diagram:

As shown in the figure, the new vector obtained by adding vector V1 (x1, y1) and vector v2(x2, y2) is the sum of corresponding coordinates of the two vectors, which can be expressed by the formula: v1(x1, y1) + v2(x2, y2) = v3(x1 + x2, y1 + y2)

If you subtract v3(x1 + x2, y1 + y2) -v2 (x2, y2)= v1(x1, y1)

,

Vector multiplication has cross product and dot product

Dot product diagram:

The physical meaning is that a force of va. Length in the direction of VA is the work done by pulling vb. Length in the direction of VB

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

Schematic diagram of cross product:

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

It can also be understood as a line segment of length va.length moves along the direction of VB to the area swept by the vb vertex, and vice versa

A unit vector

A vector of length 1 is called a unit vector, and there are an infinite number of vectors that satisfy this condition. A non-zero vector divided by its magnitude is the unit vector of this vector. Let’s take the vector with an Angle 0 from the X-axis: **[1, 0]**

Rotation of a vector

If YOU rotate a vector by a certain Angle how do you calculate the vector after RAD. There’s a little bit of a complicated derivation here, so you can just remember the conclusion.

The code is shown in the constructor below

The constructor

// Use an array of length 2 to represent a vector, and position 0 to represent x and position 1 to represent 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}}Copy the code

At this point, the basic elements are in place to draw the figure at the beginning of the article. Below, let’s witness the creation of the world famous painting.

And began to draw

  1. Prepare a 512 by 512 canvas
<html>.<canvas
    width="512"
    height="512"
    style="display:block; margin:0 auto; background-color: #ccc"
  ></canvas>.</html>
Copy the code
  1. Flip canvas coordinate system
const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')
ctx.translate(0, canvas.height)
ctx.scale(1, -1)
Copy the code
  1. Defines methods for drawing 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 factor */
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)
  constv1 = v0.copy().add(v) ctx.beginPath() ctx.lineWidth = thickness ctx.moveTo(... v0) ctx.lineTo(... v1) ctx.stroke() ctx.closePath() }// Now that we've defined it, let's try drawing a branch
drawBranch(ctx, v0, 50.10.Math.PI / 2.1)
Copy the code
  1. Recursive drawing
// Define the contraction factor 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)
Copy the code

This step draws a more regular shape, the code goes to this step, the basic shape of the tree is already there, but to show the effect, add some randomness to the vector flip to draw a more natural tree. 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)
    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)
    drawBranch(
      ctx,
      v1,
      length * LENGTH_SHRINK,
      thickness * THICKNESS_SHRINK,
      right,
      bias
    )
  }
}
drawBranch(ctx, v0, 50.10.Math.PI / 2.1)
Copy the code

Wait, wait, wait, wait, the effect: a bare tree

Effect:(Isn’t that a little artistic?)

All that’s left is to add some embellishments and 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()
  }
}

// The random factor is increased to make the branches more scattered
drawBranch(ctx, v0, 50.10.Math.PI / 2.1)
Copy the code

Here’s how it looks:

conclusion

This paper first shows how to convert canvas coordinate system into cartesian coordinate system

Secondly, an example is used to demonstrate the basic operation of vector in graphics.

Vector operations are more than just a way of finding points and constructing line segments, but that’s just the beginning.

Visual presentation depends on computer graphics, and vector operation is the mathematical basis of the whole computer graphics. Moreover, in the vector operation, in addition to the addition represents moving points and drawing line segments, the vector dot product and cross product operation also has special significance.