dc.getHeight() behaves differently on different devices (simulator)

I'm trying to figure out a good way to place things on the screen, but the dc.getHeight() confuses me. My code is like this:

function initialize() {
View.initialize();
var time = App.getApp().getProperty("period_duration");
var sec = time%60;
var min = (time/60);
App.getApp().setProperty("menu_time_min", min);
App.getApp().setProperty("menu_time_sec", sec);
App.getApp().setProperty("menu_time_pos", 1);
}

// Load your resources here
// function onLayout(dc) {
// setLayout(Rez.Layouts.MainLayout(dc));
// }

// Called when this View is brought to the foreground. Restore
// the state of this View and prepare it to be shown. This includes
// loading resources into memory.
function onShow() {
}

// Update the view
function onUpdate(dc) {
// Call the parent onUpdate function to redraw the layout
View.onUpdate(dc);
var x = dc.getWidth()/2;
var y = percentInPx(15, dc.getHeight());
var numFont = Graphics.FONT_NUMBER_MEDIUM;
var text = "Set time";
dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT);
dc.drawText(x, y, Graphics.FONT_MEDIUM, text, Graphics.TEXT_JUSTIFY_CENTER);

// Up-/downarrows
var triSize = 16;
y = percentInPx(32, dc.getHeight());
dc.fillPolygon([[x, y], [x+triSize, y+triSize], [x-triSize, y+triSize]]);
y = percentInPx(80, dc.getHeight());
dc.fillPolygon([[x, y+triSize], [x+triSize, y], [x-triSize, y]]);

// Draw time
y = percentInPx(50, dc.getHeight());
var sec = App.getApp().getProperty("menu_time_sec"); //time%60;
var min = App.getApp().getProperty("menu_time_min");//(time/60);
text = padLeft(min) + ":" + padLeft(sec);
var offset = dc.getTextWidthInPixels("00:", numFont);
var startOffset = dc.getTextWidthInPixels("00", numFont);
var shift = startOffset / 2 + offset*(App.getApp().getProperty("menu_time_pos")-1);
dc.drawText(x-shift, y, numFont, text, Graphics.TEXT_JUSTIFY_LEFT);
}

// Called when this View is removed from the screen. Save the
// state of this View here. This includes freeing resources from
// memory.
function onHide() {
}

function percentInPx(percent, total) {
return total / 100 * percent;
}

function padLeft(s) {
return s.toString().length() == 1 ? "0"+s.toString() : s.toString();
}
I thought this code would give me a semi-nice behavior on various devices but when I try different ones, the result varies a lot. I thought that at least the "time" would be rendered at the middle of my screen, but that's not the case. Look at the screenshots from a Fenix 7s, Fenix 5xPlus and FR230, respectively.

