1

I really like the Raphael Javascript library which is very useful for handling SVG with javascript.

However there is an offset value which is added to the generated svg code that I don't understand. Does anybody know where does it come from and how to avoid it?

Here is my JS code:

var paper = Raphael("canvas", 510, 510);
paper.clear();
paper.rect(0, 0, 500, 500, 10).attr({fill: "#fff", stroke: "black"});

The generated SVG code is

<div id="canvas">
    <svg width="510" height="510">
        <desc>Created with Raphaël</desc>
        <defs/>
        <rect x="0.5" y="0.5" width="500" height="500" r="10" rx="10" ry="10" fill="#ffffff" stroke="#000000"/>
    </svg>
</div>          

Why does the x and y attributes of the rect are 0.5 and not 0?

Update: It seems that the values are rounded with the code below:

var round = function (num) {
    return +num + (~~num === num) * .5;
}; 

Does anybody know the reason?

luc
  • 41,928
  • 25
  • 127
  • 172

2 Answers2

6

The expression +num + (~~num === num) * .5 is saying:

  1. +num: get the value of the variable num as a number;
  2. (~~num === num): return true if the bitwise-NOT of the bitwise_NOT of the value of the variable num (which is num with any fractional component removed, equivalent to Math.floor(num)) is exactly equal to the value of the variable num: that is, return true if num is an integer, false otherwise;
  3. add the result of step 1 to the result of step 2, thereby coercing the Boolean value returned by step 2 into a numeric value: for the case when the variable num has the numeric value 0, this will result in 1;
  4. multiply the result of step 3 by 0.5.

So we get the result (0 + 1) * 0.5, which is 0.5.

In other words, the code is saying "For all integers, add 0.5; for all non-integers, add nothing."

This has some interesting results whose purpose is obscure to say the least; consider its application to the following values:

  1. 0 -> 0.5;
  2. 0.1 -> 0.1;
  3. 0.4 -> 0.4;
  4. 0.5 -> 0.5;
  5. 0.9 -> 0.9;
  6. 1.0 -> 1.5;
  7. 1.1 -> 1.1;

...and so on.

As to why they do this: I really haven't got the faintest idea. FWIW I've got large amounts of SVG, both static and dynamically created, working happily on Firefox, Safari and Opera, and none of it has ever needed this kind of silliness.

If anybody ever finds out the reason for this, I'd love to know it :-)

NickFitz
  • 34,537
  • 8
  • 43
  • 40
  • Thanks, great explanation. Raphael is also supporting IE thanks to VML. Might be related? – luc Oct 28 '09 at 08:21
  • 1
    Apparently not: the function in question is defined under a comment saying "SVG", and inside an if clause: *if (R.svg) {* so I think it's definitely on the SVG branch ;-) – NickFitz Oct 28 '09 at 10:28
  • @luc - No problem. Thanks for remembering after all this time :-) – NickFitz Dec 10 '09 at 14:08
  • just speculation but adding 0.5 to an a float that will be converted to an integer is a cheap way to get rounding behavior using integer math in C and C++... in ecmascript the equivalent would be Math.floor which is probably faster than rounding in some browsers if the underlying implementation uses casting to int for floor – technosaurus Dec 31 '12 at 23:11
2

The reason may be the coordinate system used for drawing: a 1px black line drawn at x=1.0 is half left of 1.0 and half right of it, resulting in a 2px gray line. With the 0.5px offset, the line is between 1.0 and 2.0.

theso
  • 21
  • 1