Skip to content

Dashboard

thingsub edited this page Dec 23, 2025 · 8 revisions

Plotto Dashboard Code

0. 개요

본 문서는 로봇 드로잉 시스템 PlottoDashboard.js 파일을 설명합니다.
Dashboard는 사용자 입력을 수집하여 Spine 모듈에 전달하고, Sketch 모듈을 통해 결과를 시각적으로 확인할 수 있도록 하는 중간 제어 인터페이스 역할을 수행합니다.

이 문서에서는 대시보드를 구성하는 버튼, 슬라이더, 파일 입력 위젯을 중심으로 각 UI 요소가 수행하는 기능과 시스템 내 위치를 설명합니다.


1. 전역변수

자세히 보기
  • init (boolean)

    • 시스템 초기화 여부를 나타내는 변수
    • 기본값: false. 대시보드가 처음 로드될 때 true로 설정됨.
  • angles (배열 [J1, J2])

    • 로봇의 두 관절(Joint 1과 Joint 2)의 각도를 저장하는 배열
    • 기본값: [0, 0] (각각 J1과 J2의 초기 각도)
  • targetAngles (배열 [J1, J2])

    • 목표 각도를 저장하는 배열. 슬라이더와 키보드 입력에 따라 업데이트됨.
    • 기본값: [0, 0]
  • penIsDown (boolean)

    • 펜의 상태를 나타내는 변수. 펜이 내려가면 true, 올라가면 false.
    • 기본값: false
  • KEY_STEP (number)

    • 키보드 입력에 의한 각도 변화량을 설정하는 변수.
    • 기본값: 1.5
  • lastSvgName (string)

    • 최근 드래그 앤 드롭으로 불러온 파일 이름(SVG 또는 TXT plot).
    • 대시보드 상단 UI에 표시됨. 예: "SVG: 파일명 | Loading..."
  • lastSvgSize (number)

    • 최근 불러온 파일의 크기(byte).
    • UI 상태 표시와 용량 확인 용도로 사용됨.
  • lastSvgStatus (string)

    • 최근 파일의 로드 상태 또는 처리 결과를 나타냄.
    • 예: "Loading...", "SVG Loaded ✅", "TXT empty/invalid ❌".
  • $('pen').d (number)

    • 펜 장치의 상태를 나타냄.
    • 0 = 펜 Up, 1 = 펜 Down.
    • 대시보드의 Pen Up/Down 버튼과 연동됨.
  • $("encoder.joint_1").d, $("encoder.joint_2").d (number)

    • 관절 1, 2의 엔코더 값.
    • .d에 값을 할당하면 목표 각도를 설정 가능.
    • degToStep(angle) 함수를 통해 각도를 스텝 단위로 변환 후 적용.
  • plotto (object)

    • SVG나 경로 데이터를 저장하고 처리하는 전역 객체.
    • 주요 기능:
      • plotto.buildFromSvgText(svgText) → SVG 텍스트 파싱 후 plotto.motionJson 생성
      • plotto.plot → 인코딩된 경로 데이터(byte 배열) 저장
  • startJsonPlayback() (function)

    • plotto.motionJson에 저장된 드로잉 경로 명령을 읽어 로봇팔이 움직이도록 재생 시작.
  • trailLayer (object)

    • 그림 시각화 레이어(canvas).
    • trailLayer.clear() → 화면에 남은 궤적 초기화(Erase 기능).
  • drawMode (number)

    • 현재 드로잉 모드를 나타냄.
    • 0 = 수동(Manual), 1 = 일반 자동 드로잉, 2 = 고속 드로잉(Fast), 3 = 전체 즉시 드로잉(All)
  • currentAngleJoint1, currentAngleJoint2 (number)

    • SVG 경로 재생 중 현재 관절 각도 상태.
    • 자동 드로잉 중 일시정지/갱신 시 참조.
  • bakeAllToTrailLayer() (function)

    • drawMode=3("SVG Draw All")에서 사용.
    • 전체 경로를 애니메이션 없이 trailLayer에 한 번에 그려 최종 그림 시각화.

위 전역 변수들과 객체들은 서로 연계되어 UI와 로봇팔 동작 상태를 추적하고 제어합니다. 다음으로 주요 함수들을 살펴보겠습니다.


2. UI 요소 생성

2.1. 슬라이더 및 버튼

대시보드에서는 슬라이더를 통해 두 개의 관절(J1, J2)의 각도를 실시간으로 조절할 수 있습니다. 이 값은 angles 배열에 저장되어, 로봇의 실제 동작을 제어합니다.

슬라이더

createSlider(parent, cfg, id, min, max, initial);

주어진 부모 SVG 요소 내에 슬라이더를 생성하는 함수입니다.

-180 30

파라미터 설명
parent 부모 SVG 요소
cfg [width, x, y], 슬라이더의 폭과 위치
id 슬라이더 요소의 ID
min 최소값
max 최대값
initial 초기값

버튼

createButton(parent, cfg, id, label, onClick);

SVG 내에 HTML 버튼을 생성하는 함수입니다.

SVG Draw

파라미터 설명
parent 부모 SVG 요소
cfg [width, x, y], 버튼의 폭과 위치
id 버튼 요소의 ID
label 버튼 위에 표시할 텍스트
onClick 클릭 이벤트 핸들러 함수(옵션)

두 유틸리티 함수를 통해 대시보드 UI에 간편하게 생성하고, 위치를 일괄적으로 관리할 수 있습니다.

2.2. dashboard() 함수

dashboard() 함수는 컨트롤 패널(UI)을 초기화하고 구성하는 역할을 합니다. 호출 시에 미리 준비된 w2popup 팝업 창 내부에 대시보드 내용을 채우게 됩니다. 주요 동작은 다음과 같습니다.

팝업 크기 설정 및 이벤트

w2popup.resize(470, 610);
window.onresize = () => {
  w2popup.resize(470, 610);
};

팝업 크기를 지정합니다. 현재는 창 크기 변경 시에도 동일하게 유지합니다.

SVG 컨테이너 초기화

const popup_box = select("#dashboard");
popup_box.html(""); // 기존 내용 초기화
popup_box.style("user_select", "none");

대시보드용 <div> 영역을 선택하고 내용을 비웁니다.

SVG/파일 드래그앤드롭 안내 영역

사용자가 SVG 파일 등을 드롭하여 로딩할 수 있도록 상단에 안내 박스를 표시합니다. 현재는 파란색 점선 경계(stroke-dasharray: 6 4)로 드롭 영역을 강조하고 있습니다.

관절 제어 슬라이더 추가

슬라이더 관절 종류 범위 초기값 조작
J1 어깨 -30° ~ 180°
-180 30
J2 팔꿈치 -100° ~ 15°
-100 15

슬라이더 값 변경 시에는 두 관절 모두 동일하게 <div>에 실시간 표시되고, targetAngles 배열의 해당 관절 값이 갱신됩니다.

대시보드 버튼

대시보드의 버튼들은 모두 이전에 정의한 createButton() 함수를 통해 생성할 수 있습니다.

버튼 ID 버튼 라벨 기능 요약
pen_toggle_btn Pen Down / Up 펜 상태 토글
클릭 시 penIsDown 값을 반전시키고, 펜 장치($('pen').d)와 버튼 라벨을 갱신
erase_btn Erase 현재 그려진 경로를 초기화
trailLayer.clear() 호출로 화면에 남은 궤적 제거
svg_draw_btn SVG Draw 불러온 SVG 경로를 따라 자동 드로잉.
drawMode = 1 설정 후 startJsonPlayback() 호출.
draw_manual_btn Draw Manual 수동 드로잉 모드 전환
drawMode = 0으로 설정하고, 펜을 올린 상태로 초기화. 슬라이더/키보드로 직접 관절 제어 가능
svg_draw_fast_btn SVG Draw Fast 불러온 SVG 경로를 고속으로 재생
drawMode = 2, 펜 올림, startJsonPlayback() 호출
svg_draw_all_btn SVG Draw All 전체 경로를 한 번에 시각화
drawMode = 3, 펜 올림, startJsonPlayback()bakeAllToTrailLayer() 호출
download_txt_btn Download TXT 현재 경로 데이터를 TXT 파일로 다운로드
downloadPlotTxtDecSpace("motion_plot.txt") 호출

각 버튼은 클릭 시 내부 상태를 갱신하고, 필요 시 UI나 로봇팔 동작과 연동됩니다.

3. 키보드 이벤트 처리: 화살표 키로 관절 제어

3.1. 키 입력 처리

window.addEventListener("keydown", (e) => { ... })

화살표 키 입력을 통해 관절 각도를 실시간으로 조절하는 이벤트 리스너입니다.

  • J1 관절 (좌/우 회전)

    • ArrowLefttargetAngles[0] -= KEY_STEP
    • ArrowRighttargetAngles[0] += KEY_STEP
    • 범위 제한: -30° ~ 180°
  • J2 관절 (상/하 회전)

    • ArrowUptargetAngles[1] += KEY_STEP
    • ArrowDowntargetAngles[1] -= KEY_STEP
    • 범위 제한: -100° ~ 15°

3.2. 처리 흐름

순서 절차
1. 감지 키 입력 감지 후 변경 여부(changed) 확인
2. 변경 방향키에 따라 targetAngles 값 증가/감소
3. 제한 각도 값 제한 적용 (clamping)
4. 갱신 슬라이더(#angle_J1, #angle_J2)와 값 표시(#angle_J?_val) 실시간 업데이트
5. 방지 e.preventDefault() 호출로 스크롤 등 기본 동작 방지

화살표 키 입력으로도 슬라이더와 동일하게 targetAngles가 업데이트되며, 이후 로봇팔이 제어 루프에서 목표 각도로 움직입니다.

4. control() 함수 : 실시간 관절 제어 루프

주기적으로 호출되어 로봇팔의 각도를 업데이트하는 루프 역할을 합니다.

호출마다 로봇팔 각 관절의 목표 각도를 입력으로 받아, 엔코더 값으로 변환·반영하는 실시간 관절 제어 루프 역할을 수행합니다.

본 함수는 초기화, 상태 동기화, 사용자 입력 처리, 보간 및 엔코더 갱신의 네 단계로 구성됩니다.

주요 기능은 다음과 같습니다.

주요 기능

  1. 초기화 처리 (최초 1회 실행)

      if (!init) {
        init = true;
    
          angles = [
          Math.round($("encoder.joint_1").d), // J1
          Math.round($("encoder.joint_2").d), // J2
        ];
      //  ...(중략)...

    함수가 최초 호출될 때만 실행되는 초기화 단계입니다.

    현재 엔코더 값을 기준으로 내부 각도 상태(angles)를 설정하여, 제어 루프가 실제 로봇의 현재 자세에서 시작되도록 합니다.


  2. UI 슬라이더와 상태 동기화

    if (select("#angle_J1").node()) {
      select("#angle_J1").property("value", angles[0]);
    }
    if (select("#angle_J2").node()) {
      select("#angle_J2").property("value", angles[1]);
    }

    초기화된 관절 각도를 UI 슬라이더에 반영하는 단계입니다.

    이를 통해 내부 제어 상태와 사용자 인터페이스 간의 값 불일치를 방지하고,

    사용자가 현재 관절 상태를 직관적으로 확인할 수 있도록 합니다.


  3. 사용자 입력 값 처리

    if (select("#angle_J1").node()) {
      $("encoder.joint_1").d = degToStep(
        parseFloat(select("#angle_J1").property("value"))
      );
    }
    if (select("#angle_J2").node()) {
      $("encoder.joint_2").d = degToStep(
        parseFloat(select("#angle_J2").property("value"))
      );
    }

    매 프레임마다 슬라이더로부터 입력된 관절 각도를 읽어, 엔코더 단위(step)로 변환하여 제어 입력으로 즉시 반영합니다.

    DOM 요소 존재 여부를 확인함으로써, UI가 없는 환경에서도 안정적으로 동작하도록 구성되어 있습니다.


  1. 관절 각도 보간 및 엔코더 값 갱신

    let lerpSpeed = 0.05;
    angles[0] += (targetAngles[0] - angles[0]) * lerpSpeed;
    angles[1] += (targetAngles[1] - angles[1]) * lerpSpeed;
    
    $("encoder.joint_1").d = degToStep(angles[0]);
    $("encoder.joint_2").d = degToStep(angles[1]);

    현재 각도(angles)에서 목표 각도(targetAngles)로 부드럽게 이동하도록 선형 보간(LERP)을 적용하는 단계입니다.

    보간된 결과는 엔코더 값으로 변환되어 실제 관절 구동 값으로 적용되며, 급격한 각도 변화 없이 안정적인 움직임을 보장합니다.


5. 파일 드래그 앤 드롭 처리: setupSvgDragDrop(popup_box_selection)

setupSvgDragDrop() 함수는 대시보드 상단의 SVG/TXT 드롭 영역에 파일 드래그 앤 드롭 이벤트를 연결합니다.
사용자가 파일을 드롭하면 자동으로 로딩 및 처리 과정을 수행하며, UI 상태 표시도 갱신됩니다.

주요 기능

  1. 중복 등록 방지

    if (el.__svg_drop_ready__) return;
    el.__svg_drop_ready__ = true;
    • DOM 노드에 __svg_drop_ready__ 플래그를 두어 한 번만 이벤트를 등록.
  2. 드래그 오버 / 드래그 리브 이벤트

    el.addEventListener("dragover", (e) => {
      e.preventDefault();
      el.style.outline = "3px dashed #4aa3ff";
    });
    el.addEventListener("dragleave", () => {
      el.style.outline = "";
    });
    • dragover → 기본 동작 방지, 영역 강조 표시 (파란 점선)
    • dragleave → 강조 해제
  3. 드롭 이벤트 처리

    const file = e.dataTransfer.files[0];
    const isSvg =
      file.type === "image/svg+xml" || file.name.toLowerCase().endsWith(".svg");
    const isTxt =
      file.type === "text/plain" || file.name.toLowerCase().endsWith(".txt");
    • 드롭된 파일 확인 (SVG 또는 TXT(plot)만 허용)
    • 상태 표시 업데이트: lastSvgName, lastSvgSize, lastSvgStatus
    • 로딩 상태: "Loading..."
  4. SVG 파일 처리

    if (isSvg) {
      if (typeof window.rebuildFromSvgText === "function") {
        window.rebuildFromSvgText(text);
        lastSvgStatus = `SVG Loaded ✅ (${(file.size / 1024).toFixed(1)} KB)`;
      } else {
        lastSvgStatus = "rebuildFromSvgText() missing ❌";
        alert(
          "rebuildFromSvgText()가 없습니다. Sketch.js에 전역 함수로 추가해 주세요."
        );
      }
    }
    • 텍스트에 <svg> 태그 존재 여부 확인
    • rebuildFromSvgText(text) 호출 → SVG를 화면/경로 데이터로 변환
    • 상태 표시: "SVG Loaded ✅ (크기 KB)"
    • 오류 시 알림 및 상태 업데이트
  5. TXT(plot) 파일 처리

    if (isTxt) {
      const bytes = parsePlotTxt(text);
      plotto.plot = bytes;
      plotto.motionJson = plotDecode(bytes);
      plotto.jsonBuilt = true;
      if (typeof startJsonPlayback === "function") startJsonPlayback();
    }
    • plotDecode(text) 함수를 사용해 바이트 데이터를 디코딩 → plotto.motionJson 생성
    • plotto.plot에 원본 바이트 저장
    • 재생 초기화: startJsonPlayback() 호출
    • 상태 표시: "TXT Plot Loaded ✅ (크기 KB, 바이트 수, 명령 수)"
    • 유효하지 않은 TXT 파일은 경고 메시지 표시
  6. 에러 처리

    catch(err) {
    console.error(err);
    lastSvgStatus = "Read failed ❌";
    alert(`파일 읽기/처리 실패: ${err?.message || err}`);
    }
    • 읽기/처리 실패 시 콘솔 출력 및 상태 표시 "Read failed ❌"
    • 사용자에게 alert로 알림

참고

  • TXT 파서 (parsePlotTxt)

    function parsePlotTxt(text) {
      return text
        .trim()
        .split(/\s+/)
        .map(Number)
        .filter((n) => Number.isInteger(n) && n >= 0 && n <= 255);
    }
    • 공백과 줄바꿈 기준으로 숫자 추출
    • 0~255 범위의 정수만 필터링
  • 상태 표시 DOM

    const t = document.getElementById("svg_status_text");
    t.textContent = `FILE: ${lastSvgName} | ${lastSvgStatus}`;
    • #svg_status_text 요소에 파일 이름과 처리 상태 출력

사용자는 파일을 드래그 앤 드롭만 해도, 자동으로 SVG/TXT를 로딩하고 대시보드와 로봇팔 제어에 필요한 데이터로 변환할 수 있습니다.

6. downloadPlotTxtDecSpace(filename) 함수 : 플롯 데이터 내보내기

downloadPlotTxtDecSpace() 함수는 현재 메모리에 저장된 플롯 데이터(plotto.plot)를
10진수 공백 구분 형식의 .txt 파일로 다운로드할 수 있도록 하는 기능입니다.

UI의 "Download TXT" 버튼에 연결되어 있으며, 플롯 데이터를 외부 파일로 영구 저장(export)하는 역할을 담당합니다.

주요 기능은 다음과 같습니다.

주요 기능

  1. 플롯 데이터 유효성 검사

    if (!plotto.plot || plotto.plot.length === 0) {
      alert("plot 비어있음 (plotEncode 먼저 수행됐는지 확인)");
      return;
    }

    메모리에 플롯 데이터가 존재하는지 확인합니다.

    데이터가 없을 경우, 저장할 수 없음을 사용자에게 알리고 함수 실행을 즉시 중단합니다.

    이에 따라 빈 파일이 생성되는 상황을 방지할 수 있습니다.


  1. 플롯 데이터 텍스트 변환 및 파일 생성

    const text = plotto.plot.join(" "); // 10진수 공백 구분 문자열
    const blob = new Blob([text], {
      type: "text/plain;charset=utf-8",
    });
    const url = URL.createObjectURL(blob);

    plotto.plot 배열에 저장된 값을 10진수 숫자들을 공백으로 구분한 문자열 형식으로 변환합니다.

    변환된 문자열을 기반으로 평문(text/plain) 형식의 .txt 파일을 생성하는데,

    이 형식은 사람이 읽기 쉽고, 이후 동일한 플롯을 다시 불러오기에도 적합합니다.


  1. 브라우저 다운로드 트리거

    const a = document.createElement("a");
    a.href = url;
    a.download = filename;
    document.body.appendChild(a);
    a.click();
    a.remove();
    
    URL.revokeObjectURL(url);

    Blob 객체와 임시 URL을 생성하여, 브라우저가 제공하는 파일 다운로드 방식에 따라 텍스트 파일을 저장합니다.

    동적으로 생성한 링크 요소를 사용해 사용자의 추가 조작 없이 다운로드를 실행합니다.

    다운로드 요청 이후에는 생성된 링크와 임시 URL을 제거하여 메모리 자원이 불필요하게 유지되지 않도록 합니다.


7. SVG 경로 재구성 함수: window.rebuildFromSvgText(svgText)

window.rebuildFromSvgText 함수는 사용자가 SVG 파일을 드롭했을 때 호출되며,
SVG 내용을 로봇팔이 이해할 수 있는 경로 데이터로 변환하는 역할을 수행합니다.

이 함수는 전역(window) 범위에 정의되어 있어, setupSvgDragDrop 등에서 바로 사용할 수 있습니다.

주요 기능은 다음과 같습니다.

주요 기능

  1. plotto를 통한 SVG 파싱

    if (plotto && typeof plotto.buildFromSvgText === "function") {
      plotto.buildFromSvgText(svgText);
    } else {
      console.error("plotto.buildFromSvgText가 없습니다.");
      return;
    }

    plotto 객체가 존재하고, buildFromSvgText 메서드를 제공하는지 확인합니다.

    가능할 경우 SVG 문자열(svgText)을 plotto.buildFromSvgText에 전달하는데, 이때 다음 기능을 수행합니다.

    • <path> 요소를 따라 움직일 좌표 및 관절 명령으로 변환

    • 내부 배열(plotto.plot, plotto.motionJson)에 변환된 경로 저장

    메서드가 없으면 콘솔 오류를 출력하고 함수를 종료합니다.


  2. 변환 후 자동 재생

    if (typeof startJsonPlayback === "function") startJsonPlayback();

    SVG 파싱 후, 전역 함수 startJsonPlayback이 존재하면 호출합니다.

    이 단계에서 변환된 경로 데이터를 이용하여 자동 드로잉 재생을 수행하고, 이어서 UI 초기화나 시뮬레이션 시작 등 후속 처리가 수행됩니다.

    구체적인 동작은 startJsonPlayback 구현에 따릅니다.

SVG 드롭 처리의 핵심 기능으로, plotto 라이브러리의 함수를 이용해 SVG를 플로터가 이해할 수 있는 데이터로 변환하고, 필요한 후속 처리를 이어서 호출합니다.

8. 코드의 전반적인 작동 흐름

마지막으로, 위의 구성 요소들이 함께 어떻게 동작하는지 전체 흐름을 설명합니다:

  1. 초기화 및 UI 표시

    애플리케이션이 시작되면 dashboard() 함수가 호출되어 제어 패널 UI가 화면에 나타납니다.
    (예: 사용자가 메뉴에서 "Control Panel"을 열거나, 페이지가 로드되면서 자동으로 팝업이 뜨는 시나리오)

    • 대시보드에는 J1/J2 슬라이더, Pen 버튼, Erase 버튼, 여러 드로잉 모드 버튼, 파일 드롭 영역 등이 구성됨
    • 초기 상태:
      • 로봇팔의 현재 각도가 슬라이더에 반영
      • 펜은 기본 상태
      • 경로는 로드되지 않은 상태
  2. 사용자 조작 대기

    이제 사용자 입력을 기다리는 상태가 됩니다. 사용자는 다음과 같은 행동을 할 수 있습니다:

  3. 슬라이더 조작

    • J1 또는 J2 슬라이더를 드래그 → targetAngles 변경
    • control() 루프를 통해 로봇 관절이 해당 각도로 이동
    • 슬라이더 옆의 수치도 실시간 갱신
  4. 키보드 입력

    • 화살표 키 누르기: 좌/우 → J1, 상/하 → J2
    • 즉시 targetAngles 변경, 슬라이더 UI도 갱신
    • control()을 통해 부드럽게 움직임
  5. Pen 토글 버튼 클릭

    • 펜 올리기/내리기
    • 내부적으로 $('pen').d 값 0/1로 반영
    • 이후 로봇팔 움직임에서 펜 상태 반영
    • UI 버튼 텍스트도 변경
  6. Erase 버튼 클릭

    • 현재까지 그려진 경로 모두 삭제
    • 화면에서 선 사라짐, trailLayer 초기화
  7. 파일 드래그 & 드롭

    • SVG 파일 또는 이전 저장된 TXT 파일 처리 (setupSvgDragDrop)

    SVG 파일:

    • plotto.buildFromSvgTextplotto.motionJson 생성
    • 필요 시 startJsonPlayback 호출 → 경로 데이터 준비 완료
    • UI 상태 텍스트: "SVG Loaded ✅"

    TXT 파일:

    • plotDecode로 바이트 해석 → plotto.plotplotto.motionJson 채움
    • startJsonPlayback 호출
    • UI 상태 텍스트: "TXT Plot Loaded ✅ (... cmds)"
  8. 드로잉 모드 버튼 클릭

    • SVG나 TXT 로드 후 다양한 모드 선택 가능
    버튼 drawMode 동작
    SVG Draw 1 모션 재생 시작, 로봇팔 일정 속도로 펜 내려 그림, 엔코더 초기화 진행
    SVG Draw Fast 2 모션 재생, 매우 빠른 속도로 펜 이동/그림
    SVG Draw All 3 모션 재생 없이 결과 한꺼번에 표시, 로봇팔 내부 상태 동기화
    Draw Manual 0 사용자 수동 제어 모드, 펜 기본적으로 들어올린 상태 시작
  9. 메인 루프 동작

    • 화면이 열려 있는 동안 control() 함수 지속 실행
    • 항상 현재 UI 입력 상태(targetAngles, 슬라이더 값) 읽어서 로봇팔 명령 반영
    • 실제 각도(angles)를 목표각에 가깝게 변화
  10. 드로잉 자동 재생 시

    • startJsonPlayback 활성화, drawMode = 1/2/3
    • 자동 재생 로직이 targetAngles$("encoder.joint_*").d를 시간에 따라 갱신
    • control()은 해당 값을 하드웨어에 반영
  11. 수동 모드

    • control()이 슬라이더/키보드 입력을 받아 움직임 처리
    • 사용자가 조작하는 대로 관절 이동
  12. 상태 업데이트 및 종료

    • 그림 완료 후 또는 앱 종료 시 팝업(w2popup) 닫기 가능
    • 필요 시 키 이벤트 해제 또는 기타 마무리 작업 확장 가능
    • "Download TXT" 버튼으로 motion 경로를 파일로 저장 가능 → 나중에 드롭하면 동일 그림 재현

결론

개발자는 이 구조를 바탕으로 필요한 부분을 수정하거나 기능을 추가할 수 있습니다.

함수의 개선, 부가 기능 구현, 위젯 추가 등 확장 여지가 있습니다.

이 문서를 통해 각 부분의 동작 원리를 파악하여, 향후 개발과 디버깅에 활용하시기 바랍니다.

향후 확장 계획에 대한 내용은 Ideas 문서를 참고합니다.

감사합니다.

Clone this wiki locally