1P by GN⁺ | ★ favorite | 댓글 1개
  • JavaScript 블록체 알파벳 생성기에서 출발해, 두 달 뒤 필기체 손글씨까지 코드로 구현한 과정임
  • 글자는 p5js 도구로 핵심 좌표를 찍고 Chaikin 곡선으로 다듬은 뒤, 가변 두께 경로로 변환됨
  • 필기체는 글자쌍마다 연결 방식이 달라, 경로 시작·끝에 0~3 태그를 붙여 연결 불가·기준선·기준선 위·x-height 근처 상태를 구분함
  • 단어를 만들 때는 글자별 2~3개 경로 중 하나를 고르고, 이웃 글자의 연결 높이에 따라 점을 추가·삭제·교체한 뒤 Perlin noise로 흔들림을 더함
  • 축소 후 필기체 글자 클래스는 26.1kb로 블록체의 9.7kb보다 크며, 여러 경로와 이웃 글자 보정 함수가 용량 증가의 원인임

블록체 생성 방식에서 필기체로 확장

  • 이전 블록체 알파벳 생성기는 각 글자 경로의 핵심 점을 코드로 정의하는 방식이었음
    • 글자마다 약 10개 점을 배치함
    • Chaikin 곡선 알고리듬으로 경로를 부드럽게 만듦
    • 경로를 길이 방향으로 두께가 변하는 형태로 변환함
    • p5js로 최종 형태를 그림
  • 블록체에서는 좌표를 코드에 직접 쓰고, 글자가 자연스러워질 때까지 점을 수동으로 조정해야 했음
  • 필기체 구현에서는 이 반복 작업을 줄이기 위해 글자 설계 도구를 따로 만듦

p5js에서 글자 경로를 설계하는 도구

  • p5js 편집기에서 핵심 점을 정의하고 출력하는 도구를 만들어 사용함
  • 도구는 스케일과 맥락을 보여주는 샘플 글자와 새 글자를 설계할 영역을 함께 제공함
    • 클릭으로 경로의 핵심 점을 배치하면 Chaikin 곡선이 적용된 결과가 표시됨
    • p 키로 편집 모드로 전환함
    • 점을 선택한 뒤 드래그해 위치를 조정함
    • enter 키로 경로를 콘솔에 출력함
  • 각 글자마다 2~3개 옵션을 만들었고, 결과 경로는 {x, y} 좌표 배열 형태가 됨
  • 자신의 손글씨를 기준으로 삼기 위해 소문자와 대문자 예시를 직접 쓰고, 그 이미지를 글자 생성 도구에 불러와 따라 그림
    • w/a/s/d 키로 이미지를 이동함
    • r/e 키로 이미지를 확대·축소함
    • 종이에 적은 숫자는 해당 영역을 글자 생성 창에 맞추기 위한 x/y 좌표임

필기체 연결 문제와 0~3 태그

  • 어떤 글자쌍은 한 글자의 핵심 점 경로 뒤에 다음 글자 경로를 붙이고 한 번에 Chaikin 곡선을 적용하는 것만으로 자연스럽게 이어짐
  • 하지만 일부 조합에서는 연결선이 글자 형태를 망가뜨림
    • na에서는 n의 마지막 점이 낮고 a의 첫 점이 높아 연결선이 a를 대각선으로 지나가며 e처럼 보일 수 있음
    • ti에서는 t가 기준선 바로 위에서 끝나고 i가 기준선에서 시작해 부자연스러운 능선이 생김
  • 특정 글자쌍마다 규칙을 쓰는 대신, 각 경로의 시작과 끝에 연결 높이 정보를 숫자로 붙임
    • 0: 다른 글자와 연결할 수 없음
    • 1: 기준선 근처에서 다른 글자와 연결함
    • 2: 기준선 바로 위에서 다른 글자와 연결함
    • 3: x-height 근처에서 다른 글자와 연결함
  • 같은 글자라도 단어 시작 위치나 앞뒤 글자에 따라 점 추가·삭제 방식이 달라짐
    • a가 단어 시작에 있으면 추가 점이 어색함
    • w 뒤의 a에서는 다른 방식으로 선이 a를 가로지를 수 있음
    • tk와 붙으면 형태가 변형될 수 있음
  • 모든 글자쌍을 테스트했고, 여러 경로 옵션과 이웃 글자별 편집으로 변형성을 만들었음
  • 이상적으로는 글자마다 5~6개 경로 옵션을 두고 싶지만, 파일 크기와 균형을 맞춰야 함

단어 생성 절차

  • 단어를 만들 때는 먼저 각 문자에 대해 2~3개 기본 경로 중 하나를 선택함
  • 선택된 경로의 시작·끝 연결 정보가 인접 글자에 전달됨
    • 같은 글자라도 선택된 경로 옵션에 따라 끝점 정보가 다를 수 있어, 모든 글자 경로를 먼저 골라야 함
  • 이후 기본 경로는 이웃 글자에 맞춰 조정됨
    • 이전 글자의 끝 높이가 2이면 현재 경로 시작점 하나를 제거할 수 있음
    • 다음 글자의 시작 높이가 1이면 특정 위치에 점을 추가할 수 있음
  • 조정 함수는 글자마다 다르게 동작함
    • q의 조정 함수는 70% 확률로 끝에 끊김을 추가하고, 이전 글자의 끝 정보나 다음 글자의 시작 정보에 따라 점을 교체하거나 추가함
    • n의 조정 함수는 다음 글자가 3에서 시작할 때 30% 확률로 끊김을 만들거나 마지막 점을 이동함
  • 모든 글자의 기본 경로를 합칠 때 1, 2, 3은 무시하고, 0이 나오면 새 경로를 시작해 끊김을 만듦
  • 합쳐진 경로에는 Chaikin 곡선을 적용하고, 가변 두께 형태로 바꾼 뒤, Perlin noise로 약간의 흔들림을 추가함

용량과 활용 방식

  • 블록체용 글자 클래스는 9.7kb였고, 축소 처리한 필기체 글자 클래스는 현재 26.1kb
  • 필기체 쪽이 더 큰 이유는 글자마다 여러 경로가 있고, 인접 글자에 맞춰 점을 조정하는 함수가 들어 있기 때문임
  • 추가 절감 아이디어도 남아 있음
    • 현재 글자는 기본 폰트 크기 20을 기준으로 설계한 뒤 리사이즈함
    • 이 때문에 x: 14.5 같은 좌표가 많음
    • 기본 크기를 200으로 바꾸면 145처럼 소수점을 없애 한 글자를 줄일 수 있음
    • 이 변경은 신중히 해야 해서 이후 할 일 목록에 있음
  • 이 손글씨의 주요 용도는 작업 중인 다이어그램의 제목, 라벨, 낙서 메모임
  • 폰트 대신 인코딩된 경로를 사용하면 글자의 위치나 개별 글자 내부의 두께를 직접 바꾸는 식으로 경로를 조작할 수 있음
  • 이 손글씨 코드의 발전된 버전을 사용한 Meaningful Nonsense 프린트도 제공되며, 각 프린트는 주문 시 생성되는 고유한 결과물임

댓글과 토론

Hacker News 의견들
  • 손글씨를 인코딩하면 일방향 해시로 봐도 될 것 같음
    나중에 읽어보면 도대체 뭘 쓰려고 했는지 해독할 방법이 없음

    • 도전 과제처럼 들림! https://www.handwritingocr.com :)
    • 그래서 대학 때 필기체를 일부러 버려야 했음
    • 나도 마찬가지고, 몇 시간만 지나도 스스로도 복호화가 불가능해짐
  • Stuff Made Here의 비슷한 내용을 다룬 YouTube 영상이 꽤 멋짐: https://www.youtube.com/watch?v=cQO2XTP7QDw
    시행착오도 설명해줘서 보는 재미가 있었고, 손글씨 합성 저장소도 흥미로움: https://github.com/sjvasquez/handwriting-synthesis

  • 마지막의 아트워크가 꽤 아름다움
    다음 단계는 이걸 실제 글꼴로 만들어서 어떤 프로그램에서도 입력할 수 있게 하는 걸지 궁금함

    • 플로터용 글꼴도 이미 있음
      우리가 보통 생각하는 글꼴과는 다르게, 채워 넣을 외곽선이 아니라 따라 그릴 경로여야 함
    • 이미 곡선이 있다면 꽤 쉽게 가능함
      다만 수작업으로 해야 하거나 그렇게 하고 싶다면 지루할 수는 있음
    • 여기서 기술과 예술을 섞는 방식이 정말 좋음
  • 글자 연결이 맞지 않는다는 부분은 잘 이해가 안 됨
    내게 필기체의 핵심은 펜을 종이에서 떼지 않는 것이고, 그래서 글자를 잇는 방식은 애초에 글쓰기 안에 포함되어 있음
    작성자는 실제로 그렇게 쓰지 않아서 문제가 생긴 것처럼 보임
    그래도 글꼴을 건드리지 않고 화면에 여러 글자를 그리는 가장 단순한 방법으로 풀어낸 나머지 글은 아주 재미있음

    • “필기체의 핵심은 펜을 종이에서 떼지 않는 것”이라는 표현 자체는 맞지만, 해석은 다름
      필기체에서 글자 사이 연결은 문맥 의존적
      예를 들어 b 다음에 a가 오느냐 o가 오느냐에 따라 가독성을 위해 모양이 달라질 수 있고, 단어 안에서도 어떤 경우에는 글자 사이에서 펜을 떼고 싶을 수 있음
      그래도 필기체의 “규칙”을 어기는 건 아님
      다르게 배웠을 수도 있고 그 가르침이 맞을 수도 있지만, 연결이 문맥 의존적이지 않아야 하는 필기체 양식은 잘 모르겠음
    • 핵심은 글자 쌍마다 맞물리는 방식이 다르고, 그걸 처리해야 한다는 것처럼 보임
    • 모든 글자 쌍은 서로 다른 방식으로 이어짐
      이어지지 않는 글꼴에서도 커닝과 비슷하게, 여러 글자 시퀀스가 어떻게 보이는지 인코딩해야 함
  • 아름다움! 필기체 손글씨 글꼴이 더 많아졌으면 좋겠음
    2년 전에 만든 내 작업은 여기 있음: https://certik.github.io/slabikar-otf/

  • 멋지긴 한데, 내 글씨가 나만의 글꼴로 만들고 싶을 만큼 좋았으면 좋겠음 :)
    < 14.5 대신 기본 크기를 200으로 바꾸면 점을 145로 정의할 수 있어서 소수점 문자 하나를 줄일 수 있음
    adjust라는 함수가 보이는데 글꼴 명세는 잘 모르지만, 0,{x:12.2,y:13.2}0,[12.2,13.2]처럼 다르게 직렬화하고 adjust 함수에서 변환하면 어떨까 싶음

    • 다른 기술과 마찬가지로, 손글씨도 약간의 노력으로 개선할 수 있음