Author Topic: preserve_aspect_ratio, but "fill" an add_surface or add_artwork?  (Read 27531 times)

liquid8d

  • Global Moderator
  • Sr. Member
  • *****
  • Posts: 442
    • View Profile
Re: preserve_aspect_ratio, but "fill" an add_surface or add_artwork?
« Reply #30 on: November 21, 2015, 05:19:57 PM »
Okay, I've worked on this quite a bit since I need this for a few different things and it seems quite useful:



Does that all look like what we'd want in relation to the art's container size and the art aspect ratio? I think that is all correct, but still doing some tests. I've reworked the fill-art module so it does fit or fill, depending on preference - hence PreserveArt - this also works as a PreserveImage.

Once I have a few more kinks worked out, I'll post the code here, upload it to my github and possibly work on a conveyor mod. I'm doing something called a 'Shelf' right now which is similar:



And finally, this is Shelf using FILL with PSP (tall textures) anchored to the texture top:


 ;D

omegaman

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 880
    • View Profile
Re: preserve_aspect_ratio, but "fill" an add_surface or add_artwork?
« Reply #31 on: November 22, 2015, 04:05:40 PM »
Liquid8d-

WOW! This is great work. Are these value or reference based containers, I assume reference based? And, how does scaling affect the objects in the container. Would it be a stretch to call these containers flexible surfaces.

liquid8d

  • Global Moderator
  • Sr. Member
  • *****
  • Posts: 442
    • View Profile
Re: preserve_aspect_ratio, but "fill" an add_surface or add_artwork?
« Reply #32 on: November 22, 2015, 04:50:15 PM »
Each object has its own surface with the art added to it. The art is then scaled and anchored on the surface appropriately based on the surface dimensions and the art texture dimensions. I still can't for the life of me quite get the math (i.e. aspects ) right. It works shown in the pics above, but some texture dimensions still aren't scaling/anchoring correctly.

Ideally this could be likened to setting the aspect on your tv or video player - crop, fit, fill, etc.. if I can get the math right that's how it should work.

FYI - I haven't forgotten about the animate module, although this has taken some time away from that. I still have some fairly major fixes for it to work properly to take over for v1 :)

Bgoulette

  • Sr. Member
  • ****
  • Posts: 116
  • I wrote a book.
    • View Profile
    • BlakeGoulette.com
Re: preserve_aspect_ratio, but "fill" an add_surface or add_artwork?
« Reply #33 on: November 22, 2015, 08:06:13 PM »
This probably isn't the math you're talking about, but I took the previous sizing branches you'd posted and discovered some tweaks that needed to be made. I think these are all accurate:

Code: [Select]
function update ()
{
local _pAspect=pdims.w.tofloat()/pdims.h.tofloat(); // parent dimensions
local _tAspect=tdims.w.tofloat()/tdims.h.tofloat(); // texture dimensions
/*
::print ("_pAspect: "+_pAspect+"; _tAspect: "+_tAspect+"\n");
::print ("pdims: "+pdims.w+", "+pdims.h+"\n");
::print ("tdims: "+tdims.w+", "+tdims.h+"\n");
/**/

if (_pAspect >= 1.0)
{
// Fill to parent width
if (_tAspect >= 1.0)
{
// Wider or equal
art.width=(tdims.w*(pdims.w/tdims.h.tofloat()));
}
else
{
// Taller
art.width=(tdims.w*(pdims.w/tdims.w.tofloat()));
}
art.height=art.width/_tAspect;
}
else
{
// Fill to parent height
if (_tAspect >=1 )
{
art.height=(tdims.w*(pdims.h/tdims.h.tofloat()));
}
else
{
art.height=(tdims.w*(pdims.h/tdims.w.tofloat()))
}
art.width=art.width*_tAspect;
}

switch (anchor)
{
case ::Anchor.TopLeft:
art.x=0;
art.y=0;
break;

case ::Anchor.Top:
art.x=(pdims.w-art.width)/2.0;
art.y=0;
break;

case ::Anchor.TopRight:
art.x=pdims.w-art.width;
art.y=0;
break;

case ::Anchor.Left:
art.x=0;
art.y=(pdims.h-art.height)/2.0;
break;

case ::Anchor.Right:
art.x=pdims.w-art.width;
art.y=(pdims.h-art.height)/2.0;
break;

case ::Anchor.BottomLeft:
art.x=0;
art.y=pdims.h-art.height;
break;

case ::Anchor.Bottom:
art.x=(pdims.w-art.width)/2.0;
art.y=pdims.h-art.height;
break;

case ::Anchor.BottomRight:
art.x=pdims.w-art.width;
art.y=pdims.h-art.height;
break;

case ::Anchor.Center:
default:
art.x=(pdims.w-art.width)/2.0;
art.y=(pdims.h-art.height)/2.0;
break;
}

// ::print ("art.width: "+art.width+"; art.height: "+art.height+"\n");
}

