Rust-core + Python DSL video renderer
Project description
dwanim
Rust 코어 + Python DSL 비디오 렌더러. 프로그래밍 방식 애니메이션과 픽셀 기반 합성을 동일 엔진에서 렌더한다. Python으로 씬을 작성하면 Rust가 wgpu로 렌더링하고 ffmpeg로 인코딩한다.
Status
v1.0.0 (2026-06-03). Rust workspace, Python wheel metadata, 문서, smoke 예제는 final 1.0.0 기준으로 맞춘다. dwanim은 특정 기존 API 호환 레이어가 아니라, Rust+PyO3 렌더러 위에 문서화된 Python DSL 표면을 제공한다.
- 패키지 버전: Cargo workspace는
1.0.0, Python project는1.0.0; Python 모듈도dwanim.__version__ == "1.0.0"을 노출한다. - Rust 검증:
cargo fmt --all --check,cargo check --locked --workspace --all-targets기본/no-default/all-features,cargo test --locked --workspace --all-targets기본/no-default/all-features + release,cargo clippy --locked --workspace --all-targets기본/no-default/all-features-D warnings,cargo doc --locked기본/all-features-D warnings통과. - Python smoke:
bash scripts/python_smoke.sh통과. 스크립트는 package metadata/primitives/composites/timeline/state/layout helpers/graph helpers를OK:marker로 확인하며, TeX 경로는 환경에 따라 real LaTeX 또는 missing-tool fallback 검증 중 하나가 실행된다. - 공개 심볼: 66 pyclass + 3 module helper (
Square,camera_preset,render_silence_to) + 6 compatibility aliases (Write,Create,ReplacementTransform,IntegerMatrix,Sphere,Torus) — primitives + composites + charts + indication + 3D + compute + state/updater + declarative dynamic expressions. - Python 예시:
examples/python/*.py64개 파일 (60개hello_*.pyrunnable examples + helper/showcase modules). - 릴리스 QA: clean checkout 기준 CI, release wheel/sdist QA, Rust crate packaging, artifact smoke를 통과한 산출물을 배포 대상으로 삼는다.
자세한 변경 내역은 CHANGELOG.md, composite API는 docs/composites.md 참고.
Architecture
- Cargo 워크스페이스:
crates/아래 11개 크레이트 (resolver="3", edition 2024). - 코어:
dwanim-core(pyo3 비의존 데이터 모델·타임라인·보간). - 그래픽스:
dwanim-geom(lyon Bezier tessellation) +dwanim-render(wgpu 22 오프스크린 렌더러: clear → compute registry(Mandelbrot/Julia) → vector → text, preferred 8× MSAA with 4× fallback + 2× SSAA resolve). - 텍스트:
dwanim-text(ab_glyph + 번들 DejaVu Sans). - 인코딩:
dwanim-encode(ffmpeg 서브프로세스, H.264 encoder 자동 선택: libx264 우선, libopenh264 fallback, yuv420p + ultrafast). - Python 바인딩:
dwanim-py(PyO3 0.28 + abi3-py311, maturin 빌드). - 하니스:
dwanim-bench(공유render_to_mp4+ dev 바이너리). - LaTeX:
dwanim-tex(latex+dvisvgm-> SVG path/rect import, disk cache, 도구 미설치 시 명시적 오류 또는allow_stub=Truefallback). - 컴퓨트:
dwanim-compute(Mandelbrot/Julia fullscreen effect registry + Conway CPU simulator). - 3D:
dwanim-3d(CPU projection 기반 wireframe sphere/torus +ThreeDCamera/camera presets). - 상태:
dwanim-state(scalar input binding + derivedStateExprevaluator).
설계 결정과 릴리스 QA 메모는 현재 tracked 문서(README.md, CHANGELOG.md, docs/composites.md)와 커밋 이력에 유지한다. 로컬 .omc/ 런타임/계획 파일은 저장소 추적 대상에서 제외한다.
현재 DSL 능력 (1.0.0 QA 기준)
아래는 API inventory다. 그대로 실행 가능한 예제는 examples/python/hello_*.py와 이 문서의 Example 섹션을 기준으로 한다.
import dwanim
scene = dwanim.Scene(1920, 1080, 30, dwanim.Color.BLACK)
# Core primitives / renderable objects
c = dwanim.Circle(x, y, radius, color)
r = dwanim.Rectangle(cx, cy, width, height, color)
l = dwanim.Line(x1, y1, x2, y2, thickness, color)
t = dwanim.Text(cx=, cy=, size=, color=, content="HELLO", origin="center")
tex = dwanim.Tex(cx=, cy=, size=, color=, content="E=mc^2", origin="center")
svg = dwanim.Svg(cx=, cy=, size=, color=, content="<svg ...>")
d = dwanim.Dot(cx=, cy=, color=, radius=0.04)
p = dwanim.Polygon(points=[(x,y), ...], color=)
path = dwanim.StrokedPath(points=[(x1,y1), (x2,y2), ...], color=color, thickness=0.02)
m = dwanim.Mandelbrot(cx, cy, zoom, max_iter, color_a, color_b, alpha)
j = dwanim.Julia(c_re, c_im, zoom, max_iter, color_a, color_b, alpha)
s3 = dwanim.SphereSurface(radius, color, u_steps=12, v_steps=8, thickness=0.01)
torus = dwanim.TorusSurface(major_radius=0.55, minor_radius=0.18, color=dwanim.Color.WHITE)
# Composite helpers (Python-side decomposition — pieces() → list of primitives)
ax = dwanim.Axes(x_range=(-3, 3), y_range=(-2, 2))
ax.pieces() # list[Line + Text]
ax.c2p(x, y) # data → NDC
ax.get_graph(f, samples=60, color=...) # list[Line]
ax.get_graph_label(f, "f(x)", x=1.0) # Text
ax.get_v_line_to_graph(f, x=1.0) # Line
ax.get_h_line_to_graph(f, x=1.0) # Line
ax.get_tangent_line(f, x=1.0) # Line
ax.get_riemann_rectangles(f, x_range=(0, 1), dx=0.1) # list[Rectangle]
ax.get_area_under_graph(f, x_range=(0, 1)) # Polygon
grid = dwanim.Grid(3, 4, width=1.6, height=0.9)
grid.pieces() # list[Line]
grid.cell_center(1, 2) / cell_rect / cell_text
bars = dwanim.BarChart([1, 3, 2], labels=["A", "B", "C"], show_values=True)
bars.add_bar(4, "D") # mutable chart helper
bars.pieces() # list[Line + Rectangle + Text]
line_chart = dwanim.LineChart([(0, 1), (1, 3), (2, 2)])
line_chart.add_data_point((3, 4)) # autoscale by default
line_chart.pieces() # list[Line + Dot]
coord = dwanim.CoordinateScene(center_x=0, center_y=0, zoom=1.0)
coord.c2p(1, 1) / point_to_pixel / pixel_to_point
coord.pieces() # grid + axes + optional labels
slider = dwanim.StateSlider(dwanim.ValueTracker(0.5), label="x")
slider.pieces() # track + fill + handle + label
flash = dwanim.Flash(0.0, 0.0, n_lines=8)
flash.pieces() # list[Line] radial burst
highlight_box = dwanim.SurroundingRectangle(label, buff=0.03)
highlight_box.pieces() # list[Line] stroked rectangle
label_bg = dwanim.BackgroundRectangle(label, fill_opacity=0.75)
label_bg.pieces() # list[Rectangle]
under = dwanim.Underline(label)
under.pieces() # list[Line]
# Turing tape helpers are intentionally example-local, not public dwanim API.
# See examples/python/tape_helpers.py for Tape/TapeState/TapeTransition
# built from Grid/Line/Rectangle/Text primitives.
# Composite helpers — 모두 pieces() → list[primitive]
arrow = dwanim.Arrow((x1,y1), (x2,y2), color, tip_size=0.1, thickness=0.02)
# pieces(): [Line(shaft), Polygon(tip)]
brace = dwanim.Brace((x1,y1), (x2,y2), color, depth=0.15, thickness=0.02)
# pieces(): [Line(start→tip), Line(tip→end)] — V-shape MVP
darr = dwanim.DoubleArrow((x1,y1), (x2,y2), color, tip_size=0.1, thickness=0.02)
# pieces(): [Line(shaft), Polygon(end tip), Polygon(start tip)]
bl = dwanim.BraceLabel((x1,y1), (x2,y2), "label", color,
depth=0.15, thickness=0.02, label_offset=0.08, label_size=0.08)
# pieces(): [Line(start→tip), Line(tip→end), Text(label)]
ca = dwanim.CurvedArrow((x1,y1), (x2,y2), color, arc_offset=0.2,
tip_size=0.1, thickness=0.02, segments=16)
# pieces(): [StrokedPath(Bezier shaft), Polygon(end tip)]
cda = dwanim.CurvedDoubleArrow((x1,y1), (x2,y2), color, arc_offset=0.2,
tip_size=0.1, thickness=0.02, segments=16)
# pieces(): [Polygon(start tip), StrokedPath(Bezier shaft), Polygon(end tip)]
np_ = dwanim.NumberPlane(x_min=-1.5, x_max=1.5, y_min=-0.8, y_max=0.8,
x_step=0.2, y_step=0.2,
grid_color=dwanim.Color(0.3,0.3,0.3,1.0),
axis_color=dwanim.Color.WHITE,
thickness=0.004, axis_thickness=0.01)
# pieces(): list[Line] — grid lines + highlighted snapped axes
scene.add_layer(np_.pieces()) # bulk add 패턴 (가변 pieces)
pf = dwanim.ParametricFunction(lambda t: (t, t*t), (-1.0, 1.0), dwanim.Color.WHITE, samples=40)
dec = dwanim.DecimalNumber(3.14, cx=0.0, cy=0.0, size=0.1, color=dwanim.Color.WHITE)
integer = dwanim.Integer(42, cx=0.0, cy=0.0, size=0.1, color=dwanim.Color.WHITE)
arc = dwanim.Arc(cx=0.0, cy=0.0, radius=0.4, start_angle=0.0, end_angle=1.57, color=dwanim.Color.WHITE)
sector = dwanim.Sector(cx=0.0, cy=0.0, radius=0.4, start_angle=0.0, end_angle=1.57, color=dwanim.Color.WHITE)
annulus = dwanim.Annulus(cx=0.0, cy=0.0, inner_radius=0.2, outer_radius=0.4, color=dwanim.Color.WHITE)
ellipse = dwanim.Ellipse(cx=0.0, cy=0.0, rx=0.4, ry=0.2, color=dwanim.Color.WHITE)
poly = dwanim.RegularPolygon(cx=0.0, cy=0.0, radius=0.3, n=6, color=dwanim.Color.WHITE)
star = dwanim.Star(5, 0.0, 0.0, 0.35, 0.15, dwanim.Color.WHITE)
frac = dwanim.Fraction(1, 2, cx=0.0, cy=0.0, size=0.1, color=dwanim.Color.WHITE)
vg = dwanim.VGroup(dwanim.Circle(0.0, 0.0, 0.1, dwanim.Color.RED))
centered_pieces = vg.move_to(0.2, 0.0) # origin/get_center 기준으로 그룹 전체 이동
arranged = dwanim.VGroup(circle, label).arrange(direction="RIGHT", buff=0.05)
above = dwanim.VGroup(*arranged).next_to(target, direction="UP", buff=0.05)
box = dwanim.VGroup(*above).surrounding_rectangle(buff=0.03)
underline = dwanim.VGroup(label).underline()
number_line = dwanim.NumberLine(cx=0.0, cy=0.0, x_min=-2.0, x_max=2.0, length=1.4)
ticks = dwanim.TickMarks(-0.5, 0.0, 0.5, 0.0, count=5)
angle = dwanim.AngleMarker(cx=0.0, cy=0.0, radius=0.25, start_angle=0.0, end_angle=1.57)
right_angle = dwanim.RightAngle(cx=0.0, cy=0.0, size=0.12, color=dwanim.Color.WHITE)
# RightAngle defaults to origin="center"; use origin="corner" when (cx, cy) is the angle vertex.
complex_plane = dwanim.ComplexPlane(x_min=-1.0, x_max=1.0, y_min=-1.0, y_max=1.0)
matrix = dwanim.Matrix(rows=2, cols=2, entries=[["1", "0"], ["0", "1"]])
# Dynamic values
tracker = dwanim.ValueTracker(-0.6)
scene.add_dynamic_lambda(lambda: dwanim.Circle(tracker.get_value(), 0.0, 0.12, dwanim.Color.WHITE))
scene.play(dwanim.AnimateValue(tracker, 0.6), run_time=1.0, rate_func="smooth")
# StateExpr fast path
scene.bind_state_value("x", tracker)
x = dwanim.StateExpr.var("x")
scene.add_state("y", x * 0.5)
scene.add_dynamic_expr(dwanim.DynamicCircle(x, dwanim.StateExpr.var("y"), 0.12, dwanim.Color.WHITE))
# StateExpr supports arithmetic/reverse arithmetic, comparisons (`==`, `!=`, `<`, `<=`, `>`, `>=`), select/where,
# min/max/clamp, lerp, sin/cos/abs, and smoothstep. Python lambdas remain
# available through add_dynamic/add_dynamic_lambda as an explicit slow path.
# Composite / layers (minimal Phase 7 slice)
comp = dwanim.CompositeScene()
comp.add_layer([dwanim.Mandelbrot(-0.5, 0.0, 0.85, 96, dwanim.Color.BLACK, dwanim.Color.WHITE, 1.0)])
comp.add_layer([
(dwanim.Rectangle(0.0, 0.0, 0.65, 0.22, dwanim.Color.BLACK), 1),
(dwanim.Text(cx=-0.28, cy=0.05, size=0.12, color=dwanim.Color.WHITE, content="COMPOSITE"), 2),
])
comp.add_child("badge", dwanim.Circle(0, 0, 0.06, dwanim.Color.WHITE), x=0.4, opacity=0.7)
scene.add_composite(comp)
# 3D (minimal Phase 5 slice)
scene.set_camera(dwanim.ThreeDCamera(yaw=0.8, pitch=0.5, distance=3.0, focal_length=1.6))
scene.play(dwanim.FadeIn(dwanim.SphereSurface(0.8, dwanim.Color.WHITE)), run_time=1.0)
scene.play(
dwanim.AnimateCamera(
dwanim.ThreeDCamera(yaw=1.4, pitch=0.45, distance=3.4, focal_length=1.6)
),
run_time=1.0,
rate_func="smooth",
)
# Color
dwanim.Color(r, g, b, a) # finite 0.0..1.0 channels
dwanim.Color.RED / .WHITE / .BLACK
# Animations / groups
dwanim.FadeIn(shape)
dwanim.FadeOut(shape)
dwanim.Transform(from_shape, to_shape)
scene.play([dwanim.FadeIn(a), dwanim.FadeIn(b)], run_time=1.0, lag_ratio=0.4)
# Timeline
scene.play(
anim,
run_time=1.0,
rate_func="linear"|"smooth"|"there_and_back"|"quad"|"sine_out"|"bounce"|"elastic"|...,
)
scene.wait(duration)
scene.render("out.mp4")
Python 예시 파일 64개는 examples/python/*.py에 있으며, 그중 60개가 hello_*.py runnable examples다. 핵심 smoke 대상은 primitives, animation/timeline, Mandelbrot/Julia, Text/Tex, ValueTracker/StateExpr, CompositeScene/layers, Grid, example-local Tape helpers, 3D sphere/torus/camera, axes/graph, charts/flash, arrow/brace 계열, NumberPlane/PolarPlane/ComplexPlane, VGroup transforms, matrix/number line/tick/angle marker, Conway, pixel mode를 포함한다.
Scene.play()는 단일 animation 또는 visual animation list/tuple을 받으며, list/tuple에는 lag_ratio를 적용할 수 있다. 지원 rate function은 linear, smooth, there_and_back에 더해 rush_into, rush_from, slow_into, there_and_back_with_pause, wiggle, lingering, exponential_decay, quad*, cubic*, quart*, quint*, sine*, expo*, circ*, back*, bounce*, elastic* 계열을 포함한다.
Coordinate System
dwanim uses a center-origin world coordinate system where the shorter axis of the frame always spans [-1, +1] and the longer axis spans [-ratio, +ratio], with ratio = max(width, height) / min(width, height). The origin (0, 0) is the center of the frame.
- In a 1280×720 (landscape) frame:
y ∈ [-1, +1],x ∈ [-16/9, +16/9] ≈ [-1.778, +1.778]. - In a 720×1280 (portrait) frame:
x ∈ [-1, +1],y ∈ [-16/9, +16/9]. - In a 500×500 (square) frame: both axes
[-1, +1].
Because the renderer applies the same scale to both axes, shapes stay aspect-invariant regardless of frame ratio: a Circle(0, 0, 0.3) renders as a perfect pixel-accurate circle whether the output is landscape, portrait, or square; a Rectangle(0, 0, 0.3, 0.3) stays square; a regular polygon keeps its angles. Public primitive shapes such as Circle, Rectangle, Line, Polygon, and StrokedPath share this contract.
Text and Tex use center-origin placement by default. For bounded text/formula
layouts, pass origin="top_left", "top_right", "bottom_left", or
"bottom_right" to pin that corner of the rendered bounds to (cx, cy).
Example
import dwanim
for (w, h) in [(1280, 720), (720, 1280), (500, 500)]:
scene = dwanim.Scene(w, h, 30, dwanim.Color.BLACK)
scene.play(dwanim.FadeIn(dwanim.Circle(0.0, 0.0, 0.3, dwanim.Color.RED)), run_time=1.0)
scene.render(f"circle_{w}x{h}.mp4")
# All three outputs contain the same-sized circle at the center.
Pixel Mode (Advanced)
For pixel-precise layout — matching designs from image editors, overlaying on existing frames, or integrating with tools that emit pixel coordinates — pass coord_mode="pixel" to Scene. This switches the coordinate contract to top-left pixel coordinates:
- Origin
(0, 0)is the top-left of the frame. (width, height)is the bottom-right.- Supported 2D primitives use pixel coordinates, sizes, radii, and thicknesses (floats supported for subpixel placement). This includes
StrokedPath, so composite helpers that decompose to stroked paths and polygons stay in one coordinate system. Native-coordinate surfaces such asMandelbrot,Julia,SphereSurface, andTorusSurfacekeep their own coordinate systems.
Example
import dwanim
scene = dwanim.Scene(1280, 720, 30, dwanim.Color.BLACK, coord_mode="pixel")
scene.play(
dwanim.FadeIn(dwanim.Circle(640.0, 360.0, 100.0, dwanim.Color.RED)), # center, r=100px
run_time=0.5,
)
scene.play(
dwanim.FadeIn(dwanim.Rectangle(100.0, 100.0, 80.0, 80.0, dwanim.Color.WHITE)), # 80×80 near top-left
run_time=0.5,
)
scene.render("pixel_mode.mp4")
Only "world" (default) and "pixel" are accepted; any other string raises ValueError. World mode is the recommended default because it keeps coordinates aspect-invariant and resolution-independent.
Build
시스템 요구: Linux, Python 3.11-3.13, ffmpeg, Rust stable.
LaTeX Tex 프리미티브는 런타임에 외부 latex + dvisvgm가 필요하다. 탐색 순서는 PATH, DWANIM_TEX_BIN, /usr/local/texlive/*/bin/*이며, TeX Live 자체의 Perl 의존성까지 동작해야 한다. 실제 SVG 결과는 DWANIM_TEX_CACHE_DIR 또는 $XDG_CACHE_HOME/dwanim/tex/$HOME/.cache/dwanim/tex 아래에 캐시되고, 캐시된 SVG는 외부 도구가 없어도 재사용된다. 캐시가 없고 도구도 없으면 명시적 오류를 반환하거나 allow_stub=True일 때 placeholder path로 fallback한다. Svg는 inline SVG 또는 Svg.from_file(...)을 path/rect/circle 기반 VectorPath로 가져온다.
ValueTracker/add_dynamic_expr는 StateExpr 기반 dynamic object를 Rust 쪽에서 per-frame 평가하는 fast path다. 현재 fast path object는 DynamicCircle부터 제공한다.
add_dynamic/add_dynamic_lambda는 Python callable fallback이다. callable은 shape 하나 또는 shape iterable 하나를 반환해야 하며, tracker 값의 순수 함수처럼 쓰는 것이 안전하다. 동적으로 생성된 shape는 FadeIn/FadeOut/Transform의 타깃으로 지원하지 않는다.
타임라인 segment는 half-open으로 저장하지만, material/value/camera animation 모두 마지막 in-range frame에서 terminal state를 렌더링한다.
3D는 최소 slice라서 현재는 ThreeDCamera + SphereSurface/TorusSurface wireframe을 지원하며, depth buffer / hidden-line removal은 없다.
카메라 animation은 지원하지만 여전히 최소 slice라서 전체 3D scene semantics가 아니라 고정 wireframe object + timeline-driven camera 변화 수준만 보장한다.
카메라 animation을 시작한 뒤에는 set_camera() 대신 AnimateCamera(...)를 사용해야 한다.
Scene state bindings는 scalar input binding + derived StateExpr 평가를 지원한다. StateExpr는 산술식, 비교식(==, !=, <, <=, >, >=), select/where, min/max/clamp, lerp, sin/cos/abs, smoothstep을 포함하지만, arbitrary Python function call이나 symbolic differentiation은 1.0.0 범위 밖이다.
CompositeScene은 static layer composition에 더해 named child transform을 지원한다. Circle/Rectangle/Line/Polygon/StrokedPath child는 child-local (0, 0)을 pivot으로 position/scale/opacity/angle을 적용하며, 회전된 Rectangle은 내부적으로 polygon geometry로 렌더링한다. Text child는 position/scale/opacity만 지원하고 angle은 명시적으로 거부한다. Mandelbrot/Julia/3D surface처럼 자체 좌표계를 가진 child는 opacity만 안전하게 조합할 수 있으므로 position/scale/angle transform 대상이 아니다. CompositeScene.add_layer(...)/add_child(...)는 duplicate shape id를 즉시 거부한다. child 자체 timeline propagation은 아직 별도 Scene timeline으로 승격하지 않았다.
Scene.add_layer(...)와 CompositeScene.add_layer(...)에 plain shape를 넘기면 레이어 번호는 추가 순서대로 자동 배정된다. (shape, layer) tuple을 넘기면 명시 layer를 우선한다.
Grid helper도 최소 slice라서 현재는 정적 cell geometry/text helper만 지원하며, 셀 단위 상태 머신은 아직 아니다.
Tape/TapeState/TapeTransition은 public dwanim API가 아니라 examples/python/tape_helpers.py에만 있는 예제 전용 조합이다.
# Rust-only 빌드
cargo build --locked --workspace
cargo test --locked --workspace -- --test-threads=1 # GPU 테스트 직렬 권장
cargo clippy --locked --workspace --all-targets -- -D warnings
# Python 바인딩 (maturin)
python3 -m venv .venv
source .venv/bin/activate
.venv/bin/python -m pip install -q -r requirements-release.txt
maturin develop -m crates/dwanim-py/Cargo.toml --release --features extension-module --locked
# Wheel 빌드
scripts/build_wheel.sh local # dist/*.linux_x86_64.whl, 로컬 QA/install용
scripts/build_wheel.sh sdist # dist/*.tar.gz source distribution
.venv/bin/python -m pip install -q -r requirements-release.txt # PyPI 업로드용 manylinux wheel 포함
scripts/build_wheel.sh pypi # dist/*manylinux_2_17*.whl
scripts/build_wheel.sh release # sdist + manylinux wheel
scripts/artifact_smoke.sh dist/*.whl dist/*.tar.gz
# 한 줄 검증 (TeX 환경별 분기 포함, DWANIM_SMOKE_VENV=/path/to/venv로 venv 대체 가능)
bash scripts/python_smoke.sh
Release
v1.0.0 배포는 tag v1.0.0에서 release workflow를 실행해 검증한다. release gate는 pyproject.toml의 1.0.0, Cargo workspace 1.0.0, Production/Stable classifier, tag 이름 일치를 확인한 뒤 Rust QA, Python wheel/sdist QA, artifact smoke를 수행하고 GitHub Actions artifact로 산출물을 업로드한다.
배포 업로드는 자동 publish가 아니라 수동 단계다. release workflow 산출물과 로컬 scripts/check_pypi_artifacts.py dist, scripts/artifact_smoke.sh dist/*.whl dist/*.tar.gz, scripts/check_cargo_packages.py target/package 검증을 통과한 파일만 PyPI/crates.io/GitHub Release에 업로드한다. scripts/check_cargo_packages.py target/package/*.crate처럼 archive 파일 목록을 직접 넘기는 방식도 지원한다.
License
프로젝트 코드는 MIT 라이선스다. 자세한 본문은 LICENSE-MIT를 참고한다. 배포 산출물에는 DejaVu Sans (crates/dwanim-text/fonts/DejaVuSans.ttf) 고지를 LICENSE-THIRD-PARTY.md로 포함한다. Rust/Python dependency 라이선스는 각 dependency manifest와 lockfile을 기준으로 한다.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dwanim-1.0.0.tar.gz.
File metadata
- Download URL: dwanim-1.0.0.tar.gz
- Upload date:
- Size: 733.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
220eb3ab5f25ee89b4909dc6f15642251b729b661086f226cab101bda529ec33
|
|
| MD5 |
6efeac0352507e076c2b9343cc0a42e2
|
|
| BLAKE2b-256 |
e1e3cabd60599737bc351353f4cbe65f35286422cb16a2ae3213a5b380b98330
|
File details
Details for the file dwanim-1.0.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: dwanim-1.0.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 3.3 MB
- Tags: CPython 3.11+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f1cfa04e6245e2d688247a3da6943196b1f090126b1f7607b9bd174f147f2f30
|
|
| MD5 |
4bd608f430e973e90a258b0dffae2176
|
|
| BLAKE2b-256 |
8280ef850d8f6d9942197b9349453380622efd8671665e09e90f8097b9a87295
|