Tornado Magic Tool by Pere (based on Flowers).

Using sound from an old, Public Domain film (found & extracted by Bill).
This commit is contained in:
William Kendrick 2009-06-05 23:43:35 +00:00
parent 78b99414f5
commit 7b26fdf5e6
7 changed files with 561 additions and 3 deletions

View file

@ -7,7 +7,7 @@ bill@newbreedsoftware.com
http://www.tuxpaint.org/
June 17, 2002 - June 2, 2009
June 17, 2002 - June 5, 2009
$Id$
@ -69,6 +69,20 @@ $Id$
Math for arc used by Real Rainbow provided by
Jeff Newmiller <jdnewmil@dcn.davis.ca.us>
String Art Magic Tools
by Pere Pujal i Carabantes <pere@fornol.no-ip.org>
Tornado Magic Tool
(based on Flowers)
by Pere Pujal i Carabantes <pere@fornol.no-ip.org>
Tornado sound effect from
"Tornado" film, from Prelinger Archives,
produced by Calvin Productions, sponsored by U.S. Weather Bureau.
Extracted and edited using 'mplayer' and 'Audacity'.
Public Domain. Archived at the Internet Archive:
http://www.archive.org/details/tornado
* Graphics

View file

@ -8,7 +8,7 @@ http://www.tuxpaint.org/
$Id$
2009.June.2 (0.9.21)
2009.June.5 (0.9.21)
* New Starters:
-------------
* Silver Frame
@ -58,9 +58,10 @@ $Id$
Creative Commons Attribution 2.0 Generic
http://creativecommons.org/licenses/by/2.0/deed.en
* String Edges - Draw string-like patters around the screen.
* String Edges - Draw string-like patters around the picture.
String Corner - Draw aligned string-like patterns.
String 'V' - Draw free-form string-like patterns.
Tornado (based on Flowers) - Draws a tornado effect onto the picture.
By Pere Pujal i Carabantes <pere@fornol.no-ip.org>
* Icons for some new Magic Tools

BIN
magic/icons/tornado.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 822 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

543
magic/src/tornado.c Normal file
View file