You've added tons more bells and whistles than what I'd been thinking about, but it's been great seeing how to extend the module's usefulness!

omegaman

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 880
    • View Profile
Re: preserve_aspect_ratio, but "fill" an add_surface or add_artwork?
« Reply #34 on: November 23, 2015, 06:45:25 AM »
Liquid8d-

No worries on the animate module, mate. I was just trying to give you a little nudge. Out of all the projects you are working on this one probably should take priority for its relevance.   

For what it's worth, when I was looking for a way to scale my layouts I found this simple code for my cheat sheets. To proportionally scale an image to the dimensions of its parent container you could use something like this.  Setting the dimensions of your container as a variable.  But, I still learning and have a very basic understanding of this stuff.  :)

var scale:Number = ((container.height/container.width) > (image.height/image.width)) ? (container.width/image.width) : (container.height/image.height);
image.scaleX = scale;
image.scaleY = scale;

Bgoulette

  • Sr. Member
  • ****
  • Posts: 116
  • I wrote a book.
    • View Profile
    • BlakeGoulette.com
Re: preserve_aspect_ratio, but "fill" an add_surface or add_artwork?
« Reply #35 on: November 27, 2015, 07:45:40 AM »
I revisited my width/height logic and revised it some. There might be a more efficient way to do this (I was right on the cusp of something while I was in the shower, but without anything to write on, I promptly forgot!) Maybe this will be additional food for thought:

Code: [Select]
function update ()
{
local _target_aspect=null;
local _source_aspect=null;

if (_target.width>=_target.height)
{
// Wider (or equal)
_target_aspect=_target.width/_target.height.tofloat();
if (_source.width>=_source.height)
{
// Art is wider than tall; favor height for sizing
_source_aspect=_source.width/_source.height.tofloat();
art.width=_target.height*_source_aspect.tofloat();
art.height=_target.height;

// One last check in case sizing leaves us short
if (art.width<_target.width)
{
art.width=_target.width;
art.height=_target.width*(1.0/_source_aspect);
}
}
else
{
// Art is taller than wide; favor width for sizing
_source_aspect=_source.height/_source.width.tofloat();
art.width=_target.width;
art.height=_target.width*_source_aspect.tofloat();

// One last check
if (art.height<_target.height)
{
art.width=_target.width*(1.0/_source_aspect);
art.height=_target.height;
}
}
}
else
{
// Taller
_target_aspect=_target.height/_target.width.tofloat();
if (_source.width>=_source.height)
{
// Art is wider than tall; favor height
_source_aspect=_source.width/_source.height.tofloat();
art.width=_target.height*_source_aspect;
art.height=_target.height;

// One last check
if (art.height<_target.height)
{
art.width=_target.height*(1.0/_source_aspect);
art.height=_target.height;
}
}
else
{
// Art is taller than wide; favor width
_source_aspect=_source.height/_source.width.tofloat();
art.width=_target.width;
art.height=_target.width*_source_aspect;

// One last check
if (art.height<_target.height)
{
art.width=_target.height*(1.0/_source_aspect);
art.height=_target.height;
}
}
}

// Set anchor position
switch (anchor)
{
case ::Anchor.TopLeft:
art.x=0;
art.y=0;
break;

case ::Anchor.Top:
art.x=(_target.width-art.width)/2.0;
art.y=0;
break;

case ::Anchor.TopRight:
art.x=_target.width-art.width;
art.y=0;
break;

case ::Anchor.Left:
art.x=0;
art.y=(_target.height-art.height)/2.0;
break;

case ::Anchor.BottomLeft:
art.x=0;
art.y=_target.height-art.height;
break;

case ::Anchor.Bottom:
art.x=(_target.width-art.width)/2.0;
art.y=_target.height-art.height;
break;

case ::Anchor.BottomRight:
art.x=_target.width-art.width;
art.y=_target.height-art.height;
break;

case ::Anchor.Center:
default:
art.x=(_target.width-art.width)/2.0;
art.y=(_target.height-art.height)/2.0;
break;
}
}

