데스크탑 앱
022769c8의 개발 소스를 기준으로 합니다.
OrcaSlicer는 3D 모델과 출력 설정을 프린터용 이동·압출 명령으로 바꾸는 슬라이서입니다. 화면에 예쁜 입체 모형을 보여주는 것만으로는 물체를 만들 수 없습니다. 노즐이 어느 높이에서 어떤 선을 따라 움직이고, 그동안 재료를 얼마나 밀어낼지 정해야 합니다.
이 저장소에서 배울 핵심은 복잡한 계산을 여러 단계로 나누고, 그 결과를 사람이 조작할 수 있는 제품으로 연결하는 방법입니다. 웹 서비스 경험이 있다면 화면과 서버를 떠올려도 좋지만, 여기서는 계산 엔진과 화면이 대체로 같은 데스크톱 프로세스 안에 있습니다.
3D 모델이 완성될 건물의 모양이라면, 프리셋은 벽돌 크기·장비 성능·재료의 성질입니다. 슬라이서는 이를 묶어 “첫 층의 외벽을 돌고, 내부를 채우고, 다음 층으로 올라가라”는 작업 지시서를 만듭니다.
외형이 같은 건물도 재료와 장비가 다르면 공사 방법이 달라집니다. 그래서 좋은 슬라이서는 기하 계산뿐 아니라 설정의 조합과 장비의 차이를 다뤄야 합니다.
| 단계 | 하는 일 | 읽을 소스 |
|---|---|---|
| 모델 해석 | STL·OBJ·3MF 등의 형상과 프로젝트 정보를 읽습니다. | src/libslic3r/Format/ |
| 단면 생성 | 높이별로 메시를 잘라 윤곽과 구멍을 얻습니다. | TriangleMeshSlicer.cpp의 slice_mesh() |
| 경로 생성 | 벽, 내부 채움, 지지대처럼 성격이 다른 경로를 만듭니다. | PrintObject.cpp, Fill/, Support/ |
| 명령 생성 | 이동 순서와 압출량 등을 G-code로 내보냅니다. | Print::export_gcode(), GCode::do_export() |
| 검토·전달 | 경로를 미리 보고 파일로 저장하거나 출력 호스트로 보냅니다. | src/slic3r/GUI/, src/slic3r/Utils/PrintHost.cpp |
위 표에서 파일명만 적은 계산 파일은 src/libslic3r/ 아래에 있습니다.
실제 파이프라인에는 브림, 퍼지 타워, 경로 단순화, 재료 전환 등 더 많은 단계가 있습니다. 다섯 단계는 처음 읽기 위한 큰 지도입니다.
; 문법 이해용 예시. 특정 프린터용 출력 파일이 아닙니다.
G90 ; 좌표를 절대 위치로 해석
M82 ; 압출 위치도 절대값으로 해석
G1 Z0.20 F600 ; 높이 이동
G1 X20 Y20 F3000 ; XY 위치 이동
G1 X40 Y20 E0.75 F1200 ; 이동하며 재료 압출
이 예시에는 가열·원점 복귀·압출 초기화 등이 없습니다. 문법을 읽는 연습에만 사용합니다. 같은 형상이라도 펌웨어와 압출 모드가 다르면 필요한 명령이 달라집니다.
모델을 출력하는 데는 장비가 필요하지만, 설정이 계산에 전달되는 경로와 결과를 재사용하는 규칙은 소스만으로 추적할 수 있습니다. 이 글은 FFF/FDM 출력 경로를 중심으로 C++ 제품의 설계를 읽는 안내서입니다.
2026-10-23 GitHub Trending C++ Weekly에서 7위로 선정된 프로젝트입니다. 다만 순위만으로 유입 원인을 확정할 수는 없습니다. 아래의 주목 요인은 README의 기능 설명과 소스 구성을 바탕으로 한 해석이며, 특정 소식 때문에 순위가 올랐다는 인과관계는 확인되지 않았습니다.
README는 Bambu Lab·Prusa·Creality·Voron 등을 지원 대상으로 소개합니다.
소스에는 제조사별 resources/profiles/가 있고, PrintHost::get_print_host()는
htOctoPrint, htPrusaLink, htMoonraker 등에 따라 구현 객체를 선택합니다.
“호환된다”는 홍보 문구 뒤에 데이터와 연결 코드라는 두 종류의 확장 지점이 있는 셈입니다.
JSON 파일이 있다고 모든 장비 조합을 검증했다는 뜻은 아닙니다. 그럼에도 여러 장비의 차이를 하나의 계산 엔진으로 흡수하는 방식은 프린터 사용자와 개발자 모두에게 관심을 끌 만한 지점입니다.
README가 강조하는 기능은 온도·유량·리트랙션 교정, 벽 정밀도와 이음매 제어, 지지대 최적화입니다.
관련 코드는 src/libslic3r/calib.hpp, src/libslic3r/PerimeterGenerator.cpp,
src/libslic3r/Support/ 등으로 이어집니다.
출력 실패를 줄이고 표면을 다듬는 문제는 사용자가 결과를 직접 확인하기 쉬운 문제입니다.
README의 계보 설명에 따르면 Slic3r에서 PrusaSlicer로, 다시 Bambu Studio로 발전한 흐름 위에서 OrcaSlicer가 시작됐습니다. SuperSlicer와 Cura에서 영향을 받은 아이디어도 언급합니다. 따라서 비슷한 화면이나 클래스 이름은 공유한 역사와 연결해 읽는 편이 정확합니다.
| 비교 대상 | 관계를 읽는 관점 | OrcaSlicer에서 볼 장점 |
|---|---|---|
| Bambu Studio | README가 밝힌 주요 출발점입니다. | 여러 제조사 프로필과 다양한 출력 호스트를 한 저장소에서 추적할 수 있습니다. |
| PrusaSlicer | 공유하는 상류 계보가 있습니다. | 계승된 엔진 위에 교정·벽·이음매 기능과 제품 UI가 연결되는 사례를 읽습니다. |
| SuperSlicer | README가 기능 아이디어의 영향을 언급합니다. | 세부 출력 제어가 설정 정의·프리셋·파이프라인으로 분산되는 방식을 배웁니다. |
| Cura | README가 아이디어의 영향을 언급한 다른 슬라이서입니다. | 이 글에서는 Orca의 C++ 엔진과 wxWidgets UI가 결합하는 구체적인 구현을 살펴봅니다. |
이 표는 다른 제품에 해당 기능이 없다는 주장이나 성능 순위가 아닙니다. 동일 모델·설정·하드웨어로 비교한 측정 자료가 없으므로 “가장 빠르다”는 결론도 내리지 않습니다.
커밋 022769c8에는 src/libslic3r/CAD/와 src/slic3r/GUI/CAD/,
Python 플러그인 구현이 존재합니다. 루트 CMakeLists.txt의 SLIC3R_CAD 기본값도 켜져 있습니다.
이는 현재 개발 소스의 사실이며, 안정 릴리스 v2.4.2에 모두 들어 있다는 뜻은 아닙니다.
OrcaSlicer의 학습 가치는 새 알고리즘 하나보다, 오래된 엔진에 새 기능을 붙이면서 설정·호환성·화면 반응성을 함께 지키는 데 있습니다.
README는 매우 빠른 슬라이싱과 폭넓은 사용을 강조하지만, 그 문장 자체가 비교 벤치마크나 시장 점유율 자료는 아닙니다. 이 글은 소스에 존재하는 기능과 설계를 설명하며, 인기의 원인이나 속도 우위를 수치로 확정하지 않습니다.
웹 서비스의 백엔드에 가까운 역할은 src/libslic3r/가 맡습니다.
모델·설정·단면·경로를 다루는 라이브러리이며, 외부 HTTP 서버를 띄워야 슬라이싱할 수 있는 구조는 아닙니다.
루트와 src/CMakeLists.txt 모두 기본 C++ 표준을 17로 설정합니다.
| 기술 | 소스에 적힌 버전 | 역할과 근거 파일 |
|---|---|---|
| C++ | C++17 | 계산과 제품 구현. CMakeLists.txt의 CMAKE_CXX_STANDARD. |
| Boost | 의존성 빌드 1.84.0 | 파일 시스템·로그·스레드 등. deps/Boost/Boost.cmake. |
| oneTBB | 2021.5.0 | 레이어 등의 병렬 계산. deps/TBB/TBB.cmake. |
| Eigen | 5.0.1 | 벡터·행렬 연산. deps/Eigen/Eigen.cmake. |
| CGAL | 6.2.1 | 계산기하 기능. deps/CGAL/CGAL.cmake. |
| Clipper2 | 2.0.1 | 다각형 교집합·차집합·오프셋. deps_src/clipper2/CMakeLists.txt. |
| OCCT | 8.0.1 | STEP·형상 처리와 CAD 기능. deps/OCCT/OCCT.cmake의 V8_0_1. |
| cereal | 1.3.0 | C++ 데이터를 직렬화. deps/Cereal/Cereal.cmake. |
| CPython | 3.12.13 | 내장 Python 실행 환경. deps/python3/python3.cmake. |
| pybind11 | 3.0.1 | C++와 Python 연결. deps_src/pybind11/include/pybind11/detail/common.h의 버전 상수. |
여기서 버전은 해당 커밋의 의존성 빌드 정의 또는 포함된 소스 버전입니다. 운영체제 패키지를 이용하는 빌드나 배포본의 실제 링크 결과까지 같은 버전이라고 단정할 수는 없습니다.
find_package(Boost 1.83.0 REQUIRED ...)는 사용할 Boost를 찾는 요구 조건이고,
의존성 빌드 파일의 1.84.0은 가져와 빌드할 대상입니다. 주문 가능한 최소 규격과 실제 발주한 부품을 구분하면 버전 숫자가 달라도 이해할 수 있습니다.
주 UI는 src/slic3r/GUI/의 네이티브 C++ 코드입니다.
deps/wxWidgets/wxWidgets.cmake는 Orca용 wxWidgets 저장소의 v3.3.2를 고정하고,
src/CMakeLists.txt는 wxWidgets 3.3을 요구합니다. 창·버튼·이벤트는 wxWidgets가,
3D 장면과 경로 표시는 OpenGL 관련 코드가 담당합니다.
| 구성 | 근거 | 읽을 때 볼 질문 |
|---|---|---|
| 일반 화면 | src/slic3r/GUI/GUI_App.cpp, Plater.cpp | 사용자 동작이 어떤 계산 요청으로 바뀌는가? |
| 3D 뷰 | src/slic3r/GUI/GLCanvas3D.cpp | 모델 좌표와 화면 좌표를 어떻게 연결하는가? |
| 그래픽 초기화 | src/slic3r/GUI/OpenGLManager.cpp | 드라이버의 기능을 어떻게 검사하는가? |
| 내장 웹 UI | resources/web/ | 페이지가 C++ 쪽으로 어떤 메시지를 보내는가? |
예를 들어 resources/web/dialog/ExportPresetDialog/index.js의 RequestProfile()은
request_export_preset_profile을 보내고, HandleStudio()는 응답 명령을 확인합니다.
resources/web/include/globalapi.js에는 SendWXMessage()가 있습니다.
웹 화면이 있어도 제품 전체를 웹 앱으로 분류하면 실제 구조를 놓칩니다.
저장소에는 package.json, Cargo.toml, pyproject.toml, go.mod가 없습니다.
이 프로젝트의 빌드 출발점은 CMake입니다. JavaScript 파일이 존재한다는 이유만으로 Node.js 기반 프론트엔드 빌드를 가정하면 안 됩니다.
deps/는 외부 의존성을 준비하는 빌드 정의이고, deps_src/는 저장소에 포함된 외부 소스입니다.
거대한 C++ 제품에서는 코드 컴파일만큼 버전·패치·운영체제별 라이브러리 배치를 맞추는 일이 중요합니다.
# CMakeLists.txt의 설정 중 일부를 발췌
cmake_minimum_required(VERSION 3.13)
option(SLIC3R_GUI "Compile OrcaSlicer with GUI components (OpenGL, wxWidgets)" 1)
option(SLIC3R_CAD "Compile OrcaSlicer with the parametric Design/CAD tab (needs OCCT ModelingAlgorithms)" 1)
option(BUILD_TESTS "Build unit tests" OFF)
set(CMAKE_CXX_STANDARD 17)
CMAKE_BUILD_TYPE이 비어 있고 다중 구성 생성기도 아니면 기본값은 Release입니다.
scripts/Dockerfile은 ubuntu:24.04를 기반으로 의존성·앱·AppImage를 만드는 단계를 나눕니다.
이 Dockerfile은 개발·패키징 환경을 설명하며 웹 서버 배포의 증거가 아닙니다.
.github/workflows/build_all.yml은 Linux x86_64·aarch64, Windows x64·arm64,
macOS의 아키텍처별 빌드와 Universal 패키지 결합을 구성합니다.
unit_tests.yml은 빌드된 테스트 산출물을 내려받아 실행하고 JUnit 결과를 올립니다.
.github/workflows/clang_tidy.yml은 PR의 변경된 C++ 줄을 검사합니다.
미리 컴파일한 헤더를 끈 컴파일 데이터베이스를 사용해, 우연히 다른 헤더를 통해 공급된 선언에 의존하는 코드도 드러내려 합니다.
테스트 프레임워크는 tests/catch2/CMakeLists.txt 기준 Catch2 3.11.0입니다.
앱에 포함하는 패키지 도구 uv도 루트 CMake에서 0.11.21로 고정하고 플랫폼별 SHA-256을 검사합니다.
이처럼 빌드 파일은 프로그램을 만드는 명령 목록이면서, 제품에 어떤 부품을 넣을지 기록한 명세서이기도 합니다.
src/OrcaSlicer.cpp의 일반 플랫폼 진입점 main()은 CLI().run(argc, argv)를 호출합니다.
CLI::run()은 실행할 작업이 없고 하위 호환성 검사(downward_check)가 꺼져 있으면 GUI 시작 경로를 선택하고, GUI를 포함한 빌드에서는 GUI_Run()으로 들어갑니다.
CLI와 GUI가 서로 다른 슬라이싱 엔진을 따로 갖는다고 이해할 필요는 없습니다.
위 그림은 주요 책임을 묶은 개념도입니다. 모든 화살표가 인접한 함수의 직접 호출을 뜻하지는 않습니다. 특히 실제 출력은 경로 생성 외에 프린터 설정 검증과 별도의 전달·시작 동작을 거칩니다.
| 객체 | 무엇을 표현하나 | 어디서 보나 |
|---|---|---|
| Model | 가져온 형상, 오브젝트와 배치 같은 입력 프로젝트 정보. | src/libslic3r/Model.hpp |
| 출력 작업 전체와 그 작업의 설정·진행 단계. | src/libslic3r/Print.hpp | |
| PrintObject | 개별 출력 대상의 층과 계산 결과. | src/libslic3r/Print.hpp |
| PrintRegion | 출력 영역에 적용되는 설정 묶음. | src/libslic3r/Print.hpp |
| Layer | 특정 높이의 단면과 경로를 다루는 층. | src/libslic3r/Layer.hpp |
Model과 Print를 구분해야 “모델은 그대로인데 설정 때문에 경로만 다시 계산한다”는 상황이 이해됩니다.
src/libslic3r/PrintApply.cpp의 Print::apply()가 그 사이를 잇습니다.
출력 작업을 다루는 Print.cpp에 모든 구현이 모여 있지는 않다는 점도 기억하세요.
Print::process()는 PrintObject::slice(), make_perimeters(),
prepare_infill(), infill() 등을 단계적으로 호출합니다.
하지만 호출됐다는 사실과 실제 계산이 다시 수행됐다는 사실은 다릅니다.
src/libslic3r/PrintBase.hpp의 PrintState::set_started()는 뮤텍스를 잡고 취소 여부를 확인한 뒤,
단계가 이미 DONE이면 false를 반환합니다.
이를 이용해 선행 단계를 다시 호출하더라도 유효한 결과는 재사용합니다.
// PrintObject.cpp의 infill()을 핵심 흐름만 남겨 재구성
void PrintObject::infill()
{
this->prepare_infill();
if (this->set_started(posInfill)) {
// 실제 구현은 TBB로 레이어별 make_fills() 수행
// 중간과 완료 직전에 취소 요청 확인
this->set_done(posInfill);
}
}
그렇다면 언제 DONE을 버릴까요?
Print::apply()에서 설정 차이를 반영하고,
PrintObject::invalidate_state_by_config_options()가 변경된 키를 재계산할 단계로 연결합니다.
| 변경 키 | 소스의 무효화 출발점 | 이유를 생각해 보기 |
|---|---|---|
layer_height | posSlice | 단면을 얻을 높이가 달라집니다. |
wall_loops | posPerimeters | 벽 경로와 그 뒤의 내부 영역 계산에 영향을 줍니다. |
sparse_infill_pattern | posPrepareInfill | 패턴 생성 전에 영역 준비부터 갱신합니다. |
sparse_infill_density | 준비 단계, 일부 경우 벽 단계도 | 이전·새 값 중 0%나 100%가 있으면 벽 단계도 무효화합니다. |
이 표는 전체 무효화 목록이 아닌 대표 분기입니다. 무효화는 뒤 단계에도 전파되므로 “출발 단계 하나만 다시 계산한다”는 뜻은 아닙니다. 특히 채움 밀도는 숫자만 바뀌는 설정처럼 보이지만, 비어 있음·일반 채움·완전 채움의 경계에서 더 많은 계산에 영향을 줍니다.
층별 설계도가 그대로인데 벽돌을 쌓는 무늬만 바꾸었다면 건물의 모든 치수를 처음부터 재지 않아도 됩니다. 반대로 층고가 바뀌면 위층의 기준도 달라집니다. 재사용하려면 먼저 “무엇이 무엇에 의존하는가”를 명확히 해야 합니다.
빠르게 만드는 핵심은 계산을 무조건 병렬화하는 것뿐 아니라, 바뀌지 않은 계산을 다시 하지 않는 데 있습니다.
src/libslic3r/Fill/FillBase.cpp의 Fill::new_from_type()는 패턴 열거값을 구현 객체로 바꿉니다.
FillBase.hpp의 Fill은 가상 함수를 제공하고, 구체적인 채움 구현은 그 인터페이스를 따릅니다.
객체 선택은 팩터리로, 바꿔 끼우는 계산 방식은 전략으로 읽을 수 있습니다.
// FillBase.cpp의 switch 중 대표 분기 발췌
case ipGyroid: return new FillGyroid();
case ipRectilinear: return new FillRectilinear();
case ipGrid: return new FillGrid();
// Fill.cpp의 소유권 처리 방식
std::unique_ptr<Fill> f =
std::unique_ptr<Fill>(Fill::new_from_type(surface_fill.params.pattern));
공장 역할의 함수가 원시 포인터를 반환해도 호출부는 std::unique_ptr로 소유권을 받습니다.
소유 객체가 범위를 벗어날 때 정리되는 RAII의 실제 사용입니다.
새 패턴을 추가할 때는 구현 클래스뿐 아니라 열거값·설정 선택지·팩터리 연결·테스트까지 찾아야 합니다.
장비 연결에도 비슷한 선택 구조가 있습니다. src/slic3r/Utils/PrintHost.cpp의
PrintHost::get_print_host()는 host_type을 보고 연결 구현을 만듭니다.
학습할 때는 서로 다른 파일에서 반복되는 “설정값 → 구체 구현”의 모양을 찾아보세요.
오래 걸리는 계산을 화면 이벤트 처리 중에 모두 수행하면 창이 멈춘 것처럼 보입니다.
src/slic3r/GUI/BackgroundSlicingProcess.cpp는 작업 스레드와 상태를 관리하고,
계산 완료 등은 wxQueueEvent()로 GUI 쪽에 전달합니다.
BackgroundSlicingProcess::start(), stop()과 작업 루프를 함께 읽으면
STATE_IDLE, STATE_STARTED, STATE_RUNNING, STATE_FINISHED의 역할이 보입니다.
루프는 조건변수로 시작·종료 요청을 기다리며, 완료 통지에는 예외 정보와 취소 상태도 반영합니다.
여기에 TBB 병렬 계산이 한 겹 더 있습니다. PrintObject::infill()은
tbb::blocked_range<size_t>로 레이어 범위를 나누고 make_fills()를 호출합니다.
“GUI와 계산을 분리하는 동시성”과 “계산 내부를 나누는 병렬성”은 목적이 다릅니다.
PrintConfig.cpp는 옵션의 타입·범위·설명·기본값을 정의하고,
PresetBundle.cpp는 제조사와 프리셋 데이터를 읽어 조합합니다.
PresetBundle::load_vendor_configs_from_json()은 이름과 달리 현재 소스에서
read_vendor()와 install_vendor_read()를 거치며 캐시 경로도 처리합니다.
read_vendor()는 조건에 따라 VendorCacheFile::load()를 시도하고,
채택되지 않으면 parse_vendor_json()으로 진행합니다.
검증 모드에서는 캐시를 사용하지 않고 JSON을 읽습니다. 다만 JSON이 제거된 배포 형태라면 캐시 손상을 JSON 재해석만으로 복구할 수는 없습니다.
| 검사 | 실제 구현 | 지키려는 성질 |
|---|---|---|
| 파일 형식 | CACHE_MAGIC = 0x4F52435A | 다른 파일을 캐시로 오해하지 않기. |
| 본문 길이 | read_cache_blob()에서 실제 파일 길이와 비교 | 잘린 파일과 잘못된 크기 필드 거부. |
| 손상 검사 | 본문의 CRC32 비교 | 저장·전송 중 데이터 손상 탐지. 디지털 서명과는 다릅니다. |
| 형식·제조사 | CACHE_VERSION = 2, read_cache_stamps() | 버전과 제조사 이름이 맞는 데이터만 채택. |
| 설정 사전 | CacheDictionary | 설정 이름과 타입을 이용해 현재 설정 정의에 대응. |
상세 설계 문서 docs/HLSD/preset-cache.md는 상속 결과를 모두 굳혀 저장하기보다,
원래 설정의 차이와 부모 이름을 보관하고 로딩 때 상속을 해결한다고 설명합니다.
선언 순서 번호만 저장하면 설정 하나를 끼워 넣었을 때 뒤 번호가 밀릴 수 있기 때문에, 이름으로 해석하는 사전이 의미를 갖습니다.
커밋 제목은 프리셋 캐시를 배포할 때 제조사 데이터 파일을 보존하는 수정입니다.
실제 scripts/build_preset_cache.sh의 listed_presets()는 제조사 목록의
sub_path를 모으고, 패키징 대상에서는 캐시가 생성된 제조사의 목록 JSON과 그 목록에 나열된 프리셋 파일을 제거합니다.
옆에 있는 색상표·별도 설정·이미지까지 지우지 않도록 구분하는 규칙이 코드에 보입니다.
캐시를 빨리 읽는 일과 올바른 값을 읽는 일은 함께 설계해야 합니다. 버전, 무결성, 이름 기반 해석, 원본이 있을 때의 재해석 경로가 모여 하나의 최적화를 제품 기능으로 만듭니다.
| 알고 싶은 것 | 시작할 곳 | 그다음 확인 |
|---|---|---|
| 설정 하나의 의미 | PrintConfig.cpp | 프로필 JSON과 PrintObject.cpp의 무효화 분기. |
| 채움 무늬 | Fill/FillBase.cpp | 선택된 클래스와 Fill.cpp의 호출부. |
| 창이 멈추는 문제 | BackgroundSlicingProcess.cpp | 작업 상태·취소·GUI 이벤트의 왕복. |
| 새 장비 설정 | resources/profiles/ | 제조사 목록·상속·버전과 호스트 연결 구현. |
| 호환성 보장 | tests/, docs/HLSD/ | 저장 형식의 버전과 과거 데이터 표본. |
아래는 커밋 022769c8의 파일을 확장자별로 읽어 센 결과입니다.
빈 줄·주석·헤더·포함된 외부 코드를 포함하고 .git은 제외했습니다.
마지막 줄에 개행이 없어도 한 줄로 계산했습니다. 중복 코드나 실제 실행 코드만 가려낸 수치는 아닙니다.
| 분류 | 파일 수 | 대략적인 줄 수 |
|---|---|---|
| C/C++·헤더 | 3,794 | 1,589,593줄 |
| Python | 43 | 25,462줄 |
| JavaScript | 52 | 16,467줄 |
| TypeScript | 2 | 490줄 |
| HTML | 73 | 22,273줄 |
| CSS | 41 | 7,984줄 |
| 셸 스크립트 | 35 | 5,257줄 |
| CMake | 134 | 13,950줄 |
C/C++·헤더는 .c .cc .cpp .cxx .h .hh .hpp .hxx .ipp .inl을 묶었습니다.
셸은 .sh .bash, CMake는 .cmake와 CMakeLists.txt입니다. HTML에는 .html과 .htm을 함께 집계했습니다.
Objective-C 계열, JSON·번역·문서 등은 이 표에 포함하지 않았으므로 표의 합이 저장소 전체 줄 수는 아닙니다.
C/C++ 묶음 안에서도 앱의 src/는 894,137줄,
deps_src/는 462,355줄, deps/는 79,422줄,
tests/는 152,429줄, sandboxes/는 1,250줄입니다.
테스트 폴더에는 Catch2 소스도 들어 있으므로 이 숫자로 자체 테스트 비중을 계산하면 왜곡됩니다.
resources/profiles/ 아래 JSON은 14,408개입니다.
이 수는 장비 대수가 아니라 제조사 목록·기본 설정·파생 프리셋·보조 데이터 등을 포함한 파일 수입니다.
프리셋 캐시가 왜 별도 하위 시스템으로 발전했는지 이해할 단서가 됩니다.
Fill::new_from_type()과 호출부의 std::unique_ptr<Fill>를 함께 읽으세요.
생성·사용·파괴를 한 함수 안에서만 찾으면 객체의 수명이 보이지 않습니다.
기반 클래스의 가상 소멸자와 호출부의 소유권을 연결해야 구체 구현이 올바르게 정리되는 이유를 이해할 수 있습니다.
PrintState의 std::scoped_lock<std::mutex>도 같은 관점입니다.
자원을 메모리에 한정하지 않고 잠금까지 확장해 보면 RAII가 제품 코드 전반에 쓰이는 방식이 보입니다.
FillGyroid가 어디서 만들어지고 어떤 포인터에 담기는지 적습니다.
이어서 FillBase.hpp의 가상 소멸자를 찾고, 반환 이후 예외가 나도 소유 객체가 정리되는 이유를 설명해 보세요.
메시는 입체 표면을 삼각형으로 표현합니다. 단면은 평면의 다각형이며, 출력 경로는 노즐 중심이 움직일 선입니다. 바깥 윤곽 그대로 노즐을 움직이면 선의 폭 때문에 원하는 치수와 달라질 수 있습니다. 윤곽을 안팎으로 이동하는 오프셋 연산이 필요한 이유입니다.
src/libslic3r/ClipperUtils.hpp와 ClipperUtils.cpp는
내부 다각형 타입과 Clipper2 사이의 접점을 제공합니다.
합집합·교집합·차집합·오프셋이 벽과 채움 영역 계산의 공통 도구가 됩니다.
tests/libslic3r/test_clipper_utils.cpp의 첫 시나리오는 사각형 안에 구멍을 둔 뒤 오프셋 결과를 확인합니다.
보조 함수 start_at_min()으로 다각형 시작 꼭짓점을 정규화하는 점도 좋습니다.
같은 테두리라도 어느 꼭짓점부터 나열했느냐는 달라질 수 있기 때문입니다.
작은 실습으로 종이에 사각형과 구멍을 그리고 각각 1칸씩 오프셋해 보세요. 두 다각형의 정점 배열이 다르다는 이유만으로 형상이 다르다고 판단하면 안 되는 사례를 직접 만들 수 있습니다.
src/libslic3r/libslic3r.h의 활성 분기에서 coord_t는 int64_t입니다.
SCALING_FACTOR_INTERNAL은 0.000001, 큰 프린터용 상수는 0.00001입니다.
libslic3r.cpp에서 전역 배율은 기본 상수로 초기화됩니다.
# 기본 배율에서 단위를 이해하기 위한 계산
내부 좌표 = mm 좌표 / 0.000001
1 mm = 내부 좌표 1,000,000
20 mm = 내부 좌표 20,000,000
# 저장 단위가 작다고 실제 출력 정밀도가 그만큼 높아지지는 않습니다.
정수 격자는 좌표 표현을 일정하게 만들지만, 모든 기하 연산이 무한히 정확해지는 것은 아닙니다.
docs/HLSD/polygon-clipping.md는 Clipper2의 일부 교점·기울기 계산에 부동소수점이 쓰인다는 점과 좌표 범위를 설명합니다.
실습에서는 mm와 내부 단위를 섞었을 때 100만 배 오류가 어디서 생기는지 적어 보세요.
PrintConfig.cpp에서 sparse_infill_density는 coPercent, 범위 0~100,
기본값 20입니다. wall_loops는 coInt, 범위 0~1000, 기본값 2입니다.
하지만 이는 옵션 정의의 기본값이지, 모든 프리셋을 선택했을 때 화면에 나타나는 값은 아닙니다.
// resources/profiles/Vzbot/process/0.20mm Standard @Vzbot.json
{
"type": "process",
"name": "0.20mm Standard @Vzbot",
"inherits": "fdm_process_Vzbot_common",
"from": "system",
"setting_id": "aufrk8Rt9qtJksVX",
"instantiation": "true",
"layer_height": "0.2",
"bottom_shell_layers": "3",
"top_shell_layers": "4"
}
이 프리셋의 부모 resources/profiles/Vzbot/process/fdm_process_Vzbot_common.json은
wall_loops를 "3", sparse_infill_density를 "15%"로 지정합니다.
부모의 top_shell_layers는 3이지만 자식은 4로 덮어씁니다.
또한 JSON에서는 많은 값이 문자열로 저장되므로 파일 표기와 엔진 내부 타입을 구분해야 합니다.
공통 레시피가 “설탕 20g”이라고 적혀 있어도, 특정 빵의 레시피가 15g으로 고치면 실제 사용량은 달라집니다. 옵션 정의는 항목의 뜻과 기본값을, 프리셋 상속은 특정 조건에서의 선택을 나타냅니다.
설정을 이해하려면 이름과 기본값만 읽지 말고, 누가 덮어쓰고 어느 계산을 다시 시작시키는지까지 따라가야 합니다.
PrintObject::infill()의 람다는 this를 캡처하고 여러 레이어를 처리합니다.
범위가 나뉜다는 사실만으로 모든 공유 데이터가 안전해지는 것은 아닙니다.
각 작업이 쓰는 레이어와 함께 참조하는 자료구조를 구분해야 합니다.
실습으로 함수 안의 값을 “레이어별 결과”, “공유해서 읽는 입력”, “취소·진행 상태”로 나눠 보세요.
throw_if_canceled()가 반복 안과 완료 전에도 호출되는 이유를 설명하면,
빠른 완료 외에 취소 반응성이라는 목표가 있음을 이해하게 됩니다.
tests/fff_print/test_print.cpp는 G-code 생성 등 출력 관련 동작을,
tests/libslic3r/test_vendor_cache.cpp는 제조사 캐시를 다룹니다.
캐시 테스트의 TempDir은 생성자에서 임시 디렉토리를 만들고 소멸자에서 정리합니다.
테스트도 C++ 자원 수명 관리의 학습 자료입니다.
docs/HLSD/design-tab.md는 CAD 레시피의 과거 버전 표본을 이용한 호환성 검사를 설명합니다.
같은 버전에서 저장하고 다시 읽는 왕복 검사만으로는, 저장·읽기 구현이 함께 잘못 바뀐 문제를 놓칠 수 있습니다.
과거 프로그램이 만든 고정 표본을 읽는 검사가 필요한 이유입니다.
예를 들어 “손상된 캐시는 거부된다”, “채움 패턴을 바꾸면 채움 준비 결과가 무효화된다”, “구멍을 포함한 오프셋은 올바른 영역을 만든다”처럼 검증하고 싶은 동작을 문장으로 써 보세요. 그 문장을 어떤 입력과 관찰 결과로 확인할지 연결하는 것이 테스트 설계입니다.
소스와 README에 명시된 조건은 아래와 같습니다. 아래의 RAM·디스크 수치는 빌드 전 검사값이므로 앱 실행 최소 사양과 구분해야 합니다.
| 항목 | 확인되는 사실 | 읽을 때의 주의점 |
|---|---|---|
| 운영체제 | README는 Windows·macOS·Linux 설치를 안내합니다. | 배포 파일의 대상 아키텍처를 구분합니다. |
| CPU 아키텍처 | CI에 Windows x64·arm64, Linux x86_64·aarch64, macOS 아키텍처별 빌드가 있습니다. | 개발 CI 구성과 과거 안정 배포 파일 목록은 같다고 단정하지 않습니다. |
| macOS 대상 | 루트 CMake의 기본 CMAKE_OSX_DEPLOYMENT_TARGET은 12.0입니다. | 직접 지정하면 달라질 수 있는 빌드 기본값입니다. |
| Linux 화면 | GUI 진입 때 DISPLAY 또는 WAYLAND_DISPLAY를 확인합니다. | 화면이 없는 환경의 GUI 실행과 CLI 작업을 구분합니다. |
| 그래픽 | OpenGL을 사용하는 3D 화면과 초기화 검사가 있습니다. | 검사 조건과 메시지의 버전 차이를 아래에서 설명합니다. |
| Linux 빌드 RAM | build_linux.sh의 기본 검사는 가용 메모리를 GiB 정수로 읽어 10 이하이면 거부합니다. | 설치된 총 RAM이 아닌 빌드 전 가용량 검사입니다. -r 옵션으로 생략할 수 있습니다. |
| Linux 빌드 디스크 | 기본 검사에서 남은 공간이 10 * 1024 * 1024 KiB 이하이면 거부합니다. | 10 GiB 경계 검사입니다. -r 옵션으로 생략할 수 있으며 최종 빌드의 총 필요량 보장은 아닙니다. |
| 프린터·인터넷 | 코드 읽기와 로컬 형상·경로 학습에는 물리 프린터가 필요 없습니다. | 장비 연결·업데이트·다운로드는 별도의 네트워크 기능입니다. |
src/slic3r/GUI/OpenGLManager.cpp의 해당 초기화 분기는
is_version_greater_or_equal_to(2, 0)으로 검사합니다.
그런데 실패 시 표시할 문장은 OpenGL 3.2보다 낮다고 안내합니다.
검사식과 사용자 문구가 서로 다릅니다.
따라서 이 분기 하나만 보고 앱 전체가 모든 OpenGL 2.0 장치에서 정상 동작한다고 보장해서도, 안내 문장만 보고 그 검사가 정확히 3.2를 요구한다고 설명해서도 안 됩니다. 실제 배포본과 드라이버의 조합은 따로 확인해야 합니다.
코드 탐색은 일반 개발용 PC와 편집기로 시작할 수 있습니다. 직접 빌드를 계획한다면 여유 있는 멀티코어 CPU, SSD, 32GB급 RAM을 학습용 권장 환경으로 고려할 만합니다. 이 수치는 공식 최소 사양이나 측정 결과가 아니라, 의존성과 병렬 컴파일의 여유를 위한 제안입니다.
우선 작은 단일 모델로 학습하고, 레이어 높이·삼각형 수·오브젝트 수를 한 번에 하나씩 늘려 관찰하세요. 대략 20mm 높이를 0.2mm 층으로 나누면 100층, 0.1mm면 200층입니다. 실제 층 수는 첫 층 설정과 형상 등에 따라 달라지고 실행 시간도 단순히 두 배가 되는 것은 아닙니다.
요구사항을 읽을 때는 사용자 실행 조건, 빌드 도구의 검사값, 학습용 권장 환경을 서로 구분해야 합니다.
노즐 지름·출력 영역·온도·펌웨어가 맞지 않으면 파일이 만들어져도 올바른 출력은 보장되지 않습니다. 처음에는 경로 검토까지 진행하고, 실제 출력은 사용하는 장비에 맞는 프리셋과 절차를 확인한 뒤 진행하세요.
다음 과제는 프린터 없이 시작할 수 있습니다. 소스 검색 명령은 저장소 루트에서 읽기만 수행하는 예입니다. 구현·빌드가 필요한 확장은 별도 연습용 사본에서 진행하고, 처음에는 분석 결과물부터 완성해 보세요.
질문: 0.20mm Standard @Vzbot의 벽 개수·채움 밀도·상단 층 수는 어디서 오는가?
PrintConfig.cpp에서 세 옵션의 타입과 기본값을 찾습니다.inherits를 따라 부모와 공통 조상 파일을 읽습니다.결과물: 옵션 세 개의 출처 표. 완료 기준: 왜 벽 3개, 채움 15%, 상단 4층으로 읽히는지 각 파일을 가리키며 설명합니다.
# 저장소 루트에서 실행하는 읽기 전용 검색
rg -n 'wall_loops|sparse_infill_density|top_shell_layers' \
src/libslic3r/PrintConfig.cpp resources/profiles/Vzbot/process
# 이름에 공백이 있으므로 경로 전체를 따옴표로 감쌉니다.
cat 'resources/profiles/Vzbot/process/0.20mm Standard @Vzbot.json'
질문: 화면에서 선택한 자이로이드 패턴은 어떤 C++ 객체로 계산되는가?
ipGyroid, FillGyroid, new_from_type을 검색합니다.FillBase.hpp의 공통 인터페이스와 FillGyroid.cpp의 구현을 연결합니다.Fill.cpp에서 생성된 객체가 어떻게 소유되고 사용되는지 적습니다.결과물: 설정값 → 팩터리 → 구현 → 호출부의 네 칸 그림. 완료 기준: 객체 생성과 알고리즘 실행이 각각 어디에서 일어나는지 구분합니다.
rg -n 'ipGyroid|FillGyroid|new_from_type' src/libslic3r/Fill
rg -n 'gyroid' src/libslic3r/PrintConfig.cpp
질문: 밀도를 15%→20%로 바꾸는 경우와 0%→20%로 바꾸는 경우는 무엇이 다른가?
PrintObject::invalidate_state_by_config_options()의 밀도 분기를 읽습니다.steps에 추가되는 단계를 적습니다.invalidate_step() 관련 구현에서 뒤 단계로의 전파를 찾습니다.결과물: 변경 전후 값·출발 단계·전파 단계 표. 완료 기준: 0% 경계를 일반적인 밀도 조정과 다르게 다루는 이유를 코드 분기와 연결합니다.
질문: 같은 다각형을 다른 정점부터 나열해도 같은 형상이라고 확인할 수 있는가?
test_clipper_utils.cpp의 square_with_hole 사례를 읽습니다.결과물: 좌표 그림과 검증 조건.
완료 기준: 배열의 시작 위치 같은 표현 차이와 실제 영역의 차이를 구분합니다.
구현으로 확장한다면 핵심 라이브러리 테스트인 tests/libslic3r/가 자연스러운 위치입니다.
질문: 캐시가 빠르면서도 잘못된 설정을 조용히 적용하지 않게 하려면 무엇을 검사해야 하는가?
PresetCacheFormat.cpp의 매직·길이·CRC·형식 버전 검사를 순서대로 적습니다.read_vendor()가 실패 뒤 어디로 가는지, JSON이 남아 있는 경우와 없는 경우를 나눕니다.tests/libslic3r/test_vendor_cache.cpp에서 관련 검사와 테스트용 데이터 생성 방식을 찾습니다.결과물: 네 실패 입력에 대한 기대 동작 표와 복구 경로 그림. 완료 기준: 거부·원본 재해석·재설치가 필요한 상황을 구분하고, CRC를 인증으로 설명하지 않습니다.
실습의 완료 기준은 파일을 많이 읽는 것이 아니라, 입력 하나가 결과와 재계산 범위에 미치는 영향을 설명하는 것입니다.
다음은 C++ 기초 문법을 조금 아는 독자를 위한 학습 제안입니다. 처음부터 전체 빌드를 성공시키는 일을 목표로 삼기보다, 작은 설정 하나와 단순한 형상을 반복해서 사용하세요. 각 주의 결과물을 다음 주에 연결하면 지식이 따로 흩어지지 않습니다.
| 주차 | 배울 내용과 읽을 곳 | 남길 결과물 |
|---|---|---|
| 1주차 | C++ 클래스·열거형·가상 함수·스마트 포인터. FillBase.hpp와 팩터리 호출부를 읽습니다. | FillGyroid의 생성·호출·파괴 흐름도. |
| 2주차 | 설정 타입과 상속. PrintConfig.cpp, Vzbot 프리셋, PresetBundle.cpp를 연결합니다. | 옵션 세 개의 타입·기본값·최종 출처 표. |
| 3주차 | 벡터·다각형·오프셋·좌표 단위. ClipperUtils와 작은 테스트를 읽습니다. | 구멍이 있는 형상의 오프셋 그림과 성질 세 개. |
| 4주차 | 단계 상태와 무효화. PrintApply.cpp, PrintObject.cpp, PrintBase.hpp를 읽습니다. | 레이어 높이·벽 개수·채움 밀도의 재계산 비교표. |
| 5주차 | 뮤텍스·조건변수·GUI 이벤트·TBB. BackgroundSlicingProcess.cpp와 infill()을 읽습니다. | 시작·완료·취소 시의 두 스레드 시간 흐름도. |
| 6주차 | 직렬화·캐시·CI·호환성. PresetCacheFormat.cpp, 캐시 테스트, 워크플로를 읽습니다. | 실패 입력별 기대 동작과 테스트 계획 한 장. |
C++가 처음이라면 참조와 포인터, const, 컨테이너, 생성자·소멸자, 예외를 먼저 익히세요.
수학은 2D 좌표·벡터·선분·넓이부터 시작하면 됩니다.
자이로이드의 수식이나 복잡한 지지대 알고리즘을 첫 관문으로 삼을 필요는 없습니다.
처음에는 “이 함수가 무슨 일을 하나”를 적고, 익숙해지면 “이 함수가 어떤 전제에 기대나”를 적으세요. 예를 들어 레이어 수가 0일 때, 취소가 들어왔을 때, 설정 타입이 바뀌었을 때의 행동을 묻는 식입니다.
TriangleMeshSlicer.cpp에서 단면 선분이 폐곡선으로 연결되는 흐름을 추적합니다.Plater.cpp와 GLCanvas3D.cpp에서 사용자 조작과 장면 갱신을 연결합니다.PythonInterpreter::initialize(), PluginManager::initialize()와 C++·Python 경계를 읽습니다.CadDocument::checkpoint()와 recompute()를 읽고 작업 이력과 파생 형상을 구분합니다.한 기능을 입력·설정·계산·저장·검증까지 연결해 읽는 경험이, 디렉토리 전체를 훑는 것보다 오래 남습니다.
| 용어 | 쉬운 설명 |
|---|---|
| 슬라이서 | 입체 형상과 설정에서 층별 출력 경로와 프린터 명령을 만드는 프로그램입니다. |
| FFF / FDM | 가열한 재료를 노즐로 내보내 층층이 쌓는 방식입니다. 이 글의 파이프라인 설명 범위입니다. |
| 메시 | 물체의 표면을 작은 면들로 표현한 데이터입니다. 삼각형 메시가 대표적입니다. |
| STL | 주로 삼각형 표면 형상을 전달하는 파일 형식입니다. 완전한 출력 프로젝트와는 다릅니다. |
| 3MF | 형상과 관련 정보를 담는 형식입니다. Orca 프로젝트에는 설정과 확장 데이터도 포함될 수 있습니다. |
| G-code | 이동·압출·온도 등 프린터 동작을 표현하는 명령입니다. 펌웨어별 차이가 있습니다. |
| 레이어 높이 | 한 층의 두께입니다. 값이 작아지면 같은 높이에 필요한 층 수가 늘어납니다. |
| 퍼리미터 / 벽 | 물체의 외곽이나 구멍을 따라 만드는 벽 경로입니다. |
| 인필 / 채움 | 내부 영역을 지지하고 강도를 만드는 경로입니다. 패턴과 밀도를 조절합니다. |
| 지지대 | 아래가 비어 있는 형상을 받치기 위해 추가하는 구조입니다. |
| 브리지 | 양쪽 지지점 사이의 빈 공간을 건너 출력하는 구간입니다. |
| 브림 | 첫 층 주변에 넓게 덧붙여 바닥 접착을 돕는 경로입니다. |
| 이음매 | 닫힌 벽 경로의 시작·끝이 만나는 자리입니다. 표면 흔적과 관련됩니다. |
| 프리셋 | 프린터·재료·출력 조건에 맞게 저장한 설정 묶음입니다. |
| 스키마 | 설정 항목의 이름·타입·범위·기본값 등 구조를 정의한 규칙입니다. |
| 상속 | 공통 설정을 가져오고 차이가 나는 값만 덮어쓰는 방식입니다. |
| 오프셋 | 테두리를 일정 거리 안쪽이나 바깥쪽으로 이동해 새 경계를 만드는 연산입니다. |
| 정수 좌표 | 정해진 작은 단위의 정수로 위치를 표현하는 방식입니다. 물리 출력 정밀도와 같지는 않습니다. |
| 팩터리 | 설정이나 조건을 보고 사용할 구체 객체를 만들어 주는 코드입니다. |
| 전략 패턴 | 같은 역할의 여러 알고리즘을 공통 인터페이스 뒤에서 바꿔 쓰는 설계입니다. |
| RAII | 객체 수명에 메모리·파일·잠금 같은 자원의 획득과 해제를 연결하는 C++ 기법입니다. |
| 무효화 | 입력이 바뀌어 기존 계산 결과를 더 이상 재사용하면 안 된다고 표시하는 일입니다. |
| 뮤텍스 | 여러 스레드가 같은 상태를 동시에 바꾸지 않도록 접근을 조절하는 잠금입니다. |
| 조건변수 | 스레드가 조건이 충족될 때까지 기다리고, 요청이 오면 깨어나게 하는 도구입니다. |
| TBB | 작업을 나누어 여러 CPU 코어에서 수행하도록 돕는 병렬 처리 라이브러리입니다. |
| 직렬화 | 객체의 데이터를 파일에 저장하거나 전달할 수 있는 바이트 형태로 바꾸는 일입니다. |
| CRC32 | 바이트가 손상됐는지 확인하는 검사값입니다. 작성자의 신원을 증명하는 서명은 아닙니다. |
| 회귀 테스트 | 수정 후 예전에 맞던 동작이 다시 깨지지 않았는지 확인하는 검사입니다. |
| 고정 표본 | 과거 형식의 저장 파일처럼 변하지 않는 입력을 두고 새 구현의 호환성을 확인하는 자료입니다. |
| CI | 코드 변경에 맞춰 빌드·검사·산출물 생성을 자동으로 수행하는 환경입니다. |
| AGPL-3.0 | 이 프로젝트의 주 라이선스입니다. 구체적인 조건은 원문과 포함된 구성요소의 고지를 확인합니다. |
Print::process()와 G-code 내보내기.기술 문서의 최신판은 여기서 인용한 의존성 버전과 다를 수 있습니다. API를 실제로 적용할 때는 저장소가 고정한 버전과 포함된 패치도 함께 확인하세요.
| 항목 | 확인 값과 범위 |
|---|---|
| 저장소 | OrcaSlicer/OrcaSlicer · 주 언어 C++. |
| 트렌딩 | GitHub Trending C++ Weekly #7 · 2026-10-23 분석 기준. |
| 스타 / 포크 | 16,087 / 3,303 · 2026-10-23 GitHub 기준. |
| 생성일 | 2022-07-16 · 저장소 생성일이며 모든 코드의 개발 시작일은 아닙니다. |
| 최신 릴리스 | v2.4.2 · 2026-07-07 · 2026-10-23 GitHub 기준. |
| 개발 소스 버전 | version.inc의 SoftFever_VERSION은 2.5.0-dev. |
| 최근 push | 2026-10-23 · GitHub 기준. |
| 최근 커밋 | 022769c8 · 2026-10-23 · fix: keep vendor data files when shipping preset caches (#16706)프리셋 캐시 배포 시 제조사 데이터 파일을 보존하는 수정. |
| 코드 규모 | C/C++·헤더 1,589,593줄 / 3,794개 파일. 이 중 앱 src/ 894,137줄. 집계 범위는 5절 참조. |
| 설정 데이터 | resources/profiles/ JSON 14,408개. 장비 대수와는 다릅니다. |
| 라이선스 | AGPL-3.0. 외부 구성요소에는 별도 고지가 있습니다. README는 Bambu 네트워킹 플러그인을 선택적인 비자유 라이브러리 기반 구성요소로 설명합니다. |
코드 분석 범위는 커밋 022769c8이며, 안정 릴리스 v2.4.2와 차이가 있습니다.
CAD·Python 플러그인 등 개발 소스의 구성을 안정 버전의 확정 기능으로 읽지 마세요.
속도·메모리·출력 품질은 실행 측정으로 검증한 값이 아닙니다. 트렌딩 유입 원인과 경쟁 제품 대비 우위도 동일 조건의 비교 실험으로 확인되지 않았습니다.
프로필 존재는 모든 장비·재료 조합의 검증을 뜻하지 않습니다. G-code 예시는 문법 학습용입니다. 실제 사용 전에는 배포 버전, 프리셋, 펌웨어, 장비 조건을 맞추어 확인하세요.
코드를 다른 제품에 활용할 때는 LICENSE.txt와 구성요소별 라이선스 고지를 읽어야 합니다.
README의 짧은 라이선스 요약은 원문의 조건 전체를 대신하지 않습니다.