Coding/JavsScript 삽질기2022. 1. 19. 01:08

유튜브에서 회전하는 도넛 영상을 보고, 회전하는 구를 그려 보았다.

Sphere

1. 난해하게 만든 전체 소스는 아래와 같다.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Sphere</title>
    <meta name='viewport' content='width=device-width, initial-scale=1.0'>
    <style>
        canvas {
            height: 100%;
            width: 100%;
        }
    </style>
</head>
<body>
<div align='center'>
    <canvas id="canvas"></canvas>
</div>
<script language="JavaScript">
    // Start: 2022.01.18
    // Update: 2022.01.19

    let cvs;
    let canvas;
    let bufCanvas;
    let bufCtx;
    let thetaX = 0;
    let thetaZ = 0;

    let SIN = [];
    let COS = [];

    function InitValue() {
        for (let i = 0; i < 628; i++) {
            SIN[i] = Math.sin(i / 100);
            COS[i] = Math.cos(i / 100);
        }
    }
    
    function InitCanvas() {
        height = window.innerHeight;
        width = window.innerWidth;

        console.log(">Width:", width);
        console.log(">Heigth:", height);

        canvas = document.getElementById("canvas");
        canvas.width = width;
        canvas.height = height;
        cvs = canvas.getContext("2d");

        bufCanvas = document.createElement("canvas");
        bufCanvas.width = canvas.width;
        bufCanvas.height = canvas.height;
        bufCtx = bufCanvas.getContext("2d");
    }

    function drawCircle(x, y, c, r) {
        bufCtx.beginPath();
        bufCtx.fillStyle = "rgb(" + c + "," + c + "," + c + ")";
        bufCtx.strokeStyle = "rgb(255, 255, 255)";
        bufCtx.arc(x, y, r, 0, Math.PI * 2);
        bufCtx.stroke();
        bufCtx.fill();
        bufCtx.closePath();
    }

    function _draw() {
        let maxSize = canvas.height < canvas.width ? canvas.height : canvas.width;
        let dimen = 8;
        let ox = canvas.width / 2;
        let oy = canvas.height / 2;
        let R1 = maxSize/20;
        let ZZ1 = maxSize * 1000 * 0.4 / R1;

        let points = [];

        for (let i = 0; i < 628; i += dimen) {
            let a = COS[i];
            let b = SIN[i];

            for (let j = 0; j < 628; j += dimen) {
                let c = COS[j];
                let d = SIN[j];
                let e = COS[thetaX];
                let f = SIN[thetaX];
                let g = COS[thetaZ];
                let h = SIN[thetaZ];

                let k = b * R1 * e - a * R1 * d * f;
                let x4 = a * R1 * c * g - k * h;
                let y4 = a * R1 * c * h + k * g;
                let z4 = ZZ1 / (1000 + b * R1 * f + a * R1 * d * e);
                let light = (b * f + a * d * e);

                if (light > 0) {
                    let color = Math.round(light * 255);
                    let xp = Math.floor(x4 * z4 + ox);
                    let yp = Math.floor(-y4 * z4 + oy);
                    points.push([xp, yp, 255-color]);
                }
            }
        }

        bufCtx.strokeStyle = "black";
        bufCtx.fillStyle = "black";
        bufCtx.fillRect(0, 0, canvas.width, canvas.height);
        points.forEach(e => drawCircle(e[0], e[1], e[2], 1));

        thetaX = (thetaX + 2) % 629;
        thetaZ = (thetaZ + 2) % 629;
    }

    function OnDraw() {
        let startTime = new Date();
        _draw();

        cvs.clearRect(0, 0, canvas.width, canvas.height);
        cvs.drawImage(bufCanvas, 0, 0);

        console.log("Elapsed time: " + (new Date() - startTime));
    }


    function onLoadPage() {
        InitValue();
        InitCanvas();
        OnDraw();
        setInterval(OnDraw, 50);
    }

    window.onload = onLoadPage();
</script>
</body>
</html>

 

 


1. HTML로 구를 그리기 위한 기본적인 뼈대를 작성한다.

아래 코드에서 _drawSphere()를 채워 나갑니다.

하기 HTML 코드는 좋은 예제가 아닙니다. HTML 관련은 인터넷에 있는 좋은 코드를 참고 하시기 바랍니다.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Sphere</title>
    <meta name='viewport' content='width=device-width, initial-scale=1.0'>
    <style>
        canvas {
            height: 100%;
            width: 100%;
        }
    </style>