@ -0,0 +1,543 @@
/*
tornado.c
Tornado Magic Tool Plugin
Tux Paint - A simple drawing program for children.
Copyright (c) 2002-2008 by Bill Kendrick and others; see AUTHORS.txt
bill@newbreedsoftware.com
http://www.tuxpaint.org/
Some modifications to convert the flower plugin in to a tornado
plugin by Pere Pujal i Carabantes
pere@fornol.no-ip.org
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
(See COPYING.txt)
Last updated: May 29, 2009
$Id$
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "tp_magic_api.h"
#include "SDL_image.h"
#include "SDL_mixer.h"
/* Our globals: */
enum { SIDE_LEFT, SIDE_RIGHT };
enum { LEAFSIDE_RIGHT_DOWN,
LEAFSIDE_LEFT_DOWN,
LEAFSIDE_RIGHT_UP,
LEAFSIDE_LEFT_UP };
static Mix_Chunk /* * tornado_click_snd, */ * tornado_release_snd;
static Uint8 tornado_r, tornado_g, tornado_b;
static int tornado_min_x, tornado_max_x, tornado_bottom_x, tornado_bottom_y;
static int tornado_side_first;
static int tornado_side_decided;
static SDL_Surface * tornado_base, * tornado_cloud,
* tornado_cloud_colorized;
static int top_w;
/* Local function prototypes: */
typedef struct
{
float x, y;
} Point2D;
static void tornado_predrag(magic_api * api, SDL_Surface * canvas,
SDL_Surface * last, int ox, int oy, int x, int y);
static void tornado_drawbase(magic_api * api, SDL_Surface * canvas);
static void tornado_drawstalk(magic_api * api, SDL_Surface * canvas, SDL_Surface * last,
int top_x, int top_y, int minx, int maxx,
int bottom_x, int bottom_y, int final);
static void tornado_drawtornado(magic_api * api, SDL_Surface * canvas, int x, int y);
static Point2D tornado_PointOnCubicBezier(Point2D* cp, float t);
static void tornado_ComputeBezier(Point2D* cp, int numberOfPoints, Point2D* curve);
static void tornado_colorize_cloud(magic_api * api);
static Uint32 tornado_mess(Uint32 pixel, SDL_Surface * canvas);
Uint32 tornado_api_version(void) { return(TP_MAGIC_API_VERSION); }
// No setup required:
int tornado_init(magic_api * api)
{
char fname[1024];
/*
snprintf(fname, sizeof(fname), "%s/sounds/magic/tornado_click.ogg",
api->data_directory);
tornado_click_snd = Mix_LoadWAV(fname);
*/
snprintf(fname, sizeof(fname), "%s/sounds/magic/tornado_release.ogg",
api->data_directory);
tornado_release_snd = Mix_LoadWAV(fname);
snprintf(fname, sizeof(fname), "%s/images/magic/tornado_base.png",
api->data_directory);
tornado_base = IMG_Load(fname);
snprintf(fname, sizeof(fname), "%s/images/magic/tornado_cloud.png",
api->data_directory);
tornado_cloud = IMG_Load(fname);
return(1);
}
// We have multiple tools:
int tornado_get_tool_count(magic_api * api)
{
return(1);
}
// Load our icons:
SDL_Surface * tornado_get_icon(magic_api * api, int which)
{
char fname[1024];
snprintf(fname, sizeof(fname), "%s/images/magic/tornado.png",
api->data_directory);
return(IMG_Load(fname));
}
// Return our names, localized:
char * tornado_get_name(magic_api * api, int which)
{
return(strdup(gettext_noop("Tornado")));
}
// Return our descriptions, localized:
char * tornado_get_description(magic_api * api, int which, int mode)
{
return(strdup(gettext_noop("Click and drag to draw a tornado stalk. Let go to finish the tornado.")));
}
// Affect the canvas on drag:
static void tornado_predrag(magic_api * api, SDL_Surface * canvas,
SDL_Surface * last, int ox, int oy, int x, int y)
{
if (x < tornado_min_x)
tornado_min_x = x;
if (ox < tornado_min_x)
tornado_min_x = ox;
if (x > tornado_max_x)
tornado_max_x = x;
if (ox > tornado_max_x)
tornado_max_x = ox;
if (y > tornado_bottom_y)
y = tornado_bottom_y;
if (oy > tornado_bottom_y)
y = tornado_bottom_y;
// Determine which way to bend first:
//
if (tornado_side_decided == 0)
{
if (x < tornado_bottom_x - 10)
{
tornado_side_first = SIDE_LEFT;
tornado_side_decided = 1;
}
else if (x > tornado_bottom_x + 10)
{
tornado_side_first = SIDE_RIGHT;
tornado_side_decided = 1;
}
}
}
void tornado_drag(magic_api * api, int which, SDL_Surface * canvas,
SDL_Surface * last, int ox, int oy, int x, int y,
SDL_Rect * update_rect)
{
tornado_predrag(api, canvas, last, ox, oy, x, y);
/* Erase any old stuff; this is a live-edited effect: */
SDL_BlitSurface(last, NULL, canvas, NULL);
/* Draw the base and the stalk (low-quality) for now: */
tornado_drawstalk(api, canvas, last,
x, y, tornado_min_x, tornado_max_x,
tornado_bottom_x, tornado_bottom_y, !(api->button_down()));
tornado_drawbase(api, canvas);
update_rect->x = 0;
update_rect->y = 0;
update_rect->w = canvas->w;
update_rect->h = canvas->h;
}
// Affect the canvas on click:
void tornado_click(magic_api * api, int which, int mode,
SDL_Surface * canvas, SDL_Surface * last,
int x, int y, SDL_Rect * update_rect)
{
tornado_min_x = x;
tornado_max_x = x;
tornado_bottom_x = x;
tornado_bottom_y = y;// - tornado_base->h;
tornado_side_decided = 0;
tornado_side_first = SIDE_LEFT;
tornado_drag(api, which, canvas, last, x, y, x, y, update_rect);
/*
api->playsound(tornado_click_snd, (x * 255) / canvas->w, 255);
*/
}
// Affect the canvas on release:
void tornado_release(magic_api * api, int which,
SDL_Surface * canvas, SDL_Surface * last,
int x, int y, SDL_Rect * update_rect)
{
/* Don't let tornado be too low compared to base: */
if (y >= tornado_bottom_y - 128)
y = tornado_bottom_y - 128;
/* Do final calcs and draw base: */
tornado_predrag(api, canvas, last, x, y, x, y);
/* Erase any old stuff: */
SDL_BlitSurface(last, NULL, canvas, NULL);
/* Draw high-quality stalk, and tornado: */
tornado_drawstalk(api, canvas, last,
x, y, tornado_min_x, tornado_max_x,
tornado_bottom_x, tornado_bottom_y, 1);
tornado_drawtornado(api, canvas, x, y);
tornado_drawbase(api, canvas);
update_rect->x = 0;
update_rect->y = 0;
update_rect->w = canvas->w;
update_rect->h = canvas->h;
api->playsound(tornado_release_snd, (x * 255) / canvas->w, 255);
}
static void tornado_drawtornado(magic_api * api, SDL_Surface * canvas, int x, int y)
{
SDL_Surface * aux_surf;
SDL_Rect dest;
aux_surf = api->scale(tornado_cloud_colorized, top_w *2, top_w,0);
dest.x = x - (aux_surf->w / 2);
dest.y = y - (aux_surf->h / 2);
SDL_BlitSurface(aux_surf, NULL, canvas, &dest);
SDL_FreeSurface(aux_surf);
}
static void tornado_drawbase(magic_api * api, SDL_Surface * canvas)
{
SDL_Rect dest;
dest.x = tornado_bottom_x - (tornado_base->w / 2);
dest.y = tornado_bottom_y - tornado_base->h / 2;
SDL_BlitSurface(tornado_base, NULL, canvas, &dest);
}
static Uint32 tornado_mess(Uint32 pixel, SDL_Surface * canvas)
{
Uint8 r, g, b, a;
float f = (float)rand()*255/RAND_MAX;
SDL_GetRGBA(pixel, canvas->format, &r, &g, &b, &a);
return (SDL_MapRGBA(canvas->format,
(tornado_r + r + (Uint8)f * 2) / 4,
(tornado_g + g + (Uint8)f * 2) / 4,
(tornado_b + b + (Uint8)f * 2) / 4,
a));
}
static void tornado_drawstalk(magic_api * api, SDL_Surface * canvas, SDL_Surface * last,
int top_x, int top_y, int minx, int maxx,
int bottom_x, int bottom_y, int final)
{
Point2D control_points[4];
Point2D * curve;
int i, n_points;
int left, right;
SDL_Rect dest, src;
int xx, yy, side;
/* Compute a nice bezier curve for the stalk, based on the
base (x,y), leftmost (x), rightmost (x), and top (x,y) */
control_points[0].x = top_x;
control_points[0].y = top_y;
if (tornado_side_first == SIDE_LEFT)
{
control_points[1].x = minx;
control_points[2].x = maxx;
}
else
{
control_points[1].x = maxx;
control_points[2].x = minx;
}
control_points[1].y = ((bottom_y - top_y) / 3) + top_y;
control_points[2].y = (((bottom_y - top_y) / 3) * 2) + top_y;
control_points[3].x = bottom_x;
control_points[3].y = bottom_y;
if (final == 0)
n_points = 8;
else
n_points = max(bottom_y - top_y, maxx - minx);
curve = (Point2D *) malloc(sizeof(Point2D) * n_points);
tornado_ComputeBezier(control_points, n_points, curve);
top_w = max(32, n_points * n_points / 1000);
int rotation = 0;
int p;
/* Draw the curve: */
for (i = 0; i < n_points - 1; i++)
{
if (final == 0)
{
dest.x = curve[i].x;
dest.y = curve[i].y;
dest.w = 2;
dest.h = 2;
SDL_FillRect(canvas, &dest, SDL_MapRGB(canvas->format, 0, 0, 0));
}
else
{
int ii;
ii = n_points - i;
/* min 10 pixels then ii^2 / 2000 after some trys */
left = min(curve[i].x, curve[i + 1].x)-5-ii*ii/2000;
right = max(curve[i].x, curve[i + 1].x)+5+ii*ii/2000;
dest.x = left;
dest.y = curve[i].y;
dest.w = right - left + 1;
dest.h = 2;
}
rotation +=3;
/* The body of the tornado: 3x 1y rotation + some random particles */
for (p = dest.x; p < dest.x + dest.w; p++)
{
if ((float)rand() * 100 / RAND_MAX > 10 )
{
api->putpixel(canvas, p, dest.y, api->getpixel(last, dest.x + (p - dest.x + rotation) % dest.w , dest.y));
}
else
{
api->putpixel(canvas, p, dest.y, tornado_mess(api->getpixel(last, dest.x + (p - dest.x + rotation) % dest.w , dest.y), canvas));
}
}
/* Some random particles flying around the tornado */
for (p = dest.x - dest.w * 20 / 100; p < dest.x + dest.w + dest.w * 20 / 100; p++)
{
if ((float)rand() * 100 / RAND_MAX < 5 && ((p < dest.x) || (p > dest.w)))
api->putpixel(canvas, p, dest.y, tornado_mess(api->getpixel(last, dest.x + (p - dest.x + rotation) % dest.w , dest.y), canvas));
}
}
free(curve);
}
void tornado_shutdown(magic_api * api)
{
/*
if (tornado_click_snd != NULL)
Mix_FreeChunk(tornado_click_snd);
*/
if (tornado_release_snd != NULL)
Mix_FreeChunk(tornado_release_snd);
if (tornado_base != NULL)
SDL_FreeSurface(tornado_base);
if (tornado_cloud != NULL)
SDL_FreeSurface(tornado_cloud);
if (tornado_cloud_colorized != NULL)
SDL_FreeSurface(tornado_cloud_colorized);
}
// Record the color from Tux Paint:
void tornado_set_color(magic_api * api, Uint8 r, Uint8 g, Uint8 b)
{
tornado_r = r;
tornado_g = g;
tornado_b = b;
tornado_colorize_cloud(api);
}
// Use colors:
int tornado_requires_colors(magic_api * api, int which)
{
return 1;
}
/*
Code to generate a cubic Bezier curve
*/
/*
cp is a 4 element array where:
cp[0] is the starting point, or P0 in the above diagram
cp[1] is the first control point, or P1 in the above diagram
cp[2] is the second control point, or P2 in the above diagram
cp[3] is the end point, or P3 in the above diagram
t is the parameter value, 0 <= t <= 1
*/
static Point2D tornado_PointOnCubicBezier( Point2D* cp, float t )
{
float ax, bx, cx;
float ay, by, cy;
float tSquared, tCubed;
Point2D result;
/* calculate the polynomial coefficients */
cx = 3.0 * (cp[1].x - cp[0].x);
bx = 3.0 * (cp[2].x - cp[1].x) - cx;
ax = cp[3].x - cp[0].x - cx - bx;
cy = 3.0 * (cp[1].y - cp[0].y);
by = 3.0 * (cp[2].y - cp[1].y) - cy;
ay = cp[3].y - cp[0].y - cy - by;
/* calculate the curve point at parameter value t */
tSquared = t * t;
tCubed = tSquared * t;
result.x = (ax * tCubed) + (bx * tSquared) + (cx * t) + cp[0].x;
result.y = (ay * tCubed) + (by * tSquared) + (cy * t) + cp[0].y;
return result;
}
/*
ComputeBezier fills an array of Point2D structs with the curve
points generated from the control points cp. Caller must
allocate sufficient memory for the result, which is
<sizeof(Point2D) numberOfPoints>
*/
static void tornado_ComputeBezier( Point2D* cp, int numberOfPoints, Point2D* curve )
{
float dt;
int i;
dt = 1.0 / ( numberOfPoints - 1 );
for( i = 0; i < numberOfPoints; i++)
curve[i] = tornado_PointOnCubicBezier( cp, i*dt );
}
static void tornado_colorize_cloud(magic_api * api)
{
Uint32 amask;
int x, y;
Uint8 r, g, b, a;
if (tornado_cloud_colorized != NULL)
SDL_FreeSurface(tornado_cloud_colorized);
/* Create a surface to render into: */
amask = ~(tornado_cloud->format->Rmask |
tornado_cloud->format->Gmask |
tornado_cloud->format->Bmask);
tornado_cloud_colorized =
SDL_CreateRGBSurface(SDL_SWSURFACE,
tornado_cloud->w,
tornado_cloud->h,
tornado_cloud->format->BitsPerPixel,
tornado_cloud->format->Rmask,
tornado_cloud->format->Gmask,
tornado_cloud->format->Bmask, amask);
/* Render the new cloud: */
SDL_LockSurface(tornado_cloud);
SDL_LockSurface(tornado_cloud_colorized);
for (y = 0; y < tornado_cloud->h; y++)
{
for (x = 0; x < tornado_cloud->w; x++)
{
SDL_GetRGBA(api->getpixel(tornado_cloud, x, y),
tornado_cloud->format, &r, &g, &b, &a);
api->putpixel(tornado_cloud_colorized, x, y,
SDL_MapRGBA(tornado_cloud_colorized->format,
(tornado_r + r * 2) / 3, (tornado_g + g * 2) / 3, (tornado_b + b * 2) / 3, a));
}
}
SDL_UnlockSurface(tornado_cloud_colorized);
SDL_UnlockSurface(tornado_cloud);
}
void tornado_switchin(magic_api * api, int which, int mode, SDL_Surface * canvas)
{
}
void tornado_switchout(magic_api * api, int which, int mode, SDL_Surface * canvas)
{
}
int tornado_modes(magic_api * api, int which)
{
return(MODE_PAINT);
}