liquid8d

  • Global Moderator
  • Sr. Member
  • *****
  • Posts: 442
    • View Profile
Re: preserve_aspect_ratio, but "fill" an add_surface or add_artwork?
« Reply #36 on: December 10, 2015, 06:20:15 AM »
OKAY. I think this is accurate, this is what I finally came up with :) Going to do a bit more testing and I will add this to my github..



Code: [Select]
/////////////////////////////////////////////////////////
//
// Attract-Mode Frontend - PreserveArt/PreserveImage Module
//
/////////////////////////////////////////////////////////
// PreserveArt - creates a surface with artwork that will
// fit or fill the specified dimensions. This preserves
// art aspect ratio, but gives option to anchor the art
// to one side of its dimensions.
//

::Anchor <-
{
    Top = "Top",
    Left = "Left",
    Centre = "Center",
    Center = "Center",
    Right = "Right",
    Bottom = "Bottom",
}

class PreserveArt
{
    VERSION = 1.0;
    debug = false;
    surface = null;
    art = null;
    isArt = true;
    anchor = ::Anchor.Center;
    fit_or_fill = "fit";
    request_size = true;
   
    constructor( name, x, y, w, h, parent = ::fe )
    {
        surface = parent.add_surface( w, h );
        surface.x = x;
        surface.y = y;
        if ( debug ) surface.add_image( ::fe.script_dir + "pixel.png", 0, 0, surface.width, surface.height );
        art = ( isArt ) ? surface.add_artwork( name, 0, 0, w, h ) : surface.add_image( name, 0, 0, w, h );
        ::fe.add_transition_callback( this, "onTransition" );
        ::fe.add_ticks_callback( this, "onTick" );
    }
   
    function set_anchor( a )
    {
        anchor = a;
        request_size = true;
    }
    function set_fit_or_fill( f )
    {
        fit_or_fill = f;
        request_size = true;
    }
   
