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};