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((x**2 + y**2), 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

- Prepare one 512 * 512 Canvas of

```
<html>
...
<canvas
width="512"
height="512"
style="display:block;margin:0 auto;background-color: #ccc"
></canvas>
...
</html>
```

- Flip canvas Coordinate system

```
const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')
ctx.translate(0, canvas.height)
ctx.scale(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)
```

- 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 、 Segmentfault、 CSDN、 Simple books 、 Open source in China account number .