简体   繁体   中英

Elegant way to position two rectangles

I have a rectangle (called the target ) and want to place another rectangle (called the satellite ) alongside. The satellite has a position (top, bottom, left, right) that determines the placement edge relative to the target. It also has an alignment that (left, center, right for top and bottom position, top, middle and bottom for left and right position).

Example:

+----------+----------------------------+
|          |                            |
|  Target  | Satellite, Position=RIGHT, |
|          | Align=TOP                  |
|          |                            |
|          |----------------------------+
|          |
+----------+

I know the top left coordinates of the target and also its width and height . I also know the width and height of the satellite and want to calculate the top left coordinates of it. I could do that as a series of 12 if clauses, but maybe there is a more elegant, mathematical or algorithmic way to do it. Is there an alternative way to this:

# s = satellite, t = target
if pos == "top" && align == "left"
  s.x = t.x
  s.y = t.y - s.height
else if pos == "top" && align == "center"
  s.x = t.x + t.width / 2 - s.width / 2
  s.y = t.y - s.height
# etc, etc

Any good solutions in Ruby or JavaScript?

I like the other answer, but here's how to do it without having to store anything. All math and logic using the trick that in javascript true evaluates to 1 and false evaluates to 0 when arithmetic operators are applied:

ps (check out the working jsfiddle: http://jsfiddle.net/vQqSe/52/ )

var t = {
    jq: $('#target'),
    width: parseInt($('#target').css('width')),
    height: parseInt($('#target').css('height')),
    top: parseInt($('#target').css('top')),
    left: parseInt($('#target').css('left'))
};
var s = {
    jq: $('#satellite'),
    width: parseInt($('#satellite').css('width')),
    height: parseInt($('#satellite').css('height'))
};

// start with it top left and add using javascript tricks
s.jq.css('top', t.top - s.height +
    s.height * (a == 'top') +
    (t.height/2 + s.height/2) * (a == 'middle') +
    t.height * (a == 'bottom') +
    (t.height + s.height) * (p == 'bottom')
);        

s.jq.css('left', t.left - s.width +
    t.width * (a == 'left') +
    s.width * (a == 'right') +
    (s.width/2 + t.width/2) * (a == 'center') +
    (s.width + t.width) * (p == 'right')
);

If you used a series of objects it will do the trick:

var positions = {

    top: {left:{x:t.x, y:y.y-s.height}, center:{x:tx.x + t.width/2- s.width/2, y:t.y-s.height}}
    //etc....
}
//then to get the position you can simply
var pos = positions[pos][align])
def vector pos, align, hash
  case hash[pos]
  when -1;     [0.0, -1.0]
  when 1;      [1.0, 0.0]
  else
    case hash[align]
    when -1;   [0.0, 0.0]
    when 1;    [1.0, -1.0]
    else       [0.5, -0.5]
    end
  end
end

y_t, y_s = vector(pos, align, "top" => -1, "bottom" => 1)
x_t, x_s = vector(pos, align, "left" => -1, "right" => 1)
s.y = t.y + y_t*t.height + y_s*s.height
s.x = t.x + x_t*t.width + x_s*s.width

or

def vector pos, align, head, tail
  case pos
  when head;   [0.0, -1.0]
  when tail;   [1.0, 0.0]
  else
    case align
    when head; [0.0, 0.0]
    when tail; [1.0, -1.0]
    else       [0.5, -0.5]
    end
  end
end

y_t, y_s = vector(pos, align, "top", "bottom")
x_t, x_s = vector(pos, align, "left", "right")
s.y = t.y + y_t*t.height + y_s*s.height
s.x = t.x + x_t*t.width + x_s*s.width

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM