start work on better packing algorithm, start with scan
This commit is contained in:
parent
d9ca08f685
commit
cc26b98909
@ -7,7 +7,7 @@ GIT
|
|||||||
PATH
|
PATH
|
||||||
remote: .
|
remote: .
|
||||||
specs:
|
specs:
|
||||||
compass (0.11.1.d9e4054)
|
compass (0.11.1.d9ca08f)
|
||||||
chunky_png (~> 1.1)
|
chunky_png (~> 1.1)
|
||||||
fssm (>= 0.2.7)
|
fssm (>= 0.2.7)
|
||||||
sass (~> 3.1)
|
sass (~> 3.1)
|
||||||
@ -50,7 +50,7 @@ GEM
|
|||||||
autotest-fsevent (0.2.5)
|
autotest-fsevent (0.2.5)
|
||||||
sys-uname
|
sys-uname
|
||||||
builder (2.1.2)
|
builder (2.1.2)
|
||||||
chunky_png (1.1.1)
|
chunky_png (1.2.0)
|
||||||
compass-validator (3.0.0)
|
compass-validator (3.0.0)
|
||||||
css_parser (1.0.1)
|
css_parser (1.0.1)
|
||||||
cucumber (0.9.4)
|
cucumber (0.9.4)
|
||||||
|
18
lib/compass/sass_extensions/sprites/image_group.rb
Normal file
18
lib/compass/sass_extensions/sprites/image_group.rb
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
module Compass
|
||||||
|
module SassExtensions
|
||||||
|
module Sprites
|
||||||
|
class ImageGroup
|
||||||
|
attr_reader :images
|
||||||
|
|
||||||
|
def initialize(images = [])
|
||||||
|
@images = images
|
||||||
|
end
|
||||||
|
|
||||||
|
def best_fitting_rows
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -1,8 +1,13 @@
|
|||||||
|
require 'forwardable'
|
||||||
|
|
||||||
module Compass
|
module Compass
|
||||||
module SassExtensions
|
module SassExtensions
|
||||||
module Sprites
|
module Sprites
|
||||||
class ImageRow
|
class ImageRow
|
||||||
|
extend Forwardable
|
||||||
|
|
||||||
attr_reader :images, :max_width
|
attr_reader :images, :max_width
|
||||||
|
def_delegators :@images, :last, :delete, :empty?, :length
|
||||||
|
|
||||||
def initialize(max_width)
|
def initialize(max_width)
|
||||||
@images = []
|
@images = []
|
||||||
@ -10,16 +15,13 @@ module Compass
|
|||||||
end
|
end
|
||||||
|
|
||||||
def add(image)
|
def add(image)
|
||||||
unless image.is_a?(Compass::SassExtensions::Sprites::Image)
|
return false if !will_fit?(image)
|
||||||
raise ArgumentError, "Must be a SpriteImage"
|
|
||||||
end
|
|
||||||
if (images.inject(0) {|sum, img| sum + img.width} + image.width) > max_width
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
@images << image
|
@images << image
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
alias :<< :add
|
||||||
|
|
||||||
def height
|
def height
|
||||||
images.map(&:height).max
|
images.map(&:height).max
|
||||||
end
|
end
|
||||||
@ -28,6 +30,17 @@ module Compass
|
|||||||
images.map(&:width).max
|
images.map(&:width).max
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def total_width
|
||||||
|
images.inject(0) {|sum, img| sum + img.width }
|
||||||
|
end
|
||||||
|
|
||||||
|
def efficiency
|
||||||
|
1 - (total_width.to_f / max_width.to_f)
|
||||||
|
end
|
||||||
|
|
||||||
|
def will_fit?(image)
|
||||||
|
(total_width + image.width) <= max_width
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
81
lib/compass/sass_extensions/sprites/row_fitter.rb
Normal file
81
lib/compass/sass_extensions/sprites/row_fitter.rb
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
require 'forwardable'
|
||||||
|
|
||||||
|
module Compass
|
||||||
|
module SassExtensions
|
||||||
|
module Sprites
|
||||||
|
class RowFitter
|
||||||
|
extend Forwardable
|
||||||
|
|
||||||
|
attr_reader :images, :rows
|
||||||
|
def_delegators :rows, :[]
|
||||||
|
def initialize(images)
|
||||||
|
@images = images
|
||||||
|
@rows = []
|
||||||
|
end
|
||||||
|
|
||||||
|
def fit!(style = :scan)
|
||||||
|
send("#{style}_fit")
|
||||||
|
end
|
||||||
|
|
||||||
|
def width
|
||||||
|
@width ||= @images.collect(&:width).max
|
||||||
|
end
|
||||||
|
|
||||||
|
def efficiency
|
||||||
|
@rows.inject(0) { |sum, row| sum += row.efficiency } ** @rows.length
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def new_row(image = nil)
|
||||||
|
row = Compass::SassExtensions::Sprites::ImageRow.new(width)
|
||||||
|
row.add(image) if image
|
||||||
|
row
|
||||||
|
end
|
||||||
|
|
||||||
|
def fast_fit
|
||||||
|
row = new_row
|
||||||
|
@images.each do |image|
|
||||||
|
if !row.add(image)
|
||||||
|
@rows << row
|
||||||
|
row = new_row(image)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@rows << row
|
||||||
|
end
|
||||||
|
|
||||||
|
def scan_fit
|
||||||
|
fast_fit
|
||||||
|
|
||||||
|
moved_images = []
|
||||||
|
|
||||||
|
begin
|
||||||
|
removed = false
|
||||||
|
|
||||||
|
catch :done do
|
||||||
|
@rows.each do |row|
|
||||||
|
(@rows - [ row ]).each do |other_row|
|
||||||
|
other_row.images.each do |image|
|
||||||
|
if !moved_images.include?(image)
|
||||||
|
if row.will_fit?(image)
|
||||||
|
other_row.delete(image)
|
||||||
|
row << image
|
||||||
|
|
||||||
|
@rows.delete(other_row) if other_row.empty?
|
||||||
|
removed = true
|
||||||
|
|
||||||
|
moved_images << image
|
||||||
|
throw :done
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end while removed
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
7
test.watchr
Normal file
7
test.watchr
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
watch('test/(.*)_test\.rb') { |m| test(m[0]) }
|
||||||
|
watch('lib/compass/sass_extensions/sprites/image_group.rb') { test('test/units/sprites/image_group_test.rb') }
|
||||||
|
watch('lib/compass/sass_extensions/sprites/row_fitter.rb') { test('test/units/sprites/row_fitter_test.rb') }
|
||||||
|
|
||||||
|
def test(file = nil)
|
||||||
|
system %{ruby -I"lib:test" #{file}}.tap { |o| puts o }
|
||||||
|
end
|
@ -21,7 +21,7 @@ require 'rubygems' if need_gems
|
|||||||
require 'compass'
|
require 'compass'
|
||||||
|
|
||||||
require 'test/unit'
|
require 'test/unit'
|
||||||
|
require 'mocha'
|
||||||
|
|
||||||
%w(command_line diff io rails test_case).each do |helper|
|
%w(command_line diff io rails test_case).each do |helper|
|
||||||
require "helpers/#{helper}"
|
require "helpers/#{helper}"
|
||||||
|
9
test/units/sprites/image_group_test.rb
Normal file
9
test/units/sprites/image_group_test.rb
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
require 'test_helper'
|
||||||
|
require 'compass/sass_extensions/sprites/image_group'
|
||||||
|
|
||||||
|
class ImageGroupTest < Test::Unit::TestCase
|
||||||
|
def setup
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
@ -49,4 +49,8 @@ class ImageRowTest < Test::Unit::TestCase
|
|||||||
assert_equal 40, @image_row.height
|
assert_equal 40, @image_row.height
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should have an efficiency rating" do
|
||||||
|
populate_row
|
||||||
|
assert_equal 1 - (580.0 / 1000.0), @image_row.efficiency
|
||||||
|
end
|
||||||
end
|
end
|
82
test/units/sprites/row_fitter_test.rb
Normal file
82
test/units/sprites/row_fitter_test.rb
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
require 'test_helper'
|
||||||
|
require 'compass/sass_extensions/sprites/row_fitter'
|
||||||
|
|
||||||
|
class RowFitterTest < Test::Unit::TestCase
|
||||||
|
def setup
|
||||||
|
end
|
||||||
|
|
||||||
|
def row_fitter(images = nil)
|
||||||
|
@row_fitter ||= Compass::SassExtensions::Sprites::RowFitter.new(images)
|
||||||
|
end
|
||||||
|
|
||||||
|
def teardown
|
||||||
|
@row_fitter = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_images(dims)
|
||||||
|
dims.collect { |width, height| stub(:width => width, :height => height) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def basic_dims
|
||||||
|
[
|
||||||
|
[ 100, 10 ],
|
||||||
|
[ 80, 10 ],
|
||||||
|
[ 50, 10 ],
|
||||||
|
[ 20, 10 ],
|
||||||
|
[ 35, 10 ]
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should use the fast placement algorithm' do
|
||||||
|
images = create_images(basic_dims)
|
||||||
|
|
||||||
|
row_fitter(images)
|
||||||
|
assert_equal 100, row_fitter.width
|
||||||
|
|
||||||
|
row_fitter.fit!(:fast)
|
||||||
|
|
||||||
|
assert_equal 4, row_fitter.rows.length
|
||||||
|
|
||||||
|
assert_equal [ images[0] ], row_fitter[0].images
|
||||||
|
assert_equal [ images[1] ], row_fitter[1].images
|
||||||
|
assert_equal [ images[2], images[3] ], row_fitter[2].images
|
||||||
|
assert_equal [ images[4] ], row_fitter[3].images
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should use the scan placement algorithm' do
|
||||||
|
images = create_images(basic_dims)
|
||||||
|
|
||||||
|
row_fitter(images)
|
||||||
|
|
||||||
|
row_fitter.fit!(:scan)
|
||||||
|
|
||||||
|
assert_equal 3, row_fitter.rows.length
|
||||||
|
|
||||||
|
assert_equal [ images[0] ], row_fitter[0].images
|
||||||
|
assert_equal [ images[1], images[3] ], row_fitter[1].images
|
||||||
|
assert_equal [ images[2], images[4] ], row_fitter[2].images
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should use the scan placement algorithm and get images from the front' do
|
||||||
|
images = create_images(
|
||||||
|
[
|
||||||
|
[ 100, 10 ],
|
||||||
|
[ 35, 10 ],
|
||||||
|
[ 80, 10 ],
|
||||||
|
[ 50, 10 ],
|
||||||
|
[ 20, 10 ],
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
row_fitter(images)
|
||||||
|
|
||||||
|
row_fitter.fit!(:scan)
|
||||||
|
|
||||||
|
assert_equal 3, row_fitter.rows.length
|
||||||
|
|
||||||
|
assert_equal [ images[0] ], row_fitter[0].images
|
||||||
|
assert_equal [ images[1], images[3] ], row_fitter[1].images
|
||||||
|
assert_equal [ images[2], images[4] ], row_fitter[2].images
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
Loading…
Reference in New Issue
Block a user