This is a read-only archive of the old Scratch 1.x Forums.
Try searching the current Scratch discussion forums.

#1 2011-04-09 12:07:54

DarthPickley
Scratcher
Registered: 2008-06-13
Posts: 100+

Math behind Graphics Effects

well, I am here less interested in the Color Effects (since current versions of scratch can't do very much math with colors. It can in panther though.)
Also not really ghost effect, or pixelate or mosaic, though you can discuss those.

My main focus is on how the Whirl effect works, and how the fisheye effect works.

Now, I know that they work differently in flash and java players (flash player it is a circle, java player it is an ellipse)

but I am going to start out assuming that you have a circle with radius of 100, and it has a direction of 90.  now, there is a point on this square, and it has a polar coordinate position of (R,A) (R = radius, A = angle).
This whole circle with the point on it is a costume of a sprite, and so I then set the sprite's whirl effect to w, and fisheye effect to f.

I want to know what the new position of the point is, what the mapping is of the graphic effect.
Here's what I know:
• the only area affected is in a circle around the sprite. r<=100, or x^2 + y^2 <=10000
• whirl effect only changes A as a function of R and parameter w.
• fisheye effect only changes R as a function of R and parameter f.
• (100, A) --W--> (100,A)
• (100, A) --F--> (100,A)
• (0, A) --W(w)--> (0,A+w)
• (0,A) --F(f)--> (0,A)

see (link to project) to test it.

I can have a progress report on this project:
Whirl:
Testing / Data Collection:  yep
Data Fitting / finding formula: yes (?) A' - A = whirl * (1-r/R)^2 where R is the radius of the big circle of influence and r is the distance to the center of the circle of influence
Fisheye:
Testing / Data Collection:
Data Fitting / finding formula:

please join the effort.

If anyone knows how to achieve the getting of this information by hacking the source code, please tell me what the formulas are!
Including Scratch Team!
thanks.

Last edited by DarthPickley (2011-04-10 21:33:00)

Offline

 

#2 2011-04-09 14:21:42

LS97
Scratcher
Registered: 2009-06-14
Posts: 1000+

Re: Math behind Graphics Effects

I'd love to find out whatever you want to find out but I didn't understand a word of what you said above  tongue

Offline

 

#3 2011-04-09 14:31:06

DarthPickley
Scratcher
Registered: 2008-06-13
Posts: 100+

Re: Math behind Graphics Effects

well, lets say you had a stretch effect. the stretch effect is a percentage stretcher that stretches along the direction that the object is pointing. it is done BEFORE rotation.
x --> k * x
y --> k * y.

To understand how whirl effect works with rotation, see Whirl + Spin

Offline

 

#4 2011-04-09 14:32:44

LS97
Scratcher
Registered: 2009-06-14
Posts: 1000+

Re: Math behind Graphics Effects

What are you trying to discover/do?

Offline

 

#5 2011-04-09 15:09:14

DarthPickley
Scratcher
Registered: 2008-06-13
Posts: 100+

Re: Math behind Graphics Effects

I want to know how to artificially recreate these effects, like with pen, so that I will be able to use them and understand how they work more deeply.
So far, with squeak, I have  found

Code:

    whirl = 0
        ifFalse: 
            [t2 _ resultForm deepCopy.
            ScratchPlugin
                primWhirl: resultForm bits
                into: t2 bits
                width: resultForm width
                angle: whirl asInteger.
            resultForm _ t2].
    fisheye = 0
        ifFalse: [ScratchPlugin
                primFisheye: resultForm bits copy
                into: resultForm bits
                width: resultForm width
                power: (fisheye asInteger + 100 max: 0)].

but this doesn't give me the equations exactly, now I will have to dig deeper and deeper.

Offline

 

#6 2011-04-09 15:10:24

DarthPickley
Scratcher
Registered: 2008-06-13
Posts: 100+

Re: Math behind Graphics Effects

An application of this knowledge would be this

Offline

 

#7 2011-04-10 16:42:24

DarthPickley
Scratcher
Registered: 2008-06-13
Posts: 100+

Re: Math behind Graphics Effects

also see this from a long time ago (only works in JAVA player!) where I was attempting this. I have been thinking about this stuff for a while. I'd really love an answer. Anyone from scratch team?

Offline

 

#8 2011-04-10 21:42:54

DarthPickley
Scratcher
Registered: 2008-06-13
Posts: 100+

Re: Math behind Graphics Effects

Okay, I think I may have an equation for whirl with no fisheye:
A = w * (1-r/R)^2
where
r = distance to center of circle of influence
R = radius of circle of influence
A = change in angle
w = whirl effect

also DOES ANYBODY KNOW SQUEAK TO INTERPRET THE CODE IN PREVIOUS POST?

http://scratch.mit.edu/static/projects/DarthPickley/1715278_sm.png

Offline

 

#9 2011-04-11 02:28:10

LS97
Scratcher
Registered: 2009-06-14
Posts: 1000+

Re: Math behind Graphics Effects

I once saw, in the code that applies graphic effects, a reference to a primitive in the ScratchPlugin.

Here you can find the source of the plugin, and I should guess you know some basic C enough to understand it. Good luck in finding it though!

I'm boarding a plane in an hour, so see you in two weeks  smile

Offline

 

#10 2011-04-11 19:21:01

DarthPickley
Scratcher
Registered: 2008-06-13
Posts: 100+

Re: Math behind Graphics Effects

LS97 wrote:

I once saw, in the code that applies graphic effects, a reference to a primitive in the ScratchPlugin.

Here you can find the source of the plugin, and I should guess you know some basic C enough to understand it. Good luck in finding it though!

I'm boarding a plane in an hour, so see you in two weeks  smile

Thanks for that info, I didn't know to look in the Plugin!

I just spent about an hour reformatting the C code so that I could interpret it, I did it for the WHOLE THING.

For my purposes I probably could have just formatted the whirl & fisheye parts:

Code:

EXPORT int primitiveWhirl(void) {
    int width;
    double whirlRadians;
    int degrees;
    double scaleX;
    double scaleY;
    int pix;
    double d;
    double ang;
    double sina;
    int y;    double dx;
    double dy;
    int sz;
    unsigned int * in;
    double radiusSquared;
    int inOop;
    int outOop;
    double factor;
    double cosa;
    unsigned int * out;
    int centerX;
    int centerY;
    int radius;
    int x;
    int height;

    inOop = interpreterProxy->stackValue(3);
    outOop = interpreterProxy->stackValue(2);
    width = interpreterProxy->stackIntegerValue(1);
    degrees = interpreterProxy->stackIntegerValue(0);

    /* begin checkedUnsignedIntPtrOf: */
    interpreterProxy->success(interpreterProxy->isWords(inOop));
    if (interpreterProxy->failed()) {
        in = 0;
        goto l1;
    }
    in = ((unsigned int *) (interpreterProxy->firstIndexableField(inOop)));

l1:
    /* end checkedUnsignedIntPtrOf: */;
    /* begin checkedUnsignedIntPtrOf: */
    interpreterProxy->success(interpreterProxy->isWords(outOop));
    if (interpreterProxy->failed()) {
        out = 0;
        goto l2;
    }
    out = ((unsigned int *) (interpreterProxy->firstIndexableField(outOop)));

l2:
    /* end checkedUnsignedIntPtrOf: */;
    sz = interpreterProxy->stSizeOf(inOop);
    interpreterProxy->success((interpreterProxy->stSizeOf(outOop)) == sz);
    if (interpreterProxy->failed()) {
        return 0;
    }
    height = sz / width;
    centerX = width / 2;
    centerY = height / 2;
    if (centerX < centerY) {
        radius = centerX;
        scaleX = (((double) centerY )) / centerX;
        scaleY = 1.0;
    } else {
        radius = centerY;
        scaleX = 1.0;
        if (centerY < centerX) {
            scaleY = (((double) centerX )) / centerY;
        } else {
            scaleY = 1.0;
        }
    }
    whirlRadians = (-3.141592653589793 * degrees) / 180.0;
    radiusSquared = ((double) (radius * radius) );
    for (x = 0; x <= (width - 1); x += 1) {
        for (y = 0; y <= (height - 1); y += 1) {
            dx = scaleX * (((double) (x - centerX) ));
            dy = scaleY * (((double) (y - centerY) ));
            d = (dx * dx) + (dy * dy);
            if (d < radiusSquared) {
                factor = 1.0 - ((sqrt(d)) / radius);
                ang = whirlRadians * (factor * factor);
                sina = sin(ang);
                cosa = cos(ang);
                pix = interpolatedFromxywidthheight(in, 
((int) (1024.0 * ((((cosa * dx) - (sina * dy)) / scaleX) + centerX)) ), 
((int) (1024.0 * ((((sina * dx) + (cosa * dy)) / scaleY) + centerY)) ), width, height);
                out[(width * y) + x] = pix;
            }
        }
    }
    interpreterProxy->pop(4);
    return 0;
}

and Fisheye is

Code:

EXPORT int primitiveFisheye(void) {
    int width;
    int srcX;
    int srcY;
    int pix;
    int power;
    int y;
    double ang;
    double scaledPower;
    double dx;
    double dy;
    int sz;
    unsigned int * in;
    int inOop;
    int outOop;
    unsigned int * out;
    int centerX;
    int centerY;
    int x;
    double r;
    int height;

    inOop = interpreterProxy->stackValue(3);
    outOop = interpreterProxy->stackValue(2);
    width = interpreterProxy->stackIntegerValue(1);
    power = interpreterProxy->stackIntegerValue(0);

    /* begin checkedUnsignedIntPtrOf: */

    interpreterProxy->success(interpreterProxy->isWords(inOop));
    if (interpreterProxy->failed()) {
        in = 0;
        goto l1;
    }
    in = ((unsigned int *) (interpreterProxy->firstIndexableField(inOop)));

l1:
    /* end checkedUnsignedIntPtrOf: */;

    /* begin checkedUnsignedIntPtrOf: */

    interpreterProxy->success(interpreterProxy->isWords(outOop));
    if (interpreterProxy->failed()) {
        out = 0;
        goto l2;
    }
    out = ((unsigned int *) (interpreterProxy->firstIndexableField(outOop)));

l2:    /* end checkedUnsignedIntPtrOf: */;
    sz = interpreterProxy->stSizeOf(inOop);
    interpreterProxy->success((interpreterProxy->stSizeOf(outOop)) == sz);
    if (interpreterProxy->failed()) {
        return 0;
    }

    height = sz / width;
    centerX = width / 2;
    centerY = height / 2;
    height = sz / width;
    centerX = width / 2;
    centerY = height / 2;
    scaledPower = power / 100.0;

    for (x = 0; x <= (width - 1); x += 1) {
        for (y = 0; y <= (height - 1); y += 1) {
            dx = (x - centerX) / (((double) centerX ));
            dy = (y - centerY) / (((double) centerY ));
            r = pow((sqrt((dx * dx) + (dy * dy))),scaledPower);
            if (r <= 1.0) {
                ang = atan2(dy,dx);
                srcX = ((int) (1024 * (centerX + ((r * (cos(ang))) * centerX))) );
                srcY = ((int) (1024 * (centerY + ((r * (sin(ang))) * centerY))) );
            } else {
                srcX = 1024 * x;
                srcY = 1024 * y;
            }
            pix = interpolatedFromxywidthheight(in, srcX, srcY, width, height);
            out[(y * width) + x] = pix;
        }
    }
    interpreterProxy->pop(4);
    return 0;
}

now I just have to analyze this. oh, and also figure out what
interpolatedFromxywidthheight(in,srcX,srcY,width,height) does, but probably that's not that important.


---------------------------------------------------------------------

so the basic part of the fisheye is:

Code:

dx = (x - centerX) / (((double) centerX ));
dy = (y - centerY) / (((double) centerY ));
r = pow((sqrt((dx * dx) + (dy * dy))),scaledPower);
if (r <= 1.0) {
    ang = atan2(dy,dx);
    srcX = ((int) (1024 * (centerX + ((r * (cos(ang))) * centerX))) );
    srcY = ((int) (1024 * (centerY + ((r * (sin(ang))) * centerY))) );
} else {
    srcX = 1024 * x;
    srcY = 1024 * y;
}

so fisheye works because of scaledPower?

for whirl:

Code:

dx = scaleX * (((double) (x - centerX) ));
dy = scaleY * (((double) (y - centerY) ));
d = (dx * dx) + (dy * dy);
if (d < radiusSquared) {
    factor = 1.0 - ((sqrt(d)) / radius);
    ang = whirlRadians * (factor * factor);
    sina = sin(ang);
    cosa = cos(ang);
    pix = interpolatedFromxywidthheight(in, 
    ((int) (1024.0 * ((((cosa * dx) - (sina * dy)) / scaleX) + centerX)) ), 
    ((int) (1024.0 * ((((sina * dx) + (cosa * dy)) / scaleY) + centerY)) ), width, height);

It appears that I was right:
if it takes factor = 1 - r/R, then ang = whirl * factor^2.
I'm not sure how interpolatedFromxywidthheight works, but it looks like I was right!
and fisheye seems to just obey a power law, so

r = R * (r'/R)^(1+fisheye/100)

I guess that this is why fisheye = -100 means that you are saying r = r'^0 = 1, so everything reads from the edge.

SOLVED THE MYSTERY!

Last edited by DarthPickley (2011-04-11 19:52:46)

Offline

 

Board footer