Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【WIP】feat: Add rectangle packing algorithm for texture placement #6

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod export;
pub mod pack;
pub mod place;
pub mod rectpack;
pub mod texture;
17 changes: 16 additions & 1 deletion src/pack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use rayon::prelude::*;

use crate::export::AtlasExporter;
use crate::place::{PlacedTextureInfo, TexturePlacer};
use crate::rectpack::{Image, Node, Rectangle};
use crate::texture::{CroppedTexture, TextureCache};

pub type Atlas = Vec<PlacedTextureInfo>;
Expand All @@ -15,22 +16,36 @@ pub struct TexturePacker<P: TexturePlacer, E: AtlasExporter> {
pub atlases: HashMap<String, Atlas>,
placer: P,
exporter: E,
root_node: Node,
}

impl<P: TexturePlacer, E: AtlasExporter> TexturePacker<P, E> {
pub fn new(placer: P, exporter: E) -> Self {
pub fn new(placer: P, exporter: E, width: u32, height: u32) -> Self {
let root_rect = Rectangle {
left: 0,
top: 0,
right: width,
bottom: height,
};
TexturePacker {
textures: HashMap::new(),
current_atlas: Vec::new(),
atlases: HashMap::new(),
placer,
exporter,
root_node: Node::new(root_rect),
}
}

pub fn add_texture(&mut self, id: String, texture: CroppedTexture) -> PlacedTextureInfo {
let current_atlas_id = self.atlases.len();

let img = Image {
id: id.clone(),
width: texture.width,
height: texture.height,
};

if self.placer.can_place(&texture) {
let texture_info =
self.placer
Expand Down
109 changes: 109 additions & 0 deletions src/rectpack.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#[derive(Clone, Copy)]
pub struct Rectangle {
pub left: u32,
pub top: u32,
pub right: u32,
pub bottom: u32,
}

impl Rectangle {
pub fn width(&self) -> u32 {
self.right - self.left
}

pub fn height(&self) -> u32 {
self.bottom - self.top
}

pub fn fits(&self, img: &Image) -> bool {
self.width() >= img.width && self.height() >= img.height
}

pub fn fits_perfectly(&self, img: &Image) -> bool {
self.width() == img.width && self.height() == img.height
}
}

pub struct Image {
pub id: String,
pub width: u32,
pub height: u32,
}

pub struct Node {
child: [Option<Box<Node>>; 2],
rect: Rectangle,
image_id: Option<i32>,
}

impl Node {
pub fn new(rect: Rectangle) -> Self {
Node {
child: [None, None],
rect,
image_id: None,
}
}

pub fn insert(&mut self, img: &Image) -> Option<&mut Node> {
if let Some(ref mut child0) = self.child[0] {
if let Some(new_node) = child0.insert(img) {
return Some(new_node);
}
} else {
if self.image_id.is_some() {
return None;
}

if !self.rect.fits(img) {
return None;
}

if self.rect.fits_perfectly(img) {
// self.image_id = Some(img.id);
return Some(self);
}

let child0_rect;
let child1_rect;
let dw = self.rect.width() - img.width;
let dh = self.rect.height() - img.height;

if dw > dh {
child0_rect = Rectangle {
left: self.rect.left,
top: self.rect.top,
right: self.rect.left + img.width - 1,
bottom: self.rect.bottom,
};
child1_rect = Rectangle {
left: self.rect.left + img.width,
top: self.rect.top,
right: self.rect.right,
bottom: self.rect.bottom,
};
} else {
child0_rect = Rectangle {
left: self.rect.left,
top: self.rect.top,
right: self.rect.right,
bottom: self.rect.top + img.height - 1,
};
child1_rect = Rectangle {
left: self.rect.left,
top: self.rect.top + img.height,
right: self.rect.right,
bottom: self.rect.bottom,
};
}

self.child[0] = Some(Box::new(Node::new(child0_rect)));
self.child[1] = Some(Box::new(Node::new(child1_rect)));

if let Some(ref mut child0) = self.child[0] {
return child0.insert(img);
}
}
None
Comment on lines +48 to +107
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Node::insert メソッドの改善点

Node::insert メソッドは、ノードに画像を挿入するためのロジックを含んでいますが、image_id の設定がコメントアウトされています。画像が完璧にフィットする場合に image_id を設定する必要があります。

if self.rect.fits_perfectly(img) {
    self.image_id = Some(img.id.clone());
    return Some(self);
}

}
}