Is it the simulator that is erroneous or am I missing something in the implementation?
  • Because my watch face second hand polygons have ten points (two colors, five points each) in about 20 minutes my polar to rectangular function is called more than 10k times. It seems that on average

    return [(x + 0.5).toNumber(), (y + 0.5).toNumber()]

    is about 5 us faster than

    return [Math.round(x), Math.round(y)]

    on an actual Fenix 7X from the profile data.

  • It's a real gift to be a mind reader and know what people like  andy was saying with out asking  questions!  Can you also levitate?

    At least I actually read what people write and try to understand what they're actually asking, as opposed to basing my answers off a few key words in the text and so often missing the entire point.

    Regardless, always adding 1 (followed by truncation) is the wrong solution for any kind of standard rounding (round up, round to nearest integer, round down, bankers rounding, etc.) I'm fairly sure you meant add 0.5. (EDIT: I was wrong, adding 0.5 then truncating doesn't round up, it rounds to nearest.)

    And yes, if someone says words to the effect of "I round as opposed to truncating", I assume:

    - they don't mean round down (because rounding down is the same as truncating) (* again I'm assuming we're talking about non-negative numbers.)

    - they *probably* don't mean round up, unless they specifically say so, because absent other context, "round" usually means "round to nearest integer". That's what the built-in round function does in Monkey C, Java, C, Python, javascript, etc. Another interpretation is that when someone says "I round", the default assumption would be that they call the built-in round function, which leads to the same conclusion.

    The various drawing calls like drawText will accept non-integer coordinates although of course they will round them to integers

    To be fair, when I said "round" above, I did mean "convert to an integer using some unspecified rounding strategy" and not "round to nearest integer", so there is that. But that's a different context.

  • Do you need to call toNumber() on the results of x + 0.5 and y + 0.5? Given that jim_m_58 determined non-integer coordinates are truncated, it seems that toNumber() is redundant.

    Also, not to state the obvious, but x + 0.5 (truncated) always rounds x up, while Math.round(x) rounds x to the nearest integer, so the two code snippets are not equivalent. May or may not make a difference for you.

    EDIT: whoops I'm a dummy. x + 0.5 followed by truncation is indeed equivalent to Math.round(x) (at least for non-negative numbers)

  • It wouldn't make any sense to round an integer argument, so I thought that x and y are Floats was obvious. Adding 0.5 to a Float then truncating would do something like 42.4 + 0.5 = 42, so no it does not round up. Second, even if x and y were Numbers, Monkey C will return a Float result when you add a Float to a Number type, so if you want a Number type returned then yes you have to have use.toNumber(). The point of the exercise is to pass Numbers to the drawing functions instead of Floats since they truncate Floats and don't round. Based on this observed behavior I am assuming that the drawing functions are overloaded and when passed a Float an extra step of converting it to a Number is performed. This is why I think passing Numbers may be ever so slightly better, though I have no proof of this. This last assumption may be wrong, but unless someone from Garmin who has seen the code confirms or denies it will just be speculation.

  • It wouldn't make any sense to round an integer argument, so I thought that x and y are Floats was obvious. Adding 0.5 to a Float then truncating would do something like 42.4 + 0.5 = 42, so no it does not round up. Second, even if x and y were Numbers, Monkey C will return a Float result when you add a Float to a Number type, so if you want a Number type returned then yes you have to have use.toNumber(). The point of the exercise is to pass Numbers to the drawing functions instead of Floats since they truncate Floats and don't roun

    You're correct, I was wrong to say that adding 0.5 always rounds up haha. I kinda deserved that one.

    You are right, adding 0.5 and truncating actually rounds to the nearest integer.

    I realize that x and y are Floats. My point is that assuming drawText(x, y, ...) truncates x and y, it's redundant to call toNumber() on x and y because drawText is already effectively doing that (assuming that it does truncate).

    The point of the exercise is to pass Numbers to the drawing functions instead of Floats since they truncate Floats and don't round. Based on this observed behavior I am assuming that the drawing functions are overloaded and when passed a Float an extra step of converting it to a Number is performed.

    So why not allow drawText() to do the conversion instead of doing your own redundant conversion?

    For all we know, drawText() is implemented something like this:

    function drawText(x, y, ...) {
      var x_as_integer = x.toNumber();
      var y_as_integer = y.toNumber();
      ...

    In this case calling toNumber() yourself would be a bit slower than not calling it.

    This is another possibility:

    function drawText(x, y, ...) {
      var x_as_integer = x instanceof Number ? x : x.toNumber();
      var y_as_integer = y instanceof Number ? y : y.toNumber();
      ...

    In this case, calling toNumber() yourself is no better or worse than not calling it.

    Unless I'm missing something, in the best-case scenario, calling toNumber() yourself provides no gains, whereas in the worst case, calling toNumber() yourself is slower.

    However, all of that is only true if we are sure that drawText() truncates rather than doing something else, like rounding to nearest. If drawText() actually rounds to nearest, then it would be incorrect for you to not call toNumber() beforehand, since it would lead to incorrect results.

    Hopefully I'm not missing something here.

  • Earlier in this thread jim_m_58 posted that from his experiment the draw functions truncate so I don't know what else we could conclude other than the draw functions treat all coordinates as Numbers. Therefore, it is necessary that I do the rounding. Whether it's better to leave the rounded number as a Float or convert it to a Number is anyone's guess. For whatever reason adding 0.5 then converting to number is faster than round, so I'm going to stick with this for now.

  • Earlier in this thread jim_m_58 posted that from his experiment the draw functions truncate so I don't know what else we could conclude other than the draw functions treat all coordinates as Numbers. Therefore, it is necessary that I do the rounding. Whether it's better to leave the rounded number as a Float or convert it to a Number is anyone's guess. For whatever reason adding 0.5 then converting to number is faster than round, so I'm going to stick with this for now.

    I understand all of that.

    My point is that *assuming* draw*() truncates its first two arguments (coordinates), then given:

    A) drawText(x + 0.5, y + 0.5, ...)

    B) drawText((x + 0.5).toNumber(), (y + 0.5).toNumber(), ...)

    I claim:

    - The end results of A and B are the same. Why? Because drawText() truncates its arguments and calling X.toNumber() truncates X. So unless I'm missing something, A and B are equivalent, since truncating the same number twice isn't any different than truncating it once

    - At best, A is faster than B, and at worst, A is the same speed as B

    Again, if you're not sure whether drawText() will truncate, round to nearest integer, or possibly do something else (unlikely), then B is the best way to go.

    My initial guess would've been that drawText() truncates (by calling toNumber()) (I know I said "round"), because iirc truncation/toNumber() is Monkey C's usual policy for that kind of thing.

  • For whatever reason adding 0.5 then converting to number is faster than round

    At the very least you save the overhead of an additional function call (assuming that Math.round() also calls toNumber() or some other function), so it's not surprising.

  • To say all of that a different way:

    - Rounding x to the nearest integer can be implemented by adding 0.5 to x and truncating the result

    - drawText truncates its coordinate arguments (we think)

    - Therefore to implement rounding of coordinates to the nearest integer, it's only necessary for the caller to add 0.5 to each of the coordinate arguments of drawText

    I only mention this because I'm under the impression you want to write the fastest code possible.

    Again, hopefully I am not missing something.

  • Never thought my original post would spark such a discussion, but I'm reading it with great interest. I never had to bother with cpu/memory limitations before, programming for a small device is new to me. Thank you all for great insights!