    function update()
    {
        if ( art.texture_width != 0 && art.texture_height != 0 )
        {
            request_size = false;
            local aspect = surface.width / surface.height.tofloat();
            local texture_aspect = art.texture_width / art.texture_height.tofloat();
           
            if ( fit_or_fill == "fit" )
            {
                if ( aspect > 1.0 )
                {
                    //wide
                    if ( texture_aspect > 1.0 )
                    {
                        print("wide, wide, fit");
                        art.width = surface.width;
                        art.height = art.width / texture_aspect;
                        if ( art.height > surface.height )
                        {
                            //reverse
                            art.height = surface.height;
                            art.width = art.height * texture_aspect;
                        }
                    } else
                    {
                        print("wide, tall, fit");
                        art.height = surface.height;
                        art.width = art.height * texture_aspect;
                        if ( art.width > surface.width )
                        {
                            //reverse
                            art.width = surface.width;
                            art.height = art.width / texture_aspect;
                        }
                    }
                } else
                {
                    //tall
                    if ( texture_aspect > 1.0 )
                    {
                        print("tall, wide, fit");
                        art.width = surface.width;
                        art.height = art.width / texture_aspect;
                        if ( art.height > surface.height )
                        {
                            //reverse
                            art.height = surface.height;
                            art.width = art.height * texture_aspect;
                        }
                    } else
                    {
                        print("tall, tall, fit");
                        art.height = surface.height;
                        art.width = art.height * texture_aspect;
                        if ( art.width > surface.width )
                        {
                            //reverse
                            art.width = surface.width;
                            art.height = art.width / texture_aspect;
                        }
                    }
                }
            } else if ( fit_or_fill == "fill" )
            {
                if ( aspect > 1.0 )
                {
                    //wide
                    if ( texture_aspect > 1.0 )
                    {
                        print("wide, wide, fill");
                        art.height = surface.height;
                        art.width = art.height * texture_aspect;
                        if ( art.width < surface.width )
                        {
                            //reverse
                            art.width = surface.width;
                            art.height = art.width / texture_aspect;
                        }
                    } else
                    {
                        print("wide, tall, fill");
                        art.width = surface.width;
                        art.height = art.width / texture_aspect;
                        if ( art.height < surface.height )
                        {
                            //reverse
                            art.height = surface.height;
                            art.width = art.height * texture_aspect;
                        }
                    }
                } else
                {
                    //tall
                    if ( texture_aspect > 1.0 )
                    {
                        print("tall, wide, fill");
                        art.width = surface.width;
                        art.height = art.width / texture_aspect;
                        if ( art.height < surface.height )
                        {
                            art.height = surface.height;
                            art.width = art.height * texture_aspect;
                        }
                    } else
                    {
                        print("tall, tall, fill");
                        art.height = surface.height;
                        art.width = art.height * texture_aspect;
                        if ( art.width < surface.width )
                        {
                            art.width = surface.width;
                            art.height = art.width / texture_aspect;
                        }
                    }
                }
            } else
            {
                //stretch
                art.preserve_aspect_ratio = false;
                art.width = surface.width;
                art.height = surface.height;           
            }
           
            switch ( anchor )
            {
                case ::Anchor.Left:
                    art.x = 0;
                    art.y = ( surface.height - art.height ) / 2;
                    break;
                case ::Anchor.Right:
                    art.x = surface.width - art.width;
                    art.y = ( surface.height - art.height ) / 2;
                    break;
                case ::Anchor.Top:
                    art.x = ( surface.width - art.width ) / 2;
                    art.y = 0;
                    break;
                case ::Anchor.Bottom:
                    art.x = ( surface.width - art.width ) / 2;
                    art.y = surface.height - art.height;
                    break;
                case ::Anchor.Center:
                default:
                    art.x = ( surface.width - art.width ) / 2;
                    art.y = ( surface.height - art.height ) / 2;
                    break;
            }
        } else
        {
            //fallback to preserve_aspect_ratio
            print("WARNING: FALLBACK TO PRESERVE_ASPECT_RATIO" );
            art.x = 0;
            art.y = 0;
            art.width = surface.width;
            art.height = surface.height;
            art.preserve_aspect_ratio = true;
        }
    }
   
    function _get( idx ) { return surface[idx]; }
    function _set( idx, val )
    {
        if ( idx == "index_offset" ) art.index_offset = val; else surface[idx] = val;
    }
   
    function print( msg )
    {
        if ( debug ) ::print("PreserveArt: " + msg + "\n" );
    }
   
    function set_rgb( r, g, b ) { surface.set_rgb( r, g, b ); }
   
    function onTransition( ttype, var, ttime )
    {
        if ( ttype == Transition.StartLayout || ttype == Transition.ToNewList || ttype == Transition.FromOldSelection ) request_size = true;
    }
   
    function onTick( ttime )
    {
        if ( request_size && art.texture_width != 0 && art.texture_height != 0 ) update();
    }
}

class PreserveImage extends PreserveArt
{
    isArt = false;
}

verion

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 861
    • View Profile
    • new projects
Re: preserve_aspect_ratio, but "fill" an add_surface or add_artwork?
« Reply #37 on: December 10, 2015, 07:36:31 AM »
I'm trying to wrap my head around it.

What option will give me:

- anchor bottom-center
- fit-to-width
- preserve aspect

I'm thinking about displaying snaps - both vertical and horizontal - in 4:3 window, so horizontal snaps will be 100% visible (fill the window completely) and vertical snaps will be cut-off from the top (but preserve aspect and fill window completely)

Bgoulette

  • Sr. Member
  • ****
  • Posts: 116
  • I wrote a book.
    • View Profile
    • BlakeGoulette.com
