Skip to main content

bin_packing/
lib.rs

1//! Cut list optimization and bin packing for 1D (linear stock), 2D (sheet
2//! stock), and 3D (box) problems.
3//!
4//! The crate exposes three shipping entry points by default:
5//!
6//! - [`one_d::solve_1d`] — cutting stock / linear bin packing. Supports first-fit
7//!   decreasing, best-fit decreasing, multistart local search, and an exact
8//!   column-generation backend that reports an LP lower bound.
9//! - [`two_d::solve_2d`] — rectangular sheet packing. Supports the MaxRects family
10//!   (best-area, best-short-side-fit, best-long-side-fit, bottom-left, contact-point),
11//!   the Skyline family (default and minimum-waste), Guillotine beam search with
12//!   configurable ranking and split rules (best-short-side-fit, best-long-side-fit,
13//!   shorter- and longer-leftover-axis, min- and max-area splits), shelf heuristics
14//!   (NFDH, FFDH, BFDH), and a multistart meta-strategy.
15//! - [`three_d::solve_3d`] — rectangular box packing. Supports Extreme Points,
16//!   Guillotine 3D, layer/wall/column builders, DBLF, volume-sorted baselines,
17//!   randomized meta-strategies, and a restricted exact backend.
18//!
19//! The `three-d` feature (enabled by default) provides the 3D rectangular box
20//! packing module with a 29-algorithm catalog. The legacy `three-d-preview`
21//! alias remains available as a compatibility shim.
22//!
23//! The 1D and 2D entry points ship an `Auto` mode that runs multiple strategies and
24//! returns the best candidate, ranked lexicographically by unplaced count, stock /
25//! sheet count, waste, and cost (with `exact` as a secondary tiebreaker for 1D).
26//!
27//! Additional capabilities:
28//!
29//! - Multiple stock / sheet types per problem, each with its own cost and optional
30//!   inventory cap. When 1D inventory caps are present, [`one_d::solve_1d`] also
31//!   reports a relaxed-inventory procurement estimate per stock type.
32//! - Per-item rotation control for 2D demands, plus a `guillotine_required` flag
33//!   that restricts the solver to guillotine-compatible layouts.
34//! - Kerf and trim modeling for 1D cuts so layouts match physical cut lists.
35//! - Reproducible randomized search via an optional `seed`.
36//! - Automatic multi-core parallelism via [`rayon`] when the `parallel` feature
37//!   is enabled (on by default). Auto-mode solvers dispatch algorithms in
38//!   parallel and multi-start / GRASP / local-search meta-strategies run their
39//!   iterations concurrently. Falls back to sequential execution on single-core
40//!   hosts or when the feature is disabled.
41//! - Structured [`BinPackingError`] variants for input validation, infeasible
42//!   demands (1D, 2D, and 3D), and unsupported solver configurations.
43//! - `metrics` blocks with iteration counts, explored states, and diagnostic notes.
44//!
45//! All problem, option, solution, and metrics types derive [`serde::Serialize`] and
46//! [`serde::Deserialize`] so they can flow through JSON APIs or other wire formats
47//! without wrapping.
48//!
49//! # Example: 1D cutting stock
50//!
51//! ```no_run
52//! # #[cfg(feature = "one-d")]
53//! # {
54//! use bin_packing::one_d::{CutDemand1D, OneDOptions, OneDProblem, Stock1D, solve_1d};
55//!
56//! let problem = OneDProblem {
57//!     stock: vec![Stock1D {
58//!         name: "bar".into(),
59//!         length: 96,
60//!         kerf: 1,
61//!         trim: 0,
62//!         cost: 1.0,
63//!         available: None,
64//!     }],
65//!     demands: vec![
66//!         CutDemand1D { name: "rail".into(), length: 45, quantity: 2 },
67//!         CutDemand1D { name: "brace".into(), length: 30, quantity: 2 },
68//!     ],
69//! };
70//! let solution = solve_1d(problem, OneDOptions::default())?;
71//! println!("stock used: {}", solution.stock_count);
72//! # Ok::<(), bin_packing::BinPackingError>(())
73//! # }
74//! # #[cfg(not(feature = "one-d"))]
75//! # Ok::<(), bin_packing::BinPackingError>(())
76//! ```
77//!
78//! # Example: 2D rectangular packing
79//!
80//! ```no_run
81//! # #[cfg(feature = "two-d")]
82//! # {
83//! use bin_packing::two_d::{RectDemand2D, Sheet2D, TwoDOptions, TwoDProblem, solve_2d};
84//!
85//! let problem = TwoDProblem {
86//!     sheets: vec![Sheet2D {
87//!         name: "plywood".into(),
88//!         width: 96,
89//!         height: 48,
90//!         cost: 1.0,
91//!         quantity: None,
92//!         kerf: 0,
93//!         edge_kerf_relief: false,
94//!     }],
95//!     demands: vec![RectDemand2D {
96//!         name: "panel".into(),
97//!         width: 24,
98//!         height: 18,
99//!         quantity: 4,
100//!         can_rotate: true,
101//!     }],
102//! };
103//! let solution = solve_2d(problem, TwoDOptions::default())?;
104//! println!("sheets used: {}", solution.sheet_count);
105//! # Ok::<(), bin_packing::BinPackingError>(())
106//! # }
107//! # #[cfg(not(feature = "two-d"))]
108//! # Ok::<(), bin_packing::BinPackingError>(())
109//! ```
110
111#![forbid(unsafe_code)]
112#![warn(missing_docs)]
113#![deny(rustdoc::broken_intra_doc_links)]
114
115pub mod cut_plan;
116mod error;
117#[cfg(feature = "one-d")]
118pub mod one_d;
119#[cfg_attr(not(feature = "parallel"), allow(dead_code))]
120mod parallel;
121#[cfg(feature = "two-d")]
122pub mod two_d;
123
124#[cfg(feature = "three-d")]
125pub mod three_d;
126
127pub use cut_plan::CutPlanError;
128pub use error::{BinPackingError, Result};