</head>
<body>
<div align='center'>
    <canvas id="canvas"></canvas>
</div>
<script language="JavaScript">
    let cvs;
    let canvas;
    let bufCanvas;
    let bufCtx;

    function InitCanvas() {
        height = window.innerHeight;
        width = window.innerWidth;

        console.log(">Width:", width);
        console.log(">Heigth:", height);

        canvas = document.getElementById("canvas");
        canvas.width = width;
        canvas.height = height;
        cvs = canvas.getContext("2d");

        bufCanvas = document.createElement("canvas");
        bufCanvas.width = canvas.width;
        bufCanvas.height = canvas.height;
        bufCtx = bufCanvas.getContext("2d");
    }

    function _drawSphere() {

    }

    function OnDraw() {
        let startTime = new Date();

        bufCtx.strokeStyle = "black";
        bufCtx.fillStyle = "black";
        bufCtx.fillRect(0, 0, canvas.width, canvas.height);

        _drawSphere();

        cvs.clearRect(0, 0, canvas.width, canvas.height);
        cvs.drawImage(bufCanvas, 0, 0);
        console.log("Elapsed time: " + (new Date() - startTime));
    }


    function onLoadPage() {
        InitCanvas();
        OnDraw();
        setInterval(OnDraw, 50);
    }

    window.onload = onLoadPage();
</script>
</body>
</html>

 

2. 원 그리기

원점을 기준으로 지름 r인 원은 아래 수식으로 표현 할 수 있습니다.

이때, x, y는 아래와 같이 구 할 수 있습니다.


원래 작성한 코드는 아래와 같다.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Sphere</title>
    <meta name='viewport' content='width=device-width, initial-scale=1.0'>
    <style>
        canvas {
            height: 100%;
            width: 100%;
        }
    </style>
</head>
<body>
<div align='center'>
    <canvas id="canvas"></canvas>
