试图在红宝石中有效地存储许多没有重复区域的矩形

时间:2016-09-02 16:38:23

标签: ruby performance shape

我想在ruby中的一个变量中存储几个矩形。这是为了添加必须刷新屏幕的矩形。我尝试了一个带有top,left,height和with的矩形类,并在数组中存储了矩形变量。但是当迭代它并用它计算时,它太慢了。

添加新矩形时,屏幕上不应有任何重复项进行刷新,并且不需要花费太多时间来计算以删除重复项(在最佳情况下没有时间)。

对不起,我不是母语为英语的人,非常感谢你的想法!

需要'矩形'

类Rectangle

attr_accessor :top,:left,:height,:width

def initialize(options={})
    @top=options[:top]
    @left=options[:left]
    @height=options[:height]
    @width=options[:width]
end

def bottom
    @top+@height-1
end

def right
    @left+@width-1
end

def coveredBy?(rectangle)
    return false if @top<rectangle.top
    return false if @left<rectangle.left
    return false if bottom>rectangle.bottom
    return false if right>rectangle.right
    return true
end

def intersection!(rectangle)
    if @top<rectangle.top
        @top=rectangle.top
    end
    if @left<rectangle.left
        @left=rectangle.left
    end
    if bottom>(t=rectangle.bottom)
        @height=rectangle.bottom+1-@top
    end
    if right>(t=rectangle.right)
        @width=rectangle.right+1-@left
    end
end

def &(rectangle)
    if @top>=rectangle.top
        top=@top
    else
        top=rectangle.top
    end
    if @left>=rectangle.left
        left=@left
    else
        left=rectangle.left
    end
    if bottom<=rectangle.bottom
        height=bottom+1-top
    else
        height=rectangle.bottom+1-top
    end
    return nil if height<=0
    if right<=rectangle.right
        width=right+1-left
    else
        width=rectangle.right+1-left
    end
    return nil if width<=0
    return Rectangle.new(
        :top=>top,
        :left=>left,
        :height=>height,
        :width=>width
    )
end

def -(rectArg)
    return [] if empty?
    return [self] if bottom<rectArg.top
    return [self] if @top>rectArg.bottom
    return [self] if right<rectArg.left
    return [self] if @left>rectArg.right

    ret=[]
    line=@top
    if @top<rectArg.top
        ret[ret.count]=Rectangle.new(
            :top=>line,
            :left=>@left,
            :height=>[rectArg.top-@top,@height].min,
            :width=>@width
        )
        line=ret.first.bottom+1
    end
    if @left<rectArg.left
        ret[ret.count]=Rectangle.new(
            :top=>line,
            :left=>@left,
            :height=>[rectArg.bottom+1-line,bottom+1-line].min,
            :width=>[rectArg.left-@left,@width].min
        )
    end
    if right>rectArg.right
        ret[ret.count]=Rectangle.new(
            :top=>line,
            :left=>rectArg.right+1,
            :height=>[rectArg.bottom+1-line,bottom+1-line].min,
            :width=>[right-rectArg.right,@width].min
        )
    end
    if bottom>rectArg.bottom
        ret[ret.count]=Rectangle.new(
            :top=>rectArg.bottom+1,
            :left=>@left,
            :height=>[bottom-rectArg.bottom,@height].min,
            :width=>@width
        )
    end
    ret
 end

# Mit return true geht es deutlich schneller als mit ||.
def outside?(rectangle)
    return true if empty?
    return true if rectangle.empty?
    return true if bottom<rectangle.top
    return true if @top>rectangle.bottom
    return true if right<rectangle.left
    return true if @left>rectangle.right
    return false
 end


 # nebeneinander oder Überschneidung
public def touching?(rectangle)
    horizontalRange=((rectangle.left-1..(rectangle.left+rectangle.width)))
    verticalRange=((rectangle.top-1..(rectangle.top+rectangle.height)))
    horizontalRange.any?{|column|
        column==@left||column==@left+@width
    } &&
    verticalRange.any?{|line|
        line==@top||line==@top+@height
    }
 end

def ==(rectangle)
    #rectangle.kind_of?(self.class) &&
    @top==rectangle.top &&
    @height==rectangle.height &&
    @left==rectangle.left &&
    @width==rectangle.width
end

def !=(rectangle)
    !(self==rectangle)
end

def empty?
    return true if top.nil?
    return true if left.nil?
    return true if height.nil?
    return true if height<=0
    return true if width.nil?
    return true if width<=0
    # @empty=@height<=0 || @width<=0 if @empty.nil?
    # @empty
end

public def area
    # @area||=@height*@width
    # @area
    @height*@width
end

# nur wenn sie passgenau nebeneinander liegen
# (Überschneidungen darf es nicht geben)
public def mergeHorizontally(rectangle)
    return nil if @top!=rectangle.top
    return nil if @height!=rectangle.height
    if @left+@width==rectangle.left
        return Rectangle.new(
            :top=>@top,
            :left=>@left,
            :height=>@height,
            :width=>@width+rectangle.width
        )
    elsif rectangle.left+rectangle.width==@left
        return Rectangle.new(
            :top=>rectangle.top,
            :left=>rectangle.left,
            :height=>rectangle.height,
            :width=>rectangle.width+@width
        )
    else
        return nil
    end
end

# nur wenn sie passgenau nebeneinander liegen
# (Überschneidungen darf es nicht geben)
public def mergeVertically(rectangle)
    return nil if @left!=rectangle.left
    return nil if @width!=rectangle.width
    if @top+@height==rectangle.top
        # return nil
        return Rectangle.new(
            :top=>@top,
            :left=>@left,
            :height=>@height+rectangle.height,
            :width=>@width
        )
    elsif rectangle.top+rectangle.height==@top
        # return nil
        return Rectangle.new(
            :top=>rectangle.top,
            :left=>rectangle.left,
            :height=>rectangle.height+@height,
            :width=>rectangle.width
        )
    else
        return nil
    end
end

public def to_rects
    Rectangles.new([self])
end

public def >(rectangle)
    return true if @height>rectangle.height
    return true if @width>rectangle.width
    return false
end

public def +(rectArg)
    # @@log<<"Rectangle#+  ==================================================="
    # @@log<<"rectArg: #{rectArg.class}"
    newTop=[@top,rectArg.top].min
    newLeft=[@left,rectArg.left].min
    newHeight=[bottom,rectArg.bottom].max+1-newTop
    newWidth=[right,rectArg.right].max+1-newLeft
    Rectangle.new(
        :top=>newTop,
        :left=>newLeft,
        :height=>newHeight,
        :width=>newWidth
    )
end

1 个答案:

答案 0 :(得分:1)

您必须定义自己的方法以确定对象是否相等。

class Rectangle
  attr_accessor :top, :left, :width, :height

  def initialize(top, left, width, height)
    @top = top
    @left = left
    @width = width
    @height = height
  end

  def ==(rect)
    @top == rect.top &&
    @left == rect.left &&
    @width == rect.width &&
    @height == rect.height
  end
end

rect1 = Rectangle.new(10, 10, 20, 30)
rect2 = Rectangle.new(20, 20, 10, 15)
rectangles = [rect1, rect2]

rect3 = Rectangle.new(30, 30, 10, 10)
rectangles.include?(rect3) # false

rect_dup = rect1.dup
rectangles.include?(rect_dup) # true

编辑:添加基准

require 'benchmark'

rectangles = []

1.upto(1000) do |i|
  rectangles << Rectangle.new(i, i, 10, 10)
end

Benchmark.bm do |x|
  x.report(:missing_rectangles) do
    1.upto(1000) do |i|
      rect = Rectangle.new(i, i + 5, 10, 10)
      rectangles.include?(rect)
    end
  end
end

结果:

                    user       system     total    real
missing_rectangles  0.110000   0.000000   0.110000 (  0.107022)