Re: preserve_aspect_ratio, but "fill" an add_surface or add_artwork?
« Reply #38 on: December 10, 2015, 07:39:30 AM »
I think just setting the anchor to Anchor.Center will do what you want: 4:3 flyers will show full size and "3:4" flyers will show the central portion while filling the width of the screen. If I'm understanding your question...

liquid8d

  • Global Moderator
  • Sr. Member
  • *****
  • Posts: 442
    • View Profile
Re: preserve_aspect_ratio, but "fill" an add_surface or add_artwork?
« Reply #39 on: December 10, 2015, 09:36:00 AM »
Here's examples using it:

Code: [Select]
local art = PreserveArt( "snap", 0, 0, 320, 240 );
art.set_anchor( Anchor.Bottom );
art.set_fit_or_fill( "fit" );

local image = PreserveImage( "image.jpg", 0, 0, 320, 240 );
image.set_anchor( Anchor.Left );
image.set_fit_or_fill( "fill" );

"fit" or "fill" will both preserve the texture aspect. Fill in this sense fills the size of the object but keeps the texture aspect -  "stretch" will be just like preserve_aspect = false and stretch the texture to the width and height of the object.

If you are wanting to change the setting between fit or fill, you can use the set_fit_or_fill function, but will need to write some code to determine when to switch from one to the other (check texture size on transition)

Let me know if you have other questions or suggestions!

« Last Edit: December 10, 2015, 09:39:12 AM by liquid8d »

verion

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 861
    • View Profile
    • new projects
Re: preserve_aspect_ratio, but "fill" an add_surface or add_artwork?
« Reply #40 on: December 10, 2015, 11:47:56 AM »
Great stuff - especially for responsive layouts.
Thanks.

raygun

  • Administrator
  • Sr. Member
  • *****
  • Posts: 393
    • View Profile
Re: preserve_aspect_ratio, but "fill" an add_surface or add_artwork?
« Reply #41 on: December 12, 2015, 10:53:19 PM »
looks great liquid8d.  Could you do a pull request against http://github.com/mickelson/attract/tree/master/config/modules when you are happy with this?  It would be nice to have it included in the base distribution of the frontend.

liquid8d

  • Global Moderator
  • Sr. Member
  • *****
  • Posts: 442
    • View Profile
Re: preserve_aspect_ratio, but "fill" an add_surface or add_artwork?
« Reply #42 on: December 15, 2015, 09:10:38 PM »
I've added it to my attract-extra fork. Still a little green on git - not sure how I submit a pull for the specific file across forks (liquid8d/attract-extra) to (mickelson/attract)

:)

raygun

  • Administrator
  • Sr. Member
  • *****
  • Posts: 393
    • View Profile
Re: preserve_aspect_ratio, but "fill" an add_surface or add_artwork?
« Reply #43 on: December 16, 2015, 11:06:07 PM »
I've added it to my attract-extra fork. Still a little green on git - not sure how I submit a pull for the specific file across forks (liquid8d/attract-extra) to (mickelson/attract)

:)

Hehe me too (on being green on git).  I think the steps go like this if I recall correctly:

- clone http://github.com/mickelson/attract locally
- make your updates and additions to the module files in config/modules
- commit the changes
- push your local repository to your github account

once you do that, there should be a "Create Pull Request" button that shows up in the github web ui, which you can just click to send your changes as a pull request.

It's probably a lot like how you are already working with the attract-extra repository, just with attract instead (and the added step of making the pull request in the github ui).


liquid8d

  • Global Moderator
  • Sr. Member
  • *****
  • Posts: 442
    • View Profile
Re: preserve_aspect_ratio, but "fill" an add_surface or add_artwork?
« Reply #44 on: December 18, 2015, 01:26:13 PM »
that makes sense - i do have a fork of attract, just playing with some customizations locally until maybe I can contribute once I am more familiar with things :) Didn't want to have multiple versions floating around in both forks. I guess as long as I create any modules/layouts/plugins in extra first, I can just push them to attract and submit from there.

Just looking to add one more thing (ability to scale) and verifying the update tick works properly before I submit it.