---
# System prepended metadata

title: CG HW1 Report

---

# CG HW1 Report

 
## Line algorithm implementation

We first calculate the vector $(dx, dy)$ and the distance $D$  from $(x_1, y_1)$ to $(x2, y2)$.



\begin{cases}
dx = x_2-x_1 \\
dy = y_2-y_1
\end{cases}       

$$
D = \sqrt{dx^2+dy^2}
$$


Then, with the vector and distance calculated, we can simply plot the graph by interpolation. 
That is, for each each "step" $d$ we make, we calculate the "completion ratio" until $d=D$, plot the line with the following formula:

$$
(x, y) = (x_1 + \frac{d}{D}dx,\ y_1 + \frac{d}{D}dy) \quad where \quad d \in [0,D]
$$ 


> ![image](https://hackmd.io/_uploads/HJiaY6og1l.png)



## Circle algorithm implementation

With the same idea as above, we can first evaluate the perimeter $P$ of a circle,

$$
P = 2 \pi r
$$

With the perimeter calculated, we need to transform "completion rate" to radian form, in order to apply trigonometric operations,

$$
c = 2\pi \frac{d}{P}
$$

We can then draw a circle with the classic circle formulae,

$$
(x, y) = (x + r \cos{c},\ y+r \sin{c} ) \quad where \quad c \in [0, 2\pi]
$$

> ![image](https://hackmd.io/_uploads/B1sbqTjeyg.png)



## Ellipse algorithm implementation

Again, same energy, we need to find the ellipse's perimeter. 
However, currently there is no accurate formula for Ellipse Perimeter in math. 
Luckily, there is still a proximated alternative version that can be easily evaluated, by using

$$
P = 2\pi\sqrt{\frac{{2r_1}^2 + {2r_2}^2}{2}}
$$

we get perimeter which gives the result at most 5% more than the real perimeter, which is acceptable as it may cause at most 5% curve segment be drawn twice.

Cool. Then we can retrieve the completion rate with the same operation

$$
c = 2\pi \frac{d}{P}
$$

Draw a ellipse with the classic ellipse formulae,

$$
(x, y) = (x + r_1 \cos{c},\ y+r_2 \sin{c} ) \quad where \quad c \in [0, 2\pi]
$$


> ![image](https://hackmd.io/_uploads/H12NqaigJl.png)


## Bezier curve algorithm implementation

Finally, here comes the boss attack.

Bezier curve has no easy way to evaluate its length $L$. My way to implement it is to abandon the accuate perimeter evaluation, using a value greater than the actual length $L_e \ge L$. I chose

$$
L_{e} = |\overrightarrow{p_1p_2}| + |\overrightarrow{p_2p_3}| + |\overrightarrow{p_3p_4}|
$$

Completion rate is the same as a simple line (which we omitted in the previous line example)

$$
c = \frac{d}{L_e}
$$

Draw the line with the bezier curve algorithm

$$
B(c) =  p_1(1-c)^3 + 3 p_2  c(1-c)^2 + 3p_3  c^2(1-t) + p4 c^3 
$$


> ![image](https://hackmd.io/_uploads/ry1RoTslke.png)

:::info
We may also set $L_e$ as a constant value, using linear interpolation to fill the gap

> ![](https://i.imgur.com/8yAhh6A.png)
> $L_e$ = 10
:::




## Eraser implementation

We draw the pixel with the same color as the background, within the range of $(x_1, y_1)$ and $(x_2, y_2)$.

I added a check to ensure $x_1, y_1$ are always less than $x_2, y_2$.

> ![image](https://hackmd.io/_uploads/ry-qsaslJl.png)


## :rainbow: Extra feature :rainbow: 

Click the rainbow button to enable <b><span style="color: #ff0000">R</span><span style="color: #ff7f00">A</span><span style="color: #eeee00">I</span><span style="color: #80ff00">N</span><span style="color: #00ff00">B</span><span style="color: #00ffff">O</span><span style="color: #0000ff">W</span></b> MODE!!!

<!-- ![](https://i.imgur.com/wYNJvkZ.gif) -->
> ![](https://i.imgur.com/Arksshy.gif)

**Implementation**

In `HW1.pde` where UI button is defined, we copied `shapeButton`'s code, modify it to fit our need, create a rainbow button.

```java=142
rainbowButton = new Button(width-90,10,30,30);
rainbowButton.setBoxAndClickColor(color(250), color(150));
rainbowButton.setImage(loadImage("rainbow.png"));
```

In `Renderer.pde`, we defined two variables `UseRainbow` and `ColorShiftSpeed`. The former tells the renderer uses the rainbow utility or not, the latter set the rainbow rotation speed. Also, there is a private field `ColorShift` records current rotation.

```java=1
public boolean UseRainbow = false;
public float ColorShiftSpeed = 148.763;

float ColorShift = 0;
```

foreach draw call, `ColorShift` value grows, until it reach 1, at that time, it will be reset to 0. We will draw rainbow color base on this value.

```java=17
public void run(){            
  //...
  ColorShift += ColorShiftSpeed / 10000;
  if(ColorShift > 1)
    ColorShift %= 1;
}
```

We also create a function to implement rainbow toggle

```java=45
public void toggleRainbow() {
  UseRainbow = !UseRainbow;
}
```

We can now reference in `HW1.pde`

```java=36
rainbowButton.run(()->{shapeRenderer.toggleRainbow();});
```

Here comes the most important part, how to draw in rainbow color?

Below is the RGB value of rainbow color
![](https://i.imgur.com/hR2ETLp.gif)

Observe that RGB value can be splitted into 5 phases,

- **Phase 1**: (255,**0**,0) → (255,**255**,0)
- **Phase 2**: (**255**,255,0) → (**0**,255,0)
- **Phase 3**: (0,**255**,0) → (0,**0**,255)
- **Phase 4**: (**0**,0,255) → (**255**,0,255)
- **Phase 5**: (255,0,**255**) → (255,0,**0**)

by using these relationships, we can write the below code in `util.pde`
```java=153
color evalColor(float t) {  

  // Use Rainbow? 
  if(!UseRainbow)
    return _color;

  // Apply animated color
  t += ColorShift;
  if(t > 1)
    t -= 1;

  // scalar [0-1] to rgb
  int phase = (int)(t * 6); // 0~6  
  t = (t % (1/6.0)) * 6; // normalize t to 0~1 scalar within that phase
  switch(phase) {
    case 0: return color(255, 255*t, 0);
    case 1: return color(255-255*t, 255, 0);
    case 2: return color(0, 255, 255*t);
    case 3: return color(0, 255-255*t, 255);
    case 4: return color(255*t, 0, 255);
    case 5: return color(255, 0, 255-255*t);
    case 6: return color(255, 0, 0);
  }   
  return -1;
}
```

Since all of our implementation has the parameter `complete_ratio` ($c$), we can easily change all color calls to this function to set rainbow color!

<!-- 
 
\begin{cases}
       \text{open,} &\quad\text{if RMSD}_\text{s-open}\ge6, \text{RMSD}_\text{closed}\ge6\\
       \text{closed,} &\quad\text{if RMSD}_\text{closed}\le2 \\
       \text{semiopen,} &\quad\text{if RMSD}_\text{s-open}\le2\\
       \text{transition,} &\quad\text{otherwise.} \\ 
     \end{cases}

-->