Author Topic: Discussion of creating Aspect-Aware Layouts  (Read 28194 times)

verion

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 861
    • View Profile
    • new projects
Re: Discussion of creating Aspect-Aware Layouts
« Reply #15 on: June 17, 2015, 09:24:02 AM »
@ omegaman

Quote
Why don't you test it with roboskin?

I've actually done exactly that. I've upscaled and cleaned up cab image in 4xHD resolution (2x 1080p in height). And it scales up as good as scales down - beautifully. It is because you've designed and coded it resolution independent.

But I'm using it in 4:3 window, because in 16:10 aspect it looks too stretched. I'll convert it to 16:9/10, but for now I'm keeping it 4:3, so you can use my graphics after we sort out that pinching, skewing quirks.

Actually this makes me think if really universal (4:3 / 16:9) layout is possible - the one that looks good in both aspects. Because even if robospin would scale like responsive website - that would leave too much empty space in the middle.
On the mockup above - I'm simply cheating: making cab and wheel bigger and cutting the bottom for 16:9 layout. So it's not that automatic - it still needs separate "designs" for different layouts.

liquid8d

  • Global Moderator
  • Sr. Member
  • *****
  • Posts: 442
    • View Profile
Re: Discussion of creating Aspect-Aware Layouts
« Reply #16 on: June 17, 2015, 09:36:30 AM »
Without reading them in detailed atm, looks like you are both multiplying based on the aspect, right? My concern there is you might not always want an object to be the same fixed scale for one aspect as you do another. Of course, you could do an if/else or something, but again you are going to be searching all through your code to change things.

Scaling up or down is nice, but if you are using the same values you multiply the aspect by, things are still being "stretched" on different aspects, right? That's another reason why I suggested fixed settings for different aspects, but having a 'default' that it falls back on. The default value could be aspect * defaultvalue, but then if you wanted you could specify different values for other aspects / resolutions.

Code: [Select]
SETTINGS <- {
    //every setting MUST be defined in default (this will be the fallback value)
    "default": {
        "width": 640,
        "height": 480,
        "logo": { src = ResourcePath("bg.png"), x = 0, y = 0, width = aspect * 1.0, height = aspect * 1.0 },
        "title": { x = 0, y = aspect * 0.02, width = aspect * 1.0, height = aspect * 0.1 },
        "fontName": "Arial",
        "animate_wheel": false
    },
    //aspect-resolution specific values can be added for each
    "720p": {
        "width": 1280,
        "height": 720,
        "title": { x = 0, y = 0, width = 1280, height = 30 },
        "animate_wheel": true
    },
    "1080p": {
        "width": 1920,
        "height": 1080
    }
}
local title = fe.add_text( "[Title]", Setting.title.x, Setting.title.y, Setting.title.width, Setting.title.height );

If you use this, all your values are in one spot and the add_text is not very confusing because it's just whatever value is in Settings. Plus this would allow you to automatically pull resources from a ResourcePath("4x3/bg.png") for aspect specific resources. You can have a nice wide bg for widescreen and a fitting standard aspect for 4:3.

Again, a lot of this is personal preference. I think it will save a lot of headache when you want to adjust the look of the theme if you have all your settings in one spot. It would allow users to go in and adjust to their liking and you could easily make variations with a multiple settings tables, using the correct values based on a config option.

omegaman

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 880
    • View Profile
Re: Discussion of creating Aspect-Aware Layouts
« Reply #17 on: June 17, 2015, 10:13:08 AM »
Liquid-

I do like your idea of having a fallback value. Definitely, makes the code easier to read and make changes on the fly.

liquid8d

  • Global Moderator
  • Sr. Member
  • *****
  • Posts: 442
    • View Profile
Re: Discussion of creating Aspect-Aware Layouts
« Reply #18 on: June 17, 2015, 10:53:05 AM »
Think I got something really sweet almost worked out :) Try to have an initial version ready sometime today.

Luke_Nukem

  • Sr. Member
  • ****
  • Posts: 135
    • View Profile
    • Blogging about Rust lang
Re: Discussion of creating Aspect-Aware Layouts
« Reply #19 on: June 17, 2015, 01:40:31 PM »
Nope. It doesn't fix that.

Turns out I didn't push my latest changes upstream. Should be fixed now I think.

Luke_Nukem

  • Sr. Member
  • ****
  • Posts: 135
    • View Profile
    • Blogging about Rust lang
Re: Discussion of creating Aspect-Aware Layouts
« Reply #20 on: June 17, 2015, 01:44:28 PM »
Without reading them in detailed atm, looks like you are both multiplying based on the aspect, right? My concern there is you might not always want an object to be the same fixed scale for one aspect as you do another. Of course, you could do an if/else or something, but again you are going to be searching all through your code to change things.

Scaling up or down is nice, but if you are using the same values you multiply the aspect by, things are still being "stretched" on different aspects, right? That's another reason why I suggested fixed settings for different aspects, but having a 'default' that it falls back on. The default value could be aspect * defaultvalue, but then if you wanted you could specify different values for other aspects / resolutions.

This is the exact reason I've done the rotating_new layout the way I have. It should be completely resolution and aspect independent now.
You can quickly test it by inserting something like;
Code: [Select]
fe.layout.width = 1024;
fe.layout.height = 768;
In the layout near the top.

The only problem I can see that might possibly pop up is if someone is using a rotated windows desktop. But hopefully this is nearly nobody.

Luke_Nukem

  • Sr. Member
  • ****
  • Posts: 135
    • View Profile
    • Blogging about Rust lang
Re: Discussion of creating Aspect-Aware Layouts
« Reply #21 on: June 17, 2015, 01:49:34 PM »
Luke-

My newer layouts already scale based on the base resolution the layout was designed for. The only thing I see that you are doing differently - and, forgive my ignorance on this, is you are creating a function for the math and calling other .nuts with the pixel position based on the BG. That and you can switch from horizontal to vertical which is nice. But, will this code scale much better than what I'm doing below. This is more for learning than a critique.     


Man, you have got to use pastebin.com or github, or the /code tags. Code in forums does not work well.
The main difference, and probably the biggest difference, is that by making all my object placements depend on the *.png overlay art, they will scale correctly to the correct places on that piece of art (hopefully now, every time.)

It also occurs to me that perhaps rendering everything on to a base surface, and scaling just that surface could work just as well.

PS: I also like to try and *modularise* my code where possible. It helps with reading, compartmentalising , and also has the added bonus of being easy to debug and easy to reuse.
« Last Edit: June 17, 2015, 01:52:25 PM by Luke_Nukem »

liquid8d

  • Global Moderator
  • Sr. Member
  • *****
  • Posts: 442
    • View Profile
Re: Discussion of creating Aspect-Aware Layouts
« Reply #22 on: June 17, 2015, 02:13:37 PM »
Luke,

Here's a quick version of what I'm talking about just for your wheel.nut.. but I would do settings across the board in one spot and for different aspects:

Code: [Select]
local s = {
    "message": { x = 0, y = 0, w = fe.layout.width, h = 80, alpha = 0, style = Style.Bold }
    "wheel": {
        "horizontal": { x = 1.83, y = 7.5, w = 2.46, h = 6.72 }
        "vertical": { x = 7.16, y = 21.3, w = 1.36, h = 5.25 }
    }
}

local message = fe.add_text("LOADING",s.message.x,s.message.y,s.message.w,s.message.h);
message.alpha = s.message.alpha;
message.style = s.message.style;

local wheel = fe.add_artwork(wheelArt, 0, 0);

function wheelUpdate( rotation )
{
    // Set wheel size.
    wheel.width = overlay_horz.width / s.wheel[rotation].w ;
    wheel.height = overlay_horz.height / s.wheel[rotation].h;
    // This keeps the original ratio
    //if (wheel.texture_height != 0 && wheel.texture_width != 0)
    //{
    //    wheel.width = (wheel.height * (wheel.texture_height / wheel.texture_width));
    //}           
    wheel.x = overlay_horz.x + (overlay_horz.width / s.wheel[rotation].x);
    wheel.y = overlay_horz.y + (overlay_horz.height / s.wheel[rotation].y);
   
    wheel.shader = titleShader;
}


function wheelFresh( ttype, var, ttime )
{
    local actual_rotation = (fe.layout.base_rotation + fe.layout.toggle_rotation)%4;   
    if (ttype == Transition.ToNewSelection || Transition.FromOldSelection)
    {
        if (( actual_rotation == RotateScreen.Left ) || ( actual_rotation == RotateScreen.Right ))
        {
            wheelUpdate("vertical");
        }
        else
        {
            wheelUpdate("horizontal");
        }
    }
    return false;
}
fe.add_transition_callback( "wheelFresh" );

Now you could make future changes to adjust the message or wheel size and position without hunting code. And the way I'm working on, those would be the defaults, but you'd also be able to have aspect or resolution specific settings. Even better, I'm doing it where you can have multiple 'skins', so you could have one that has the wheel on the left or right, one that has the wheel on the top or bottom, etc..

Luke_Nukem

  • Sr. Member
  • ****
  • Posts: 135
    • View Profile
    • Blogging about Rust lang
Re: Discussion of creating Aspect-Aware Layouts
« Reply #23 on: June 17, 2015, 02:26:05 PM »
Luke,

Here's a quick version of what I'm talking about just for your wheel.nut.. but I would do settings across the board in one spot and for different aspects:
** snip **
Now you could make future changes to adjust the message or wheel size and position without hunting code. And the way I'm working on, those would be the defaults, but you'd also be able to have aspect or resolution specific settings. Even better, I'm doing it where you can have multiple 'skins', so you could have one that has the wheel on the left or right, one that has the wheel on the top or bottom, etc..

I did think about doing that. But I'm not about to start making different skins etc, to me it makes more sense to just use different layouts instead of trying to lump everything into one layout. That would make it easier to manage settings and so on too, especially for multiple emu/lists. I could see the convenience of using tables (They get called dictionaries in Python. Was hard to switch to Squirrel after 6 months of studying that), but they just didn't make any sense to me to use in what I was doing, as all objects are specific to the overlay graphic in use.
If I thought I was going to be code hunting, then yeah, tables/dictionaries would be used, but since I split everything into their own scripts, it's just a piece of cake to modify stuff (and reuse code).

The one drawback with doing so much stuff in script, is settings management.
For example you have 3 lists, and you use the one layout for each list, change a setting in the layout for one list and it affects all 3 lists. I suppose you could just make multiple copies of the layout...

***Lastly, I just trialled my code with a large background png, it scales up and down beautifully!
Setting the height to fe height (for horizontal) or width to fe height (for vertical), and scaling the image by using a ratio calculated from its base size, is the magic that does that. Then setting object placements by using the background image works perfectly every time.

