Opkle(옵클) - 창작자를 위한 앱과 시스템
옵클(Opkle)은 창작자를 위한 다양한 앱과 시스템을 제공하는 개발사입니다. 전자책 에디터 앱 'Opkle editor'를 출시했고, 관련 전자책 클래스를 제공하고 있습니다.
EDITOR
CLASS
BLOG
LOGIN
표현한다는 것의
무한한 가능성,
새로운 형태로
담아내다.
새로운 형태의 콘텐츠
Opkle은 코드 없이 웹을 마음껏 만들고, 누구나 자기 화면을 그릴 수 있도록 에디터를 만들고, 그 결과물을 어디서나 즐길 수 있게 돕는 팀입니다.
텍스트와 화면과의 조화를 통해, 웹을 짓는다는 것이 그저 단순한 코딩이 아닌, 상상을 펼치고 감각을 깨우는 과정이 될 수 있도록 좋은 도구를 만들어 냅니다.
옵클 에디터 개발기: 그래픽 엔진 안의 선형대수
dev
12
텍스트힙이란 무엇인가
texthip
1
텍스트힙 이후 책읽기는 어떻게 달라졌나
texthip
2
텍스트힙은 글쓰기 문화를 어떻게 바꾸는가
texthip
3
창작자와 출판은 텍스트힙을 어떻게 활용할 수 있을까
texthip
4
8
9
10
11
12
...
12
옵클 에디터 개발기: 그래픽 엔진 안의 선형대수
11편에서 Rust 공장을 그래픽 엔진으로 다듬는 과정을 정리했다면, 그 다음에 더 깊게 들어가야 할 부분은 선형대수였습니다. 그래픽 에디터에서 이미지 레이어를 움직이고, 회전시키고, 찌그러뜨리고, 네 꼭짓점에 맞춰 원근을 바꾸고, 픽셀을 다시 샘플링하고, 래스터 이미지를 벡터 path로 바꾸는 일은 겉으로는 모두 다른 기능처럼 보입니다. 하지만 엔진 안에서는 거의 같은 언어로 설명됩니다. 벡터, 행렬, 기저, 좌표계, 사영공간, 보간, 최소제곱, 정규화, 고유한 수치 안정성의 문제입니다.
그래서 그래픽 에디터를 제대로 만들려면 수학을 UI 뒤편에 숨길 수는 있어도, 엔진 안에서 피할 수는 없습니다. 사용자는 handle을 드래그할 뿐이지만, Rust core는 그 드래그를 좌표 변환으로 해석해야 합니다. 사용자는 이미지를 기울일 뿐이지만, 내부에서는 3×3 homography가 만들어지고, 역행렬이 계산되고, output pixel마다 source coordinate가 다시 계산됩니다. 사용자는 SVG로 바꾸기 버튼을 누를 뿐이지만, 내부에서는 픽셀 격자의 연결 성분, 경계 추적, 곡선 근사, path precision의 균형이 움직입니다.
이 글은 그래서 11편의 후속이지만, 조금 더 수학 쪽으로 내려간 기록입니다. 핵심 설계를 전부 펼쳐놓는 글은 아니고, 그래픽 엔진을 만들 때 실제로 어떤 수학적 판단이 필요했는지에 대한 개발 일지에 가깝습니다. 옵클 그래픽 에디터는 단순한 캔버스 UI가 아니라, 좌표와 픽셀과 벡터가 서로 오갈 수 있는 편집 엔진이어야 했습니다.
Vector space
그래픽 엔진에서 가장 먼저 정리해야 하는 것은 모든 데이터가 결국 벡터라는 사실이었습니다. 한 픽셀의 RGB는 3차원 벡터이고, RGBA는 4차원 벡터입니다. 한 점의 위치는 2차원 벡터이고, affine transform을 위해 동차좌표로 올리면 3차원 벡터가 됩니다. 색 보정의 channel vector, 브러시의 방향 벡터, path segment의 control point, PDF page 위의 좌표까지 모두 어떤 vector space 안의 원소로 볼 수 있습니다.
이 관점을 잡으면 코드의 감각도 달라집니다. 픽셀 하나를 바꾸는 일은 단순히 숫자 네 개를 고치는 일이 아니라, `R^4` 안의 벡터를 다른 벡터로 보내는 함수입니다. 이미지 전체는 `Z²` 격자 위에 놓인 벡터장처럼 볼 수 있습니다. 브러시 stroke는 특정 영역 안의 벡터장을 다른 벡터장과 섞는 연산이고, 블러는 각 위치의 벡터를 주변 벡터들의 가중합으로 바꾸는 선형 연산에 가깝습니다.
여기서 선형성은 매우 중요합니다. 어떤 연산 T가 `T(a x + b y) = a T(x) + b T(y)`를 만족하면, 그 연산은 합성과 분해가 쉬워집니다. 블러 같은 convolution은 이상적인 조건에서는 선형 연산입니다. 이미지 A와 이미지 B를 더한 뒤 블러를 거는 것과, 각각 블러를 건 뒤 더하는 것이 같은 구조를 갖습니다. 물론 실제 엔진에서는 clamp, gamma, alpha, integer quantization 때문에 완전한 선형성은 깨집니다. 그래도 내부 사고의 출발점은 선형 연산으로 잡는 편이 훨씬 안정적입니다.
반대로 HSL 변환, gamma 보정, thresholding, clamp 같은 작업은 비선형입니다. 이 비선형 연산들은 사용자가 보기에는 자연스럽지만, 합성 순서에 민감합니다. blur를 먼저 하고 threshold를 하는 것과 threshold를 먼저 하고 blur를 하는 것은 완전히 다른 결과를 만듭니다. 그래서 그래픽 엔진에서는 어떤 처리가 선형이고 어떤 처리가 비선형인지, 어떤 순서로 묶어야 시각적으로 납득 가능한지 계속 구분해야 했습니다.
Coordinate frame
그래픽 에디터에서 좌표계는 하나가 아닙니다. 화면 좌표가 있고, 캔버스 좌표가 있고, 레이어 내부 좌표가 있고, 이미지 픽셀 좌표가 있고, export용 페이지 좌표가 있습니다. 사용자가 마우스로 잡은 점은 screen space에 있지만, 실제로 바뀌어야 하는 것은 layer local space의 geometry일 수 있습니다. 이 좌표계를 명확히 구분하지 않으면 에디터는 금방 이상해집니다.
선형대수적으로는 각 좌표계를 basis가 다른 vector space처럼 생각할 수 있습니다. 같은 점 p라도 어떤 basis에서 보느냐에 따라 좌표값이 달라집니다. 캔버스가 확대되어 있거나, 스크롤되어 있거나, 레이어가 회전되어 있으면 마우스 좌표를 그대로 이미지 픽셀 좌표로 쓸 수 없습니다. 변환 행렬을 거꾸로 따라가며 screen point를 document point로, 다시 layer-local point로 내려야 합니다.
이때 affine transform은 3×3 행렬로 통일하는 것이 좋습니다. 2차원 좌표 `(x, y)`를 동차좌표 `(x, y, 1)`로 올리면 translation, rotation, scale, shear를 모두 하나의 행렬 곱으로 표현할 수 있습니다. 예를 들어 이동은 마지막 열에 들어가고, 회전과 확대는 좌상단 2×2 block 안에 들어갑니다. 이렇게 하면 여러 변환을 순서대로 적용할 때 행렬 곱으로 합성할 수 있습니다.
중요한 것은 행렬 곱의 순서입니다. `ABp`는 p에 먼저 B를 적용하고 그 다음 A를 적용한다는 뜻입니다. 사용자가 보기에는 “이동하고 회전했다”와 “회전하고 이동했다”가 비슷해 보일 수 있지만, 실제 결과는 다릅니다. 특히 레이어 중심을 기준으로 회전하려면 원점으로 옮기고, 회전하고, 다시 되돌리는 세 행렬이 필요합니다. 이런 좌표계의 순서를 엔진에서 명확히 잡아두어야 UI의 handle 움직임이 사용자의 기대와 맞습니다.
Affine layer
그래픽 에디터의 기본 레이어 조작은 대부분 affine transform입니다. 이동, 회전, 확대/축소, 기울이기는 모두 직선을 직선으로 보내고, 평행선을 평행선으로 유지합니다. 이 조건이 유지되는 동안에는 레이어의 bounding box와 handle 계산도 비교적 명확합니다. 직사각형은 평행사변형이 되고, 내부의 비율은 유지됩니다.
수식으로 보면 affine transform은 `p' = A p + t`입니다. 여기서 A는 2×2 행렬이고, t는 translation vector입니다. A의 determinant는 면적 scale과 방향성을 알려줍니다. determinant가 0에 가까워지면 변환이 퇴화합니다. 이미지가 한 줄처럼 눌리거나, 역행렬을 구하기 어려워집니다. 그래서 그래픽 엔진에서는 scale이 너무 작아지는 상황, determinant가 지나치게 작아지는 상황을 경계해야 합니다.
2×2 행렬 A는 여러 의미를 품고 있습니다. 순수 회전 행렬은 두 basis vector의 길이를 유지하고 서로 직교합니다. scale은 basis vector의 길이를 바꿉니다. shear는 basis vector 사이의 각도를 바꿉니다. 사용자가 UI에서 보기에 “조금 비틀었다”는 동작은 내부적으로 basis vector의 방향과 길이를 바꾸는 일입니다. 그래픽 에디터의 transform panel은 결국 basis를 조작하는 도구입니다.
이 구조를 정확히 잡아두면 undo/redo와 export도 안정적입니다. 화면에 보이는 픽셀을 매번 덮어쓰는 방식이 아니라, 원본 자산과 transform matrix를 분리해 보관할 수 있습니다. 사용자는 여러 번 회전하고 확대할 수 있지만, 내부에서는 가능한 한 원본과 변환 정보를 유지하다가 필요한 시점에 rasterize합니다. 그래야 반복 편집으로 인한 품질 손실을 줄일 수 있습니다.
Projective geometry
그런데 그래픽 에디터가 affine transform만 다루면 표현이 금방 막힙니다. 포스터를 벽에 붙인 것처럼 보이게 하거나, 비스듬히 촬영된 이미지를 펴거나, 네 꼭짓점을 마음대로 잡아 이미지 레이어를 맞추려면 사영 변환이 필요합니다. 이때부터는 평행선이 더 이상 평행선으로 남지 않아도 됩니다. affine geometry에서 projective geometry로 넘어가는 순간입니다.
사영 변환은 동차좌표에서 3×3 행렬 H로 표현됩니다. 2차원 점 `(x, y)`를 `(x, y, 1)`로 올리고, `H[x, y, 1]^T`를 계산한 뒤, 마지막 성분 w로 나누어 다시 `(x'/w, y'/w)`를 얻습니다. 여기서 w가 들어가는 순간이 중요합니다. affine transform에서는 마지막 성분이 항상 1처럼 유지되지만, projective transform에서는 위치에 따라 w가 달라집니다. 그 결과 멀어지는 방향으로 축소되는 원근감이 생깁니다.
사영공간에서는 `(x, y, w)`와 `(λx, λy, λw)`가 같은 점을 나타냅니다. 좌표가 하나의 대표값이 아니라 비율로 정의됩니다. 이 관점은 처음에는 낯설지만, 원근 변환을 다룰 때 굉장히 편합니다. 무한원점도 표현할 수 있고, 평행선의 소실점도 같은 수학 안으로 들어옵니다. 그래픽 에디터에서 네 꼭짓점 transform이 자연스럽게 보이려면 이 projective space를 엔진이 정확히 다뤄야 합니다.
4점 대응으로 homography를 구할 수 있는 이유도 여기에 있습니다. 하나의 3×3 행렬에는 scale을 제외하고 8개의 자유도가 있습니다. 사각형의 네 점을 다른 네 점으로 보내는 조건은 각 점마다 x, y 두 개의 제약을 주므로 총 8개의 제약이 됩니다. 그래서 일반적인 위치의 네 점 대응은 하나의 homography를 결정합니다. 사용자가 네 모서리 handle을 움직이는 행위가 정확히 이 8자유도 변환을 지정하는 셈입니다.
DLT와 정규화
homography를 구할 때는 대응점에서 선형 방정식을 세웁니다. source point `p = (x, y, 1)`와 target point `p' = (u, v, 1)`가 있을 때, `p' × H p = 0`이라는 관계를 만들 수 있습니다. cross product가 0이라는 것은 두 동차좌표 벡터가 같은 방향, 즉 같은 projective point를 가리킨다는 뜻입니다. 이 식을 정리하면 H의 원소들에 대한 선형 방정식 두 개가 나옵니다.
네 점 대응이면 이런 방정식이 여덟 개 쌓입니다. H는 scale까지 포함하면 아홉 원소지만, 동차좌표에서는 전체 scale이 의미 없기 때문에 자유도는 여덟 개입니다. 그래서 마지막 원소를 1로 고정하거나, 전체 벡터의 norm을 기준으로 정규화해 해를 잡을 수 있습니다. 더 일반적으로는 행렬 A를 만들고 `Ah = 0` 형태의 homogeneous least squares 문제로 풀 수 있습니다.
여기서 수치 안정성이 중요합니다. 좌표값이 너무 크거나, 네 점이 한쪽에 몰려 있거나, 거의 일직선에 가까우면 방정식의 condition이 나빠집니다. 작은 floating point 오차가 H의 계수에 크게 증폭될 수 있습니다. 그래서 Hartley normalization을 거칩니다. 각 점 집합의 centroid를 원점으로 옮기고, 평균 거리가 `sqrt(2)` 정도가 되도록 scale을 맞춥니다. 그런 다음 normalized coordinate에서 homography를 구하고, 마지막에 다시 원래 좌표계로 되돌립니다.
이 정규화는 단순한 전처리처럼 보이지만 결과 품질에 꽤 큰 영향을 줍니다. 같은 네 점을 넣어도 좌표계가 안정적이면 행렬 계산이 덜 흔들립니다. 그래픽 에디터에서는 사용자가 아주 작은 레이어를 다루거나, 아주 큰 캔버스에서 작업하거나, 모서리를 거의 한 줄로 눌러버리는 상황이 생길 수 있습니다. 그런 상황에서도 엔진은 가능한 한 예측 가능한 결과를 만들어야 합니다.
내부적으로 모든 세부 공식을 UI에 드러낼 필요는 없습니다. 사용자는 네 꼭짓점을 잡고 움직일 뿐입니다. 하지만 Rust 공장 안에서는 그 네 점이 선형 방정식이 되고, 그 방정식이 행렬로 쌓이고, 그 행렬의 안정성이 검사되고, 그 결과가 다시 pixel sampling으로 이어집니다. 이 체인이 흔들리지 않아야 원근 도구가 실제 도구처럼 느껴집니다.
Inverse mapping
원근 변환 행렬을 얻었다고 해서 작업이 끝나는 것은 아닙니다. 실제 이미지를 만들어야 합니다. 여기서 중요한 선택이 forward mapping과 inverse mapping입니다. forward mapping은 source pixel을 target 위치로 보내는 방식입니다. 직관적이지만 문제가 있습니다. target image의 어떤 픽셀은 아무 source pixel도 받지 못해 hole이 생길 수 있습니다. 특히 확대나 원근 변형이 들어가면 빈 픽셀이 더 잘 생깁니다.
그래서 렌더링은 inverse mapping으로 잡는 편이 안정적입니다. output image의 각 픽셀 중심 `(u, v)`를 순회하면서, 역행렬 `H^-1`을 이용해 source image의 어느 좌표 `(x, y)`에서 왔는지를 찾습니다. output 격자를 빠짐없이 순회하기 때문에 hole이 생기지 않습니다. source 좌표가 이미지 밖이면 투명 픽셀로 처리하고, source 안쪽이면 sampling을 합니다.
이때 source coordinate는 보통 정수 좌표에 딱 떨어지지 않습니다. `(12.37, 45.82)` 같은 실수 좌표가 나옵니다. 그래서 nearest neighbor로 가장 가까운 픽셀을 고르면 계단 현상이 생깁니다. bilinear interpolation을 쓰면 주변 네 픽셀을 x, y 방향의 소수부에 따라 가중 평균합니다. x 방향 소수부를 tx, y 방향 소수부를 ty라고 하면 네 픽셀의 weight는 `(1 - tx)(1 - ty)`, `tx(1 - ty)`, `(1 - tx)ty`, `tx ty`가 됩니다.
이 보간은 작은 수학이지만 품질 차이를 크게 만듭니다. 특히 텍스트가 들어간 이미지나 경계가 뚜렷한 그래픽에서 nearest neighbor는 거칠고, bilinear는 훨씬 자연스럽습니다. 더 높은 차수의 보간도 생각할 수 있지만, WASM에서 반복 호출되는 편집 엔진에서는 비용과 품질의 균형이 중요합니다. 실시간 조작에서는 bilinear가 꽤 좋은 기본점이 됩니다.
알파가 들어오면 이야기가 더 예민해집니다. straight RGBA를 그대로 보간하면 투명 픽셀의 RGB가 결과에 섞여 fringe가 생깁니다. 그래서 premultiplied alpha 공간에서 보간하고 다시 straight alpha로 되돌리는 방식이 필요합니다. 수식으로는 먼저 `(r, g, b, a)`를 `(ar, ag, ab, a)`로 보내고, 네 픽셀을 가중합한 뒤, a가 0보다 크면 다시 `(ar/a, ag/a, ab/a, a)`로 복원합니다. 이 작은 차이가 원근 변환의 가장자리 품질을 만듭니다.
Eigen 감각
그래픽 에디터 구현에서 모든 행렬을 고유값 분해까지 하면서 쓰는 것은 아닙니다. 하지만 선형 변환을 이해할 때 eigenvalue와 singular value의 감각은 계속 도움이 됩니다. 어떤 변환이 한 방향을 얼마나 늘리고, 다른 방향을 얼마나 줄이는지 알면, 이미지가 왜 찌그러져 보이는지, 왜 sampling artifact가 생기는지 설명할 수 있습니다.
2×2 affine matrix를 보면 어떤 방향은 크게 확대되고, 어떤 방향은 거의 눌릴 수 있습니다. singular value가 하나는 크고 하나는 매우 작으면 이미지가 길게 늘어나거나 납작하게 압축됩니다. 이런 변환을 rasterize하면 한 방향으로는 정보가 부족하고, 다른 방향으로는 과도하게 샘플링됩니다. 그래서 품질 좋은 렌더링에서는 scale의 방향성까지 생각해야 합니다.
projective transform에서는 이 감각이 더 복잡해집니다. affine transform은 전체 영역에서 같은 선형 변환을 적용하지만, homography는 위치마다 local Jacobian이 달라집니다. 이미지의 한쪽 끝에서는 크게 확대되고, 다른 쪽 끝에서는 작게 압축될 수 있습니다. 결국 output의 각 위치에서 source image가 얼마나 늘어나거나 줄어드는지가 달라집니다. 이 때문에 원근 변환에서는 단순한 전역 scale 하나로 품질을 판단하기 어렵습니다.
실시간 에디터에서는 모든 픽셀마다 완벽한 anisotropic filtering을 하는 식으로 갈 필요는 없었습니다. 다만 이런 수학적 감각을 알고 있으면 제한과 상한을 어디에 둘지 판단하기 쉬워집니다. 출력 픽셀 수 상한, source sampling 방식, 투명 영역 처리, degenerate quadrilateral 방어가 모두 여기와 연결됩니다. 사용자는 자유롭게 네 점을 움직이지만, 엔진은 그 자유 안에서 수치적으로 말이 되는 영역을 유지해야 합니다.
Tracing math
픽셀을 SVG로 바꾸는 작업도 선형대수와 떨어져 있지 않습니다. 래스터 이미지는 격자 위의 sample이고, SVG path는 연속 평면 위의 곡선입니다. tracing은 이산 데이터에서 연속적인 geometry를 복원하는 문제입니다. 완벽한 복원은 불가능합니다. 대신 사람이 보기에 충분히 같은 형태를 갖도록 경계를 찾고, 그 경계를 적절한 path로 근사합니다.
binary tracing에서는 먼저 grayscale 값을 만들고 threshold를 정합니다. Otsu threshold는 픽셀들을 두 class로 나누었을 때 class 간 분산이 최대가 되는 임계값을 찾습니다. 수학적으로는 histogram 위에서 `σ_b²(t) = ω0(t)ω1(t)(μ0(t)-μ1(t))²`가 커지는 t를 찾는 문제입니다. 흑백으로 잘 나뉘는 이미지라면 이 값이 좋은 분리점을 줍니다.
그 다음에는 연결 성분과 경계를 봅니다. 픽셀 격자에서 같은 class에 속한 영역을 찾고, 그 영역의 boundary를 따라갑니다. 여기서 boundary는 본질적으로 계단 모양입니다. SVG path는 그 계단을 그대로 옮기는 것이 아니라, 직선 segment나 곡선 segment로 근사합니다. 너무 정밀하면 path가 지나치게 많아지고, 너무 단순하면 원래 형태가 망가집니다. path precision은 결국 근사 오차와 데이터 크기 사이의 trade-off입니다.
color tracing은 더 복잡합니다. 색 공간 안에서 비슷한 색을 묶고, 각 색상 영역의 경계를 따로 추출해야 합니다. 색상은 RGB cube 안의 점이고, 비슷함은 어떤 metric을 쓰느냐에 따라 달라집니다. 사람이 보기에 가까운 색과 RGB 유클리드 거리에서 가까운 색이 항상 같지는 않습니다. 그래서 그래픽 엔진에서는 모든 수학을 이상적인 형태로만 다루기보다, 실제 제작물에서 안정적으로 보이는 쪽을 선택해야 합니다.
output SVG를 반응형 자산으로 쓰기 좋게 만들기 위해 viewBox와 width/height 처리도 정리했습니다. viewBox는 SVG 내부 좌표계의 basis와 범위를 정의합니다. width와 height를 고정 픽셀값으로 박아버리면 EPUB이나 웹 문서 안에서 쓰기 불편해질 수 있습니다. 그래서 geometry의 내부 좌표계는 유지하되, 외부 배치는 유연하게 가져갈 수 있도록 후처리했습니다.
Page space
PDF와 export 쪽에서도 좌표계는 계속 중요했습니다. 그래픽 에디터의 캔버스 좌표와 PDF page coordinate는 같지 않습니다. 화면은 CSS pixel과 device pixel ratio의 영향을 받고, PDF는 pt 단위의 페이지 공간에서 움직입니다. EPUB이나 웹 미리보기에서는 반응형 layout이 중요하지만, PDF export에서는 고정된 page box, bleed, margin, image placement가 중요합니다.
페이지 이미지들을 PDF로 묶는 경우에도 단순히 이미지를 붙이는 것이 아닙니다. page box 안에 이미지를 fill로 넣을지 contain으로 넣을지 결정해야 합니다. fill은 페이지를 꽉 채우지만 잘릴 수 있고, contain은 전체 이미지를 보존하지만 여백이 생길 수 있습니다. 이 역시 좌표 변환 문제입니다. 이미지의 aspect ratio와 page의 aspect ratio를 비교하고, scale factor와 translation을 계산합니다.
인쇄 흐름에서는 bleed와 color chip 같은 요소도 page space 안의 geometry입니다. 도련선은 페이지 경계 바깥으로 확장되는 영역이고, color chip은 인쇄 확인을 위한 작은 sample geometry입니다. 이런 요소들은 사용자가 그래픽 에디터 안에서 만든 자산과 별개처럼 보이지만, export 순간에는 같은 좌표계 안에서 조립됩니다.
폰트와 자산 archive도 이 흐름에 붙습니다. 폰트는 텍스트를 glyph outline과 metric으로 바꾸는 계층이고, archive는 프로젝트 안의 자산들을 다시 복원 가능한 binary structure로 묶는 계층입니다. 그래픽 엔진이 실제 제작 도구가 되려면 화면의 수학만으로는 부족합니다. 좌표와 픽셀과 벡터를 파일과 페이지로 안정적으로 내보내는 수학도 같이 필요합니다.
숨겨진 공장
이 선형대수 계층을 만들면서 계속 생각한 것은 수학을 사용자에게 보이지 않게 만드는 일이었습니다. 사용자는 determinant를 보지 않습니다. 사용자는 homography의 condition number를 보지 않습니다. 사용자는 bilinear weight를 보지 않습니다. 하지만 그 값들이 안정적으로 계산되지 않으면 사용자는 바로 이상함을 느낍니다. handle이 흔들리고, 이미지 가장자리가 더러워지고, export된 결과가 미묘하게 어긋납니다.
그래서 Rust 공장 안에서는 좌표계와 행렬과 sampling을 최대한 엄격하게 다뤘습니다. 입력 buffer의 크기를 검증하고, degenerate한 변환을 방어하고, output pixel 수에 상한을 두고, alpha space를 맞추고, SVG와 PDF로 넘어갈 때 좌표계가 무너지지 않도록 했습니다. 핵심 설계의 모든 세부를 밖으로 드러낼 필요는 없지만, 내부적으로는 수학적 계약이 분명해야 했습니다.
이 단계도 완성되었습니다. 그래픽 에디터의 원근 변환, 벡터화, SVG 래스터, PDF export, page 조립은 단순한 기능 목록이 아니라 하나의 수학적 처리 흐름으로 정리되었습니다. 선형대수와 사영기하, 이산 픽셀과 연속 곡선, 색상 벡터와 alpha composition, page space와 file export가 Rust 공장 안에서 안정적으로 맞물렸습니다. 반복 검수에서도 변환, sampling, tracing, export 경로가 안정적으로 통과했고, 옵클 그래픽 에디터는 이제 UI 뒤편에 제대로 된 수학 엔진을 가진 도구가 되었습니다.
이전글
목록으로
다음글
저작권 고시
Copyright Notice
본 웹사이트의 모든 디자인 결과물 및 영상에 대한 저작권은 Abstract Cloud에 있으며, 저작권법 및 관련 법령에 의해 보호받습니다. 웹, 영상, 본문, 표지, 내지 디자인을 포함한 모든 콘텐츠는 저작권자의 자산으로, 사전 동의 없이 무단 복제, 배포, 2차 저작물 제작, 온라인 공유 등을 금지합니다. 이를 위반할 시, 저작권법에 따라 민형사상 책임을 질 수 있습니다. 정당한 구매와 저작권 보호는 창작자의 권리를 지키며, 더 나은 작품으로 보답할 힘이 됩니다.
저작권자: Abstract Cloud | 대표자: 배창규(uragen)
© Abstract Cloud. All Rights Reserved.
HOME
FAQ
이용 약관
개인정보 이용방침
help@opkle.app
010-2747-3403
상호 :
추상적 형상 디자인(Abstract cloud) |
대표자 :
배창규
사업자등록번호 :
249-74-00533
통신판매업 신고번호 :
2025-의정부송산-0634
주소 :
경기도 의정부시 부용로 49, 108동 402호
웹의 모든 콘텐츠, 디자인, 소스 코드에 대한
저작권은 Opkle에게 있습니다.