{"id":33936777,"url":"https://github.com/nxsaken/clipline","last_synced_at":"2026-03-17T16:05:03.769Z","repository":{"id":203547912,"uuid":"709884094","full_name":"nxsaken/clipline","owner":"nxsaken","description":"Line segment rasterization with pixel-perfect clipping.","archived":false,"fork":false,"pushed_at":"2025-07-28T14:10:41.000Z","size":957,"stargazers_count":22,"open_issues_count":8,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-08T09:12:39.259Z","etag":null,"topics":["bresenham","clipping","graphics","line-drawing","pixels","rasterization","rust","rust-gamedev","software-rendering"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nxsaken.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-10-25T15:28:36.000Z","updated_at":"2025-08-07T23:43:45.000Z","dependencies_parsed_at":null,"dependency_job_id":"eff87f5c-d062-45cc-9d77-36ebd6da0c2a","html_url":"https://github.com/nxsaken/clipline","commit_stats":null,"previous_names":["nxsaken/clipline"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/nxsaken/clipline","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nxsaken%2Fclipline","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nxsaken%2Fclipline/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nxsaken%2Fclipline/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nxsaken%2Fclipline/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nxsaken","download_url":"https://codeload.github.com/nxsaken/clipline/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nxsaken%2Fclipline/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":27684775,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-12-12T02:00:06.775Z","response_time":129,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bresenham","clipping","graphics","line-drawing","pixels","rasterization","rust","rust-gamedev","software-rendering"],"created_at":"2025-12-12T14:34:11.802Z","updated_at":"2026-03-17T16:05:03.763Z","avatar_url":"https://github.com/nxsaken.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# clipline\n\n[![CI](https://github.com/nxsaken/clipline/actions/workflows/ci.yml/badge.svg)](https://github.com/nxsaken/clipline/actions/workflows/ci.yml)\n[![crates.io](https://img.shields.io/crates/v/clipline.svg)](https://crates.io/crates/clipline)\n[![docs.rs](https://img.shields.io/docsrs/clipline)](https://docs.rs/clipline/latest/clipline)\n[![downloads](https://img.shields.io/crates/d/clipline.svg)](https://crates.io/crates/clipline)\n\nThis crate provides iterators over the rasterized points of directed, half-open line segments with pixel-perfect [clipping][clip] to rectangular regions.\n\nSee the [documentation](https://docs.rs/clipline/latest/clipline) for details,\nor the example below.\n\n## Features\n\n- Supports unsigned and signed coordinates of most sizes.\n  - Defines the iterators on the entire domains of the underlying numeric types.\n  - Avoids integer overflow without overhead.\n- Guarantees that clipped segments match the unclipped versions of themselves.\n- Usable in `const` contexts and `#![no_std]` environments.\n\n![`clipline` in action](img/clip.gif)\n\n## Usage\n\n```rust\nuse clipline::*;\n\n/// Width of the pixel buffer.\nconst WIDTH: u16 = 256;\n/// Height of the pixel buffer.\nconst HEIGHT: u16 = 240;\n\n/// A function that operates on a single pixel in a pixel buffer.\n///\n/// # Safety\n///\n/// `i \u003c WIDTH`, `j \u003c HEIGHT`.\nunsafe fn draw(pixels: \u0026mut [bool], i: u16, j: u16, pixel: bool) {\n    let index = j as usize * WIDTH as usize + i as usize;\n    debug_assert!(index \u003c pixels.len());\n    unsafe { *pixels.get_unchecked_mut(index) = pixel; }\n}\n\nfn main() {\n    let mut pixels = [false; WIDTH as usize * HEIGHT as usize];\n  \n    // This defines a clipping region (0, 0, WIDTH-1, HEIGHT-1),\n    // which covers all valid indices of the pixel buffer.\n    let clip = Clip::\u003ci16\u003e::from_size(WIDTH, HEIGHT).unwrap();\n  \n    // For Clip, *_proj involves a simple cast from i16 to u16.\n    clip.line_b_proj(-32, -64, 320, 256)\n        // This will panic if the line segment is completely outside the region.\n        .unwrap()\n        // This iterates over all points inside the region, relative to that region.\n        // Effectively this allows to safely index into the underlying buffer.\n        .for_each(|(i, j)| {\n            // SAFETY: i \u003c WIDTH, j \u003c HEIGHT.\n            unsafe { draw(\u0026mut pixels, i, j, true) }\n        });\n  \n    // This is how you can construct an unclipped line segment iterator.\n    LineD::\u003cu16\u003e::new(1, 2, 31, 32)\n        // This will panic if the segment is not diagonal.\n        .unwrap()\n        // By construction, all points of this line segment lie inside the region, thus\n        // clipping can be skipped. Do this if you are sure your line segments are inside.\n        .for_each(|(i, j)| {\n            // SAFETY: i \u003c WIDTH, j \u003c HEIGHT.\n            unsafe { draw(\u0026mut pixels, i, j, true) }\n        });\n  \n    // (-32, 16) -\u003e (64, 16)\n    LineAx::\u003ci16\u003e::new(16, -32, 64)\n        // This is a naive pointwise clip-projection.\n        // It's much slower than clipping the segment as a whole.\n        .filter_map(|(x, y)| clip.point_proj(x, y))\n        // But it gets the job done.\n        .for_each(|(i, j)| {\n            // SAFETY: i \u003c WIDTH, j \u003c HEIGHT.\n            unsafe { draw(\u0026mut pixels, i, j, true) }\n        });\n  \n    // This defines a clipping region (16, 32, 16 + WIDTH - 1, 32 + HEIGHT - 1),\n    // which covers all valid indices of the pixel buffer *after projection*.\n    let clip = Viewport::\u003ci16\u003e::from_min_size(16, 32, WIDTH, HEIGHT).unwrap();\n  \n    // For Viewport, *_proj involves subtracting the minimum corner of the Viewport\n    // from all the clipped coordinates.\n    clip.line_b_proj(-16, -32, 336, 288)\n        // This is equivalent to the first example (we just shifted the original line segment).\n        .unwrap()\n        // This iterates over all points inside the region, relative to that region.\n        // Effectively this allows to safely index into the underlying buffer.\n        .for_each(|(i, j)| {\n            // SAFETY: i \u003c WIDTH, j \u003c HEIGHT.\n            unsafe { draw(\u0026mut pixels, i, j, true) }\n        });\n  \n    fn do_at_world_pos(x: i16, y: i16) {\n        println!(\"doing something at world position {x}, {y}\")\n    }\n  \n    // Both Clip and Viewport support clipping without projection.\n    // This could be useful if you want to iterate over a line segment\n    // in \"world-space\" (represented by signed or unsigned coordinates),\n    // restricted to a region. It's not safe to use this to index into a grid.\n    clip.line_b(-16, -32, 336, 288)\n        .unwrap()\n        .for_each(|(x, y)| do_at_world_pos(x, y));\n  \n    // Unsigned Clips have infallible from_max constructors\n    // and do not provide *_proj methods (no need).\n    let mut line = Clip::\u003cu16\u003e::from_max(WIDTH - 1, HEIGHT - 1)\n        .line_b(1, 2, 320, 256)\n        .unwrap();\n  \n    // custom iteration APIs are available in const contexts\n    while let Some((i, j)) = line.pop_head() {\n        // SAFETY: i \u003c WIDTH, j \u003c HEIGHT.\n        unsafe { draw(\u0026mut pixels, i, j, true) }\n    }\n}\n```\n\n## References\n\n`clipline` synthesizes the algorithms from the following papers:\n\n* [A fast two-dimensional line clipping algorithm via line encoding][spy],\n  Mark S. Sobkow, Paul Pospisil, Yee-Hong Yang, 1987.\n* [A new approach to parametric line clipping][dorr],\n  Michael Dörr, 1990.\n* [Bresenham's Line Generation Algorithm with Built-in Clipping][kuzmin],\n  Yevgeny P. Kuzmin, 1995.\n\n[clip]: https://en.wikipedia.org/wiki/Line_clipping\n[bres]: https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm\n[spy]: https://doi.org/10.1016/0097-8493(87)90061-6\n[dorr]: https://doi.org/10.1016/0097-8493(90)90067-8\n[kuzmin]: https://doi.org/10.1111/1467-8659.1450275","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnxsaken%2Fclipline","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnxsaken%2Fclipline","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnxsaken%2Fclipline/lists"}