scaleFactor strangeness

-hh hh at livecode.org
Sun Oct 18 05:03:22 EDT 2015


[Sorry for the late answer, couldn't login to jasmine for a while.]

@Mark
Your explanations, quoted below, are so valuable that they should remain also readable for non-math people (that don't have a math-involved university degree).

So let us go back for a while from some general writing in transformation matrices to a simple real number. Also let us look only at the single (PROPORTIONAL) SCALEFACTOR, not split into a first-coord and second-coord one.

[Example 1]
> This handler would still function regardless of the transform of
> the graphic containing it. Indeed, if co-ordinates are delivered to
> it in the target object's local co-ordinate system - then it does.
> on mouseMove pX, pY
>    if pX < item 1 of the loc of me then
>      if pY < item 2 of the loc of me then
>        set the backColor of me to red
>      else
>        set the backColor of me to green
>      end if
>    else
>      if pY < item 2 of the loc of me then
>        set the backColor of me to blue
>      else
>        set the backColor of me to orange
>      end if
>    end if
> end mouseMove

[Example 2]
> However, here is a handler which would not work fine in this instance:
> on mouseMove pX, pY
>    if pY < the top of me + 20 then
>      set the backColor of me to red
>    else if pY > the bottom of me - 20 then
>      set the backColor of me to green
>    else
>      set the backColor of me to blue
>    end if
> end mouseMove

For me there is no difference in coords between Examples 1 and 2.
If all coords are in card-space then absolute values (like 20) are too.
This is definable and should be defined this way.

If a user means 20 pixels in the object-space (although pX and the top of me are in card-space) then it can be adjusted:

if pY < the top of me + 20*SF(me)

If a scripter doesn't touch default scalefactors, then SF(me) of an object is one and has no scaling effect, similar with SF(the target).

I personally would prefer and use your general approach of affine transforms but the simple "scalefactor" of objects is easy to understand, also for beginners.
 
We have the total scalefactor for objects that are direct on card level:
TSF(object)=SF(stack)*SF(card)*SF(object)

If we now set SF(card)=1/SF(stack), then we have everything in usual local card coords, the display remains the same, but we can by emulating your usual adjusting of SF(stack) look at an emulated device display --- PMDE (poor man's device emulator).

From your other considerations regarding 'loc', 'left', 'top', 'right', 'bottom', 'width' and 'height' I can see, that you are nearly done, you could now write the (general transform) code right out of your brain. Let me repeat in my words to see if I got it all and give one more definition. We have the following kind of points:

* appPoints (x,y) = point on physical device = pixel (x,y)

Now define: the stack is owned by the application and
* relativePoint (x,y) = pixel (x,y) relative to the topleft of object
* ownerPoint (x,y) = pixel (x,y) relative to the topleft of object's owner

Of course: ownerPoints(object) = relativePoints(owner of object)

So, without scaling:
* ownerPoints(stack) = the app's coords (may tranform to other screen)
* ownerPoints(card) = the usual global coords
* ownerPoints(object on card) = the usual local coords
* ownerPoints(object in group) = relativePoints(group)

Now respect scalefactors:
* ownerPoints(stack) are handled by the engine only
* ownerPoints(card) = global coords scaled by TSF(stack)
* ownerPoints(object on card) = local coords scaled by TSF(card)
* ownerPoints(object in group) = local coords scaled by TSF(group)
* relativePoints(object in group) = local coords scaled by TSF(object)

here is TSF(object) = (SF of owners along owner path)*SF(object) 
and again ownerPoints(object in group) = relativePoints(group)

Most of scaling would be done down to the owner of an object only, I presume.
For example working within a group with relative group coords and then simply setting SF(group) would be very attractive. That's why I would miss ownerPoints.

What's new then?
A further property for each object.
And some simple transforms that respect the scalefactors along the owner path, and are affine transformations ;-)

object2group, object2card, card2stack, substack2stack
group2object, card2object, stack2card, stack2substack

> Of course there is a gulf between having an idea that might work and 
> implementing it in the engine - the lack of floating point co-ordinates 
> is going to be a huge issue here I think

One could work always with floating points? Then let the engine round, not before the very end of calculations, whenever an integer is needed?

> The 'visibleRect' of a stack  is at least feasible in the near term
> - which at least gives zoom and  panning at the card-level if not at
> the individual object level.

This is a great feature. And with this, apart from clipping to the stacks's window, we have already scalefactor(card) available, isn't it?

FEATURE REQUEST =====> the objectPoint(x,y) = relativePoint(x,y) in object space
would be a very welcome feature just now. It's your idea, please don't throw it away.

Thank you very much for your explanations.

Kind regards, Hermann

[This is an ad]
on preopenstack -- poor man's golden ratio emulating device
   set scaleFactor of this stack to (sqrt(5)+1)/2
   set scaleFactor of this card to 1/the scaleFactor of this stack
end preopenstack

> On 2015-10-15 19:42, hh wrote:
> > Not really, if you mean affine transform. Translation destroys the 
> > wonderful
> > commutativity you have with my proposal: If you interchange objects in 
> > the
> > owner path/message path before a target then this has no effect on the 
> > total
> > scalefactor of this target. And you know the "inverse" operation as a 
> > function!
> > Beside the usual rounding effects I can see no problem with that at the 
> > moment.
> 
> I'm not sure I follow - commutativity makes no difference in this 
> instance - you only need invertibility and associativity. Affine 
> transforms can be represented as matrices and matrix multiplication is 
> associative, and non-degenerate matrices have inverses - thus you can 
> replace any scale operation in your proposal with an arbitrary 
> (invertible) matrix representing an affine transform.
> 
> Indeed, in that light, what you propose is precisely the same as the 
> idea of every object being able to have a transform property (if not set 
> it is the identity) - the total transform of a nested object is the 
> multiplication of all the ancestor transforms. This is how most UI 
> toolkits (which allow arbitrary affine transforms) work and from a 
> rendering point of view poses no problem.
> 
> > The stack (and it's view) is the global object and handles with the OS 
> > and
> > the hardware (you have a lot of pretty things already realized for us).
> > Everything in the stack is in local coords.
> 
> Yes - this is how it is at the moment - you can translate between local 
> (card-relative) and global (screen-relative) using localLoc and 
> globalLoc functions.
> 
> > The mouseLoc etc. is scaled by TSF(mouse) = 
> > SF(stack)*SF(card)*SF(mouse).
> > We don't have to care about such things, you do in the engine.
> > And it is scripter's problem to ask for the total scalefactor ('TSF')
> > of objects.
> 
> Okay - so you suggest that any co-ordinates coming from the engine 
> remain in card-space - where card-space is the top-level transform.
> 
> This does mean that script will have to explicitly deal with all 
> co-ordinate transforms. So let's say you have a mouseMove handler in a 
> graphic which uses the location of the mouse within itself to change its 
> color:
> 
> on mouseMove pX, pY
>    if pX < item 1 of the loc of me then
>      if pY < item 2 of the loc of me then
>        set the backColor of me to red
>      else
>        set the backColor of me to green
>      end if
>    else
>      if pY < item 2 of the loc of me then
>        set the backColor of me to blue
>      else
>        set the backColor of me to orange
>      end if
>    end if
> end mouseMove
> 
> In an ideal world, this handler would still function regardless of the 
> transform of the graphic containing it. Indeed, if co-ordinates are 
> delivered to it in the target object's local co-ordinate system - then 
> it does.
> 
> However, for arbitrary transforms where the pX, pY is kept in the card 
> co-ordinate system it cannot - the reason for this is that under an 
> arbitrary affine transform a rectangle which is parallel on both sides 
> to the pixel grid may cease to be parallel to the pixel grid which means 
> that geometry changes (when doing things extrinsically from the target 
> object, at least).
> 
> Now, it is clear that if you restrict the transform to scale (and 
> translation) then because such things preserve rectangles in both 
> orientation and alignment relative to the pixel grid any non-size 
> related geometry conditions you code remain correct - so the above 
> handler would work fine.
> 
> However, here is a handler which would not work fine in this instance:
> 
> on mouseMove pX, pY
>    if pY < the top of me + 20 then
>      set the backColor of me to red
>    else if pY > the bottom of me - 20 then
>      set the backColor of me to green
>    else
>      set the backColor of me to blue
>    end if
> end mouseMove
> 
> Under a 'keep everything at the card-space' model - this code breaks as 
> the scale of the target object changes. This is because the handler is 
> dependent on a size (20) which is relative to the object's innate size 
> rather than its scaled size. So, for this handler to work (even in a 
> restricted, scale/translate only transform model) the script would need 
> to be amended:
> 
> on mouseMove pX, pY
>    if pY < the top of me + 20 * the effective yScale of me then
>      set the backColor of me to red
>    else if pY > the bottom of me - 20 * the effective xScale of me then
>      set the backColor of me to green
>    else
>      set the backColor of me to blue
>    end if
> end mouseMove
> 
> Thus, in reality, keeping things in 'card-level' co-ordinate system and 
> only allowing scale/translation makes no difference to the requirements 
> on code. For general scripts which manipulate co-ordinates in anything 
> other than very simple ways, you still have to explicitly take into 
> account the potential effects of transformation thus, at the end of the 
> day, you might as well allow arbitrary affine transforms since the code 
> burden is identical. For example, the above modified code works if only 
> scaling is allowing, however the code for the case of arbitrary 
> transforms would be something like:
> 
> on mouseMove pX, pY
>    local tY
>    put item 2 of objectLoc(pX, pY) into tY
>    if tY <  20 then
>      set the backColor of me to red
>    else if tY > the relative height of me - 20 then
>      set the backColor of me to green
>    else
>      set the backColor of me to blue
>    end if
> end mouseMove
> 
> Here, I'm assuming a function 'objectLoc' which transforms a point from 
> card-space to object-space (object-space being defined by the 
> concatenation of transforms from itself up to the card). I'm also 
> assuming that 'loc', 'left', 'top', 'right', 'bottom', 'width' and 
> 'height' have 'relative' adjectives which return them in object-space, 
> rather than card-space.
> 
> Perhaps an 'objectLoc' idea and arbitrary affine transforms are not 
> something to be concerned about. If you use transforms you might have to 
> do some co-ordinate juggling, if you are writing controls which you want 
> to be used generally - again, you might have to do some co-ordinate 
> juggling. The rule is just that if passing co-ordinates between objects 
> (unless there is a prior agreement), you need to keep things in 
> card-space and let the code in the target objects do the appropriate 
> transform.
> 
> Of course there is a gulf between having an idea that might work and 
> implementing it in the engine - the lack of floating point co-ordinates 
> is going to be a huge issue here I think... The 'visibleRect' of a stack 
> is at least feasible in the near term - which at least gives zoom and 
> panning at the card-level if not at the individual object level.
> 
> Plenty to think about!
> 
> Warmest Regards,
> 
> Mark.
> 
> -- 
> Mark Waddingham ~ mark at livecode.com ~ http://www.livecode.com/
> LiveCode: Everyone can create apps




More information about the use-livecode mailing list