</div>
<script language="JavaScript">
    // Start: 2022.01.16
    // Update: 2022.01.18

    let cvs;
    let canvas;
    let bufCanvas;
    let bufCtx;
    let thetaX = 0;
    let thetaZ = 0;

    function InitCanvas() {
        height = window.innerHeight;
        width = window.innerWidth;

        console.log(">Width:", width);
        console.log(">Heigth:", height);

        canvas = document.getElementById("canvas");
        canvas.width = width;
        canvas.height = height;
        cvs = canvas.getContext("2d");

        bufCanvas = document.createElement("canvas");
        bufCanvas.width = canvas.width;
        bufCanvas.height = canvas.height;
        bufCtx = bufCanvas.getContext("2d");
    }

    function drawCircle(x, y, c, r) {

        bufCtx.beginPath();
        bufCtx.fillStyle = "rgb(" + c + "," + c + "," + c + ")";
        bufCtx.strokeStyle = "rgb(255, 255, 255)";
        bufCtx.arc(x, y, r, 0, Math.PI * 2);
        bufCtx.stroke();
        bufCtx.fill();
        bufCtx.closePath();
    }

    function _draw() {
        let maxSize = canvas.height < canvas.width ? canvas.height : canvas.width;
        let dimen = 8;
        let ox = canvas.width / 2;
        let oy = canvas.height / 2;
        let R1 = maxSize/20;  // 원의 반지름

        // 카메라와 스크린의 거리를 z'라 하고, 그 투영되는 상을 x',y'
        // z:x = z':x'
        // z:y = z':y'
        // x' = xz'/z, y' = yz'/z
        // z' = z1
        // x', y' = xz1/z, yz1/z
        // z의 최대거리 z2 = 1000이라 하면
        // x' <= (height/2 * 0.8) (화면 중앙 위치, 마진 0.1)
        // x 의 최대 값은 R1 + R2
        // x' = Z1(R1+R2)/z2 = height * 0.4
        // Z1 = Z2 * height * 0.4 / (R1+R2)

        let ZZ2 = 1000;
        let ZZ1 = maxSize * ZZ2 * 0.4 / R1;

        let points = [];

        for (let i = 0; i < 628; i += dimen) {
            let x1 = Math.cos(i/100) * R1;
            let y1 = Math.sin(i/100) * R1;
            let nx1 = Math.cos(i/100);
            let ny1 = Math.sin(i/100);

            for (let j = 0; j < 628; j += dimen) {
                // Y축 회전
                let x2 = x1 * Math.cos(j/100);
                let y2 = y1;
                let z2 = x1 * Math.sin(j/100);

                let nx2 = nx1 * Math.cos(j/100);
                let ny2 = ny1;
                let nz2 = nx1 * Math.sin(j/100);

                // X축 회전
                let x3 = x2;
                let y3 = y2 * Math.cos(thetaX/100) - z2 * Math.sin(thetaX/100);
                let z3 = y2 * Math.sin(thetaX/100) + z2 * Math.cos(thetaX/100);

                let nx3 = nx2;
                let ny3 = ny2 * Math.cos(thetaX/100) - nz2 * Math.sin(thetaX/100);
                let nz3 = ny2 * Math.sin(thetaX/100) + nz2 * Math.cos(thetaX/100);

                // Z축 회전
                let x4 = x3 * Math.cos(thetaZ/100) - y3 * Math.sin(thetaZ/100);
                let y4 = x3 * Math.sin(thetaZ/100) + y3 * Math.cos(thetaZ/100);
                let z4 = ZZ2 + z3;

                let nx4 = nx3 * Math.cos(thetaZ/100) - ny3 * Math.sin(thetaZ/100);
                let ny4 = nx3 * Math.sin(thetaZ/100) + ny3 * Math.cos(thetaZ/100);
                let nz4 = nz3;

                // 법선 벡터 (nx4, ny4, nz)와 광원의 백터 내적 계산
                // 광원 위치는 (0, 0, -1)
                let lx = 0;
                let ly = 0;
                let lz = -1;
                let light = nx4 * lx + ny4 * ly + nz4 * lz;

                if (light > 0) {
                    let color = Math.round(light * 255);
                    
                    // x' = xz'/z, y' = yz'/z
                    let xp = Math.floor(x4 * ZZ1 / z4);
                    let yp = Math.floor(-y4 * ZZ1 / z4);

                    points.push([xp + ox, yp + oy, 255-color]);
                }
            }
        }

        bufCtx.strokeStyle = "black";
        bufCtx.fillStyle = "black";
        bufCtx.fillRect(0, 0, canvas.width, canvas.height);
        points.forEach(e => drawCircle(e[0], e[1], e[2], 1));

        thetaX = (thetaX + 2) % 629;
        thetaZ = (thetaZ + 2) % 629;
    }

    function OnDraw() {
        _draw();

        cvs.clearRect(0, 0, canvas.width, canvas.height);
        cvs.drawImage(bufCanvas, 0, 0);
    }


    function onLoadPage() {
        InitCanvas();
        OnDraw();
        setInterval(OnDraw, 50);
    }

    window.onload = onLoadPage();
</script>
</body>
</html>
Posted by chobocho
Coding/Java 삽질기2021. 10. 24. 18:08

TextView에서 한 줄에 표시되는 글자수를 계산 하는 코드

var textView: TextView = findViewById(R.id.textView)
val COL : Int = Math.round(textView.width / textView.paint.measureText("가나다라마.") * 6)

그런데 OnCreate에서 위 함수를 사용하면 0이 나온다.

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.textviewer)
      
        val vto: ViewTreeObserver = textView.getViewTreeObserver()
        vto.addOnGlobalLayoutListener {

            if (getWidthCount < 1) {
                getWidthCount++

                val COL : Int = Math.round(textView.width / textView.paint.measureText("가나다라마.") * 6)
                Log.i(TAG, "getWidth:" + COL)
            }
        }
    }

이 경우 위와 같이, 코드를 추가해 주면 된다.

Posted by chobocho
Coding/Java 삽질기2021. 10. 17. 13:38

Unit test에서 Test sample 파일을 읽어서 테스트 하는 방법

1. test 폴더에 resources 폴더를 만들고 테스트 파일을 넣는다

2. 아래와 같이 코드를 작성한다.

    @Test
    public void encrypt() throws URISyntaxException {
        File file = new File(getClass().getResource("/1.txt").toURI());
        assert(file != null);
        URL unavailableURL = getClass().getResource("/unavilable.txt");
        assert(unavailableURL == null);
    }

Posted by chobocho
Coding/JavsScript 삽질기2021. 10. 16. 13:54
Coding/Tip2021. 10. 16. 13:52

오늘날짜 입력: Ctrl + ;

'Coding > Tip' 카테고리의 다른 글

[DOSBOX] 창 크기 조절 방법  (0) 2023.01.27
[Android studio] 사용 팁  (0) 2020.04.13
사무직을 위한 Git 활용 법  (0) 2018.10.27
Posted by chobocho
Coding/Python 삽질기2021. 6. 21. 23:46

기차표 예매를 위하여 줄을 섰을 때, 이들 중 생일이 같은 사람이 존재 할 확률은 얼마일까?

계산을 해보면, 23명 이상 일 때 부터 50%가 넘어가고, 57명이 넘어가면 99%가 넘어간다.

 

 

파이썬으로 짜보면 아래와 같다.

import matplotlib.pyplot as plt

prev = 1
result = [0]
for m in range(57):
    prev = prev * (365-m)/365
    result.append(1-prev)

x = [ n for n in range(len(result))]

plt.scatter(x, result)
plt.axvline(x=23, color='RED', linestyle='-', linewidth=0.2)
plt.axhline(y=0.5, color='RED', linestyle='-', linewidth=0.2)
plt.show()

Posted by chobocho
Coding/CPP 삽질기2021. 6. 8. 22:21

오픈 챗에서 누구나 다 해보는 "별 그리기" 문제를 물어 보길래, 한 번 짜봤다.

#include <stdio.h>
#include <string.h>

int main(int argc, char **argv) {
    char star[] = "*****";

    for (int i = 0; i < strlen(star); i++) {
         printf("%s\n", &star[strlen(star)-i-1]);
    }

    return 0;
}

 

chobocho@Chobocho-Mint:~/github/cpp_study/src/sandbox$ ./a.out
*
**
***
****
*****

'Coding > CPP 삽질기' 카테고리의 다른 글

[ChatGPT에게 묻다] Fast sqrt 함수  (0) 2023.02.26
Stack과 Heap  (0) 2021.02.25
[C] Binary search  (0) 2020.12.06
Posted by chobocho
Coding/CPP 삽질기2021. 2. 25. 23:52

1. Stack과 Heap 영역에 변수 생성 시간 테스트

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define MAX_COUNT 1000000


void do_nothing_test() {
    clock_t start = clock();

    for (int i = 0; i < MAX_COUNT; i++)
    ;

    printf("Empty: %f\n", ((float)(clock()-start)/CLOCKS_PER_SEC));
}


void stack_create_test() {
    clock_t start = clock();

    for (int i = 0; i < MAX_COUNT; i++) {
        int arr[10] = { 0, };
    }

    printf("Stack: %f\n", ((float)(clock()-start)/CLOCKS_PER_SEC));
}


void heap_create_test() {
    clock_t start = clock();

    for (int i = 0; i < MAX_COUNT; i++) {
        int *arr = malloc(sizeof(int)*10);
        free(arr);
    }

    printf("Heap:  %f\n", ((float)(clock()-start)/CLOCKS_PER_SEC));
}

int main(int argc, char **argv) {
    do_nothing_test();
    stack_create_test();
    heap_create_test();
    return 0;
}

결과

chobocho@Chobocho-Mint:~/github/cpp_study/src/memory$ ./a.out
Empty: 0.003636
Stack: 0.003976
Heap:  0.015938

'Coding > CPP 삽질기' 카테고리의 다른 글

별 그리기  (0) 2021.06.08
[C] Binary search  (0) 2020.12.06
Quick sort  (0) 2017.07.13
Posted by chobocho
Coding/Python 삽질기2021. 2. 21. 23:27

인터넷에서 4개의 점 이상을 지나는 휴대폰 패턴의 가지수는 모두 몇 가지인가 라는 문제롤 보고 파이썬으로 풀어 보았다.

0: 9
1: 56
2: 320
3: 1624
4: 7152
5: 26016
6: 72912
7: 140704
8: 140704

4-9 patterns count: 389112

 

작성코드는 아래와 같다

 

import time
import math

def is_valid(used_pattern, stack, max_point):
    if len(stack) < max_point:
        return 0
                
    checked_point = [False] * 9
    checked_point[stack[0]] = True   

    for i in range(1, len(stack)):
        prev_x = stack[i-1] % 3
        prev_y = math.floor(stack[i-1] / 3)
        cur_x = stack[i] % 3
        cur_y = math.floor(stack[i] / 3)
        
        """
        0 1 2
        3 4 5
        6 7 8
        
        아래와 같은 이동한 경우,
        이동시 가운데 점이 기존 패턴에 포함 되지 않은 경우
        유효 하지 않다.
        
        0 <-> 2 | 0 <-> 6 | 0 <-> 8
        2 <-> 8 | 6 <-> 8 | 2 <-> 6
        1 <-> 7 | 3 <-> 5 

        예)
        0 -> 1 -> 0 -> 2 : OK
        0 -> 2 : Not OK
        
        유효하지 않은 경우를 판단하기 위하여
        각 버튼의 위치를 (y, x)로 나타내면 (0,0) ... (2,2) 까지이다.
         
        0 -> 2  =>  (0,0) -> (0, 2)
        0 -> 6  =>  (0,0) -> (2, 0)

        여기서 문제 경우의 
        두 좌표의 합을 구하면 x, y가 모두 2의 배수가 된다.
        그래서 아래와 같이 이동 전후의 좌표의 합이 모두 2의 배수가 되는 경우에서
        Not OK 경우만 걸러 주면 된다.
        
        0 -> 1 -> 0 -> 2 : OK
        0 -> 2 : Not OK

        """
        if ((prev_x + cur_x) % 2 == 0) and ((prev_y + cur_y) % 2 == 0):
            mid_x = math.floor((prev_x + cur_x) / 2)
            mid_y = math.floor((prev_y + cur_y) / 2)
            if checked_point[mid_y * 3 + mid_x] == False:
                return 0
                
        checked_point[stack[i]] = True
        
    return 1


def check_pattern(used_pattern, stack, max_point):
    if len(stack) > max_point:
       return 0
    
    result = is_valid(used_pattern, stack, max_point)
    
    for p in range(0, 9):
        if used_pattern[p]:
            continue
        used_pattern[p] = True
        stack.append(p)
        result = result + check_pattern(used_pattern, stack, max_point)
        stack.pop()
        used_pattern[p] = False
        
    return result

    
def main():
    used_pattern = [False] * 9
    stack = []
    result = []
  
    
    for max_point in range(1, 10):
        result.append(check_pattern(used_pattern, stack, max_point))
        
   
    for c in range(0, len(result)):
        print ("{0}: {1}".format(c, result[c]))
        
    print("4-9 patterns count:", sum(result[3:]))

if __name__ == '__main__':
    start_time = time.time() 
    main()
    print('\n', time.time() - start_time)
    

 


위 코드를 C로 바꾸면 아래와 같다.

#include <stdio.h>


int stack_idx;
int stack[9] = {0, };


int is_valid(int used) {
    int checked = 0;
    
    if (stack_idx < 4) {
        return 0;
    }

    checked |= 1 << stack[0];

    for (int i = 1; i < stack_idx; i++) {
        int prev_x = stack[i-1] % 3;
        int prev_y = stack[i-1] / 3;
        int cur_x = stack[i] % 3;
        int cur_y = stack[i] / 3;

        if (((prev_x + cur_x) % 2 == 0) &&
            ((prev_y + cur_y) % 2 == 0)) {

                 int mid_x = (prev_x + cur_x) / 2;
                 int mid_y = (prev_y + cur_y) / 2;
                 int pos = mid_y*3 + mid_x; 

                 if (!(checked & (1 << pos))) {
                     return 0;
                 }
        }
        checked |= (1 << stack[i]);
    }

    return 1;
}


int check_pattern(int used) {
    int result = 0;

    if (stack_idx == 10) {
        return 0;
    }

    result = is_valid(used);

    for (int i = 0; i < 9; i++) {
        if (used & (1 << i)) {
            continue;
        }

        stack[stack_idx++] = i;
        result += check_pattern(used | (1 << i));
        --stack_idx;
    }
    return result;
}


int main(int argc, char **argv) {
    printf("%d\n", check_pattern(0));
    return 0;
}
Posted by chobocho
Coding/JavsScript 삽질기2021. 2. 13. 15:38

Youtube에서 3D 프로그래밍 관련 괜찮은 자료가 있어서 정리를 해 둡니다.

"高校数学とJavaScriptだけ。FPSの作り方 #1【ゲームプログラミング】【ゲーム開発】"

라는 제목의 강의 입니다. 강의 내용을 압축해 두어서, 생략이 많네요;;;

youtu.be/Mtf4rz9UEQo

 

youtu.be/aj1oBvY_LWw

 

youtu.be/34uAtYSirWk

 

Posted by chobocho