EDIT::
On second thoughts. Using tables/dicts enables use of *generic* functions in some cases (but not all), and this appeals to my minimalist nature. (Thanks for pointing them out again. I'd grown to dislike them due to the data management stuff I had to do at Uni).
« Last Edit: June 17, 2015, 03:25:18 PM by Luke_Nukem »

liquid8d

  • Global Moderator
  • Sr. Member
  • *****
  • Posts: 442
    • View Profile
Re: Discussion of creating Aspect-Aware Layouts
« Reply #24 on: June 17, 2015, 04:15:10 PM »
I see what you mean... what if you could just do this?

Code: [Select]
//My Settings
Skin.store( "msg_x", 0 );
Skin.store( "msg_y", 0 );
Skin.store( "msg_w", 500);
Skin.store( "msg_h", 30 );

//My Objects
local message = fe.add_text("LOADING", Skin.msg_x, Skin.msg_y, Skin.msg_w, Skin.msg_h );

Storing the variables in tables can be done in the module, it can be made very easy for the layout dev.. in fact, that's what I already do with the animate module.

As far as 'skins', you wouldn't have to have more than the main one. Although they could be completely different layouts in one, usually it would used for the same layout but some altered resources or location/sizes for each skin.

Code: [Select]
Settings.add( "Skin 1", "bg_src", "bg1.png" );
Settings.add( "Skin 2", "bg_src", "bg2.png" );

//Skin is a reference to whatever skin option the user has chosen in Layout Options
//no need for if ( config["skin"] == "Skin 1") add this, else add this
local bg = fe.add_image( Skin.bg_src, Skin.bg_x, Skin.bg_y, Skin.bg_w, Skin.bg_h );

You can certainly just create a copy of a layout, make the minor change. But if you plan on updating that layout in the future, any code changes have to be kept in sync across them all.

Luke_Nukem

  • Sr. Member
  • ****
  • Posts: 135
    • View Profile
    • Blogging about Rust lang
Re: Discussion of creating Aspect-Aware Layouts
« Reply #25 on: June 17, 2015, 04:23:52 PM »
That could work I guess. Though I'm going to leave settings management for another day.

My main focus at the moment is making as generic as possible, a rotating and scaling layout. That part is done, so now I just need to finish off including aspect ratios.
So far the biggest problem I've got is it isn't easy to pass an object reference to a function. I still have to rely on the clunky if/else (this == "string") { } and so on.

Unless I've completely missed something....?


I'm really wishing we had Python instead of squirrel now, I'm much more familiar with that. Sod it, I'm going to start using class based programming for layouts. It's a hell of a lot easier to manage.
As for copying layouts. I was meaning more for the end user. Not for settings in actual script, but for any that we write out.
« Last Edit: June 17, 2015, 04:31:57 PM by Luke_Nukem »

liquid8d

  • Global Moderator
  • Sr. Member
  • *****
  • Posts: 442
    • View Profile
Re: Discussion of creating Aspect-Aware Layouts
« Reply #26 on: June 17, 2015, 04:35:07 PM »
So far the biggest problem I've got is it isn't easy to pass an object reference to a function. I still have to rely on the clunky if/else (this == "string") { } and so on.

I'm really wishing we had Python instead of squirrel now, then I could use class based programming and just parse in a config file for each layout/rotation/aspect straight into a nice tidy array.

Squirrel supports classes..
https://electricimp.com/docs/squirrel/squirrelcrib/#crib_objects

As for object references - I *think* sending objects to functions is a reference? I hope so because I do a lot of it. Classes (and Arrays) in the script are converted to a table, so I think it just uses a reference to the table.


Couple thoughts/questions..

I don't full comprehend what is wanted here with here for aspect vs. resolutions. Do we only care about aspect - and if so what is the proper fallback order: 16:10 -> 16:9 -> 4:3? If we care about resolutions, which resolutions are which aspect and are there "odd" aspects out there that we do care about? Obviously I can look these up, just curious on everyone's thoughts.

With regards to rotation, can that just be categorized as a reverse aspect - i.e. 3:4? If the Rotation option in AM rotates the layout - and rotation is just the reverse width/height, what are you accomplishing with your version? I don't deal with that much right now, since I don't use vertical layouts.

AM already stretches your layout automatically to whatever resolution the user has. Would we like to be able to "fit" a 4x3 layout, into 16:9 (it would scale to match height and fit in the center)? Or just stretch, but use aspect specific resources?

Luke_Nukem

  • Sr. Member
  • ****
  • Posts: 135
    • View Profile
    • Blogging about Rust lang
Re: Discussion of creating Aspect-Aware Layouts
« Reply #27 on: June 17, 2015, 05:24:26 PM »
liquid8d. Just been browsing through the SQ reference, saw the classes. Everything seems similar to Python in some ways.

Do you know off the top of your head if something like;
Code: [Select]
bg_object = fe.add_image(blaaaah);
function test(thisobj)
{
   if (thisobj == bg_object)
   {
       bg_object.x = 666;
       **more stuff;
   }
}
test(bg_object);
Will work? Because I've been trying variations on that theme for a few hours now, and it doesn't seem to pass references, and I can't see any documentation that suggests it happens.

You pretty much got it in one. I've done my layouts that way to prevent the stretching of the layout. Using the same code, I can include 16:9 and 16:10 layouts in the same, and switch to the correct one depending on AM's starting res. And if it's built for only 4:3, then it won't stretch to ugly proportions on a 16:9 or 16:10.

That's my own goal. Along with sharing all that I do, hence the reason I'm not bothering with much extraneous stuff atm.

Going by the work I did at Uni, using a class based setup, combined with being modularised, should achieve the goal of being universal.
So what I intend to do is make a very small set of functions that take a set of parameters like object x,y,w,h,  and returns new x,y,w,h based on the fe.layout dimensions and aspect of the chosen reference size (which could be an image, surface, or just a tuple).

Luke_Nukem

  • Sr. Member
  • ****
  • Posts: 135
    • View Profile
    • Blogging about Rust lang
Re: Discussion of creating Aspect-Aware Layouts
« Reply #28 on: June 17, 2015, 06:07:50 PM »
This is an example of the way i would do it;

Code: [Select]
// Use to create objects for each res/aspect
// by passing in required x,y,w,d and a
// reference for resolution such as art/surface.
class MarqueeArt
{
    constructor(orientation, aspect,x,y,w,h,refRes)
    {
        descriptor = "MarqueeArt";
        art = fe.add_artwork( wheelArt, 0, 0);
        orientation = orientation;  // Should be labels only
        aspect = aspect;            // and used to determine
        x = x;                      // which obj to update
        y = y;
        w = w;
        h = h;
        refRes = refRes; // Should probably be a global?
        visible = true;
    }
    update()
    {
        art.x = refRes.x + (refRes.width / (x / refRes.width));
        art.y = refRes.y + (refRes.height / (y / refRes.height));
        art.width = refRes.width / (w / refRes.width);
        art.height = refRes.height / (h / refRes.width);
    }
    setVisible(flag)
    {
        if (flag == true) { art.alpha = 255; }
        else if flag == false; { art.alpha = 0; }
    }
}
//////
function updateMarqueeArt(orientation, aspect)
{
    foreach (obj in objectList)
    {
        if (obj.orientation == orientation
              && obj.aspect == aspect)
        {
            obj.update();
            obj.setVisible(true);
        }
        else
        {
            obj.setVisible(false);
        }
    }
}

So for each orientation and aspect, just create a new object from the class by using the input parameters, and store that object in a global objectList.
When it comes time to switching to a new orientation or aspect, you would pass those details to the update function, and it would then update those objects only, and set all others to be invisible.

I haven't tried that code yet as I'm still conceptualising it all. But it's really feeling like the correct way to do it, makes it much more dynamic and data driven.

Thoughts?

liquid8d

  • Global Moderator
  • Sr. Member
  • *****
  • Posts: 442
    • View Profile
Re: Discussion of creating Aspect-Aware Layouts
« Reply #29 on: June 17, 2015, 06:26:33 PM »
I'm not sure in what scenario you are using it in that it doesn't, but it worked for me. thisobj = obj is comparing instances of the object. If yours isn't working, it seems like the instance you are passing to the function is not the same object instance?

I did this:
Code: [Select]
local bg = fe.add_image( Skin.bg.src, Skin.bg.x, Skin.bg.y, Skin.bg.width, Skin.bg.height );

function doit( obj )
{
   print( "obj: " + obj + "\n" );
   print( "bg: " + bg + "\n" );
   if ( obj == bg ) print( "match!" );
}

doit(bg);


and got this:
Code: [Select]
obj: (instance : 0x000000000443B1D0)
bg: (instance : 0x000000000443B1D0)
match!

However, I'm not sure on the scope when you do it from a class. I do similar stuff to what you are talking about in my animate module and what I was doing with extended objects..

AnimationCore + Animation classes (which are my base classes):
https://github.com/liquid8d/attract-extra/blob/master/modules/animate.nut

PropertyAnimation,SpriteAnimation and ParticleAnimations (which are my extended classes):
https://github.com/liquid8d/attract-extra/tree/master/modules/animate

This one might be better for you. I've suspended work because it got a bit complex and I'm focusing on animate right now, but ExtendedObjects (both base + extended classes in there) did mostly what you are talking about:
https://github.com/liquid8d/attract-extra/blob/master/modules/extended/extended.nut

I might have a bit much in there, but you'll be able to see how the classes work compared to python. I've found there is some advantages of tables vs. classes, but I'll let you decide on that :) A quick thing though about classes - make sure you set any variables to null and initialize them in the constructor unless you want them shared across instances of the same class:

Code: [Select]
class MyClass
{
   sharedArray = [ "One", "Two" ];
   instanceArray = null;
   constructor( x, y )
   {
      instanceArray = [ "InstanceOne", "InstanceTwo" ];
   }
}

I think that may only apply to arrays, tables or other class instances, but I can't remember at the moment..