← 과제 목록

[과제] 2주차) Basic Programming 채다희

@chae-dahee
  • #과제
목차

작업환경

thorvg.example Fork 한 chae-dahee/thorvg.example 경로를 origin 에 연결했다.

  thorvg.example git:(main)  git remote -v
origin	https://github.com/chae-dahee/thorvg.example.git (fetch)
origin	https://github.com/chae-dahee/thorvg.example.git (push)

git checkout -b chae-dahee/example/newtons-cradle 브랜치에서 작업했다. 과제 제출 브랜치

NewtonsCradle

image

무엇을 만들까 고민하다, 내 PC에서 사용 중인 RunCat이 뉴턴의 요람으로 설정되어있었다. 애니메이션까지 구현하기 좋을 것 같아 구현 주제로 선정했다.

src/NewtonsCradle.cpp

  1. content() 구성항목
    • 800×800 기준 좌표계
    • 창 크기에 따른 등비율 확대·축소
    • 남는 영역을 기준으로 중앙 정렬
    • 배경 도형 생성 호출부로 사용한다.
bool content(tvg::Canvas* canvas, uint32_t w, uint32_t h) override
    {
       ///...
        addFrame(scene);

        leftBall = addBall(scene, 310.0f);
        addBall(scene, 370.0f);
        addBall(scene, 430.0f);
        rightBall = addBall(scene, 490.0f);

        leftBall->rotate(MaxAngle);

        canvas->add(scene);
        return true;
    }
  1. 상단 프레임 addFrame 함수
	void addFrame(tvg::Scene* scene)
image
  1. addBall() 함수로 반복 생성
    • 쇠구슬 색상 그라데이션 컬러 추가
	tvg::Scene* addBall(tvg::Scene* scene, float centerX)
    {
    ///...
		auto ball = tvg::Shape::gen();
        ball->appendCircle(0, stringLength, BallRadius, BallRadius);

        auto fill = tvg::RadialGradient::gen();
        fill->radial(0, stringLength, BallRadius,
                     -10.0f, stringLength - 12.0f, 0.0f);
        tvg::Fill::ColorStop colorStops[3] = {
            {0.0f, 241, 245, 249, 255},
            {0.45f, 148, 163, 184, 255},
            {1.0f, 51, 65, 85, 255}
        };
        fill->colorStops(colorStops, 3);
        ball->fill(fill);
        ball->strokeFill(30, 41, 59);
        ball->strokeWidth(2);
        pendulum->add(ball);
		///...
    }
  • 줄 상단을 회전축으로 사용한다.
    • 줄과 구를 로컬 좌표로 생성한다.
  • 하나의 tvg::Scene으로 묶었다.
  • 이후 Scene을 translate()로 배치하면 rotate() 호출 시 줄과 공이 같은 축을 중심으로 함께 움직인다.
  • 양 끝 진자의 Scene 포인터는 애니메이션 갱신을 위해 반환한다. image
  1. 애니메이션
    • 주기의 동작 1. 왼쪽 공이 올라간 상태에서 중앙으로 낙하 2. 왼쪽 공이 중앙 구에 닿는 시점에 정지 3. 오른쪽 공이 같은 운동량을 전달받은 것처럼 상승 4. 오른쪽 공이 다시 중앙으로 낙하 5. 충돌 시 왼쪽 공이 다시 상승 6. 반복
    • 각도: sin(), cos() 기반으로 계산한다. 끝점에서는 자연스럽게 느려지고 중앙 충돌 지점에서는 가장 빨라지도록 한다.
      • 실제 충돌 해석을 수행하는 방식은 아니다.(실제 환경에서는 감쇠가 존재하기 때문, 가운데 공들도 충돌동작이 존재해야한다. )
      • 다만, 애니메이션 측면에서 뉴턴의 요람을 안정적으로 반복 표현하는 애니메이션 방향이라고 생각했다.
      • 0% 왼쪽 상승 → 25% 충돌 → 50% 오른쪽 상승 → 75% 충돌 → 100% 왼쪽 상승
bool update(tvg::Canvas* canvas, uint32_t elapsed) override
    {
        const auto phase = fmodf(seconds, CycleDuration) / CycleDuration;
        //..

        if (phase < 0.25f) {
            leftAngle = MaxAngle * cosf(phase * 2.0f * Pi);
        } else if (phase < 0.75f) {
            rightAngle = -MaxAngle * sinf((phase - 0.25f) * 2.0f * Pi);
        } else {
            leftAngle = MaxAngle * sinf((phase - 0.75f) * 2.0f * Pi);
        }

        leftBall->rotate(leftAngle);
        rightBall->rotate(rightAngle);
        canvas->update();
        return true;
    }
NewtonsCradle

결과물

확인 시 작업파일을 빌드하고 실행했다. 작업 시 코드 변경에 따라 실시간으로 반영해주는 도구가 있다면 등록하면 좋겠다.(아직 미파악) src/meson.build의 source_file 목록에 NewtonsCradle.cpp를 추가해 개별 실행 파일이 생성되도록 했다.

meson setup --reconfigure builddir
meson compile -C builddir NewtonsCradle
./builddir/src/NewtonsCradle

댓글

Discussion 원문