/***************************************************************************** This demo was converted from the X Screensaver distribution. The original copyright now follows. Copyright (c) 1995 Pascal Pensa <pensa@aurora.unice.fr> Original idea : Guillaume Ramey <ramey@aurora.unice.fr> Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation. This file is provided AS IS with no warranties of any kind. The author shall have no liability with respect to the infringement of copyrights, trade secrets or any patents by this file or any part thereof. In no event will the author be liable for any lost revenue or profits or other special, indirect and consequential damages. ****************************************************************************** ###CLASS### Name: Forest Version: 1.0 Status: Stable ID: ID_FOREST Category: Effect Date: July 2001 Author: Rocklyte Systems, Pascal Pensa, Guillaume Ramey Copyright: Guillaume Ramey, Pascal Pensa (c) 1995, Rocklyte Systems (c) 2000-2001. Short: Draws multiple tree-like fractals. Keywords: forest, fractal ###DESCRIPTION### <p>The Forest class is a fractal generator that draws multiple tree-like structures to generate a colourful forest. The formula was converted from the X screensaver distribution, so you may have seen this effect in use before.</p> <p>Once initialised, the fractal generator will run automatically until it is freed from the system. The trees are drawn in a single burst, then there is a gap of approximately 10 seconds before the graphics are cleared and the generator starts drawing again from scratch. A number of options are available for manipulating the way in which the fractals are generated. Details on the settings are documented for each individual field, so refer to them if you require more information.</p> ###END### *****************************************************************************/ #include <pandora/main.h> #include <pandora/system/class.h> #include <pandora/graphics/bitmap.h> #include <pandora/graphics/render.h> #include <pandora/system/module.h> #include <pandora/modules/render.h> #include <math.h> static ERROR CMDInit(OBJECTPTR, struct KernelBase *); static ERROR CMDExpunge(void); #define VER_FOREST 1.0 EXPORT struct ModHeader ModHeader = { MODULE_HEADER_V1, CMDInit, NULL, NULL, CMDExpunge, JMP_DEFAULT, 0, CPU_PC, VER_FOREST, VER_KERNEL, "Rocklyte Systems, Guillaume Ramey, Pascal Pensa", "All rights reserved.", "July 2001", "Forest" }; static struct KernelBase *KernelBase = NULL; static struct RenderBase *RenderBase = NULL; static OBJECTPTR RenderModule = NULL; static OBJECTPTR ForestClass = NULL; /****************************************************************************/ #define SINF(n) ((float)sin(n)) #define COSF(n) ((float)cos(n)) #define FABSF(n) ((float)fabs((double)(n))) /*** Degree to radian ***/ #define DEGTORAD(x) (((float)(x)) * 3.1416 / 180.0) #define RANGE_RAND(min,max) ((min) + RandomNumber((max) - (min))) /***************************************************************************** ** Forest class definition. */ struct Forest { OBJECT_HEADER LONG MaxElements; /* Total number of trees */ LONG MinHeight; /* Tree minimum height */ LONG MaxHeight; /* Tree maximum height */ LONG MinAngle; /* Angle */ LONG MaxAngle; /* Angle */ LONG Conformity; /* Max random angle from default */ LONG IterationLevel; /* Tree iteration */ /*** Storage area ***/ OBJECTID DrawableID; OBJECTID TimerID; struct Bitmap *Bitmap; WORD BitmapWidth; WORD BitmapHeight; LONG TreeCount; LONG Time; }; static ERROR SET_Conformity(struct Forest *, LONG *); static ERROR SET_IterationLevel(struct Forest *, LONG *); static ERROR SET_MaxAngle(struct Forest *, LONG *); static ERROR SET_MaxElements(struct Forest *, LONG *); static ERROR SET_MaxHeight(struct Forest *, LONG *); static ERROR SET_MinAngle(struct Forest *, LONG *); static ERROR SET_MinHeight(struct Forest *, LONG *); static struct FieldArray ForestFields[] = { { "MaxElements", 0, FDF_LONG|FDF_RW, 0, 0, SET_MaxElements }, { "MinHeight", 0, FDF_LONG|FDF_RW, 0, 0, SET_MinHeight }, { "MaxHeight", 0, FDF_LONG|FDF_RW, 0, 0, SET_MaxHeight }, { "MinAngle", 0, FDF_LONG|FDF_RW, 0, 0, SET_MinAngle }, { "MaxAngle", 0, FDF_LONG|FDF_RW, 0, 0, SET_MaxAngle }, { "Conformity", 0, FDF_LONG|FDF_RW, 0, 0, SET_Conformity }, { "IterationLevel", 0, FDF_LONG|FDF_RW, 0, 0, SET_IterationLevel }, END_FIELD }; static ERROR FOREST_ActionNotify(struct Forest *, struct acActionNotify *); static ERROR FOREST_Free(struct Forest *, APTR); static ERROR FOREST_Init(struct Forest *, APTR); static ERROR FOREST_NewObject(struct Forest *, APTR); static ERROR FOREST_Timer(struct Forest *, struct acTimer *); static struct ActionArray ForestActions[] = { { AC_ActionNotify, FOREST_ActionNotify }, { AC_Free, FOREST_Free }, { AC_Init, FOREST_Init }, { AC_NewObject, FOREST_NewObject }, { AC_Timer, FOREST_Timer }, { NULL, NULL } }; static void DrawTree(struct Forest *, struct Bitmap *, WORD, WORD, LONG, FLOAT, FLOAT, struct RGB, LONG); /***************************************************************************** ** Command: Init() */ static ERROR CMDInit(OBJECTPTR argModule, struct KernelBase *argKernelBase) { KernelBase = argKernelBase; if (CreateObject(ID_MODULE, NULL, &RenderModule, NULL, FID_Name|TSTRING, "render", FID_Version|TFLOAT, MODVERSION_RENDER, TAGEND) IS ERR_Okay) { if (GetField(RenderModule, FID_ModBase, FT_POINTER, &RenderBase) != ERR_Okay) { return(ObjectError(argModule, ERH_InitModule, ERR_GetField)); } } else return(ObjectError(argModule, ERH_InitModule, ERR_CreateObject)); return(CreateObject(ID_CLASS, NULL, (OBJECTPTR *)&ForestClass, NULL, FID_BaseClassID|TLONG, ID_FOREST, FID_Version|TFLOAT, VER_FOREST, FID_Name|TSTRING, "Forest", FID_Category|TLONG, CCF_EFFECT, FID_Actions|TPTR, ForestActions, FID_Fields|TPTR, ForestFields, FID_Size|TLONG, sizeof(struct Forest), TAGEND)); } /***************************************************************************** ** Command: Expunge() */ static ERROR CMDExpunge(void) { if (RenderModule) { Action(AC_Free, RenderModule, NULL); RenderModule = NULL; } if (ForestClass) { Action(AC_Free, ForestClass, NULL); ForestClass = NULL; } return(ERR_Okay); } /***************************************************************************** ** Forest: ActionNotify */ static ERROR FOREST_ActionNotify(struct Forest *Self, struct acActionNotify *NotifyArgs) { if ((NotifyArgs->ActionID IS AC_Redimension) IS (NotifyArgs->Error IS ERR_Okay)) { struct Bitmap *bitmap; WORD width, height; if (LockDrawableBitmap(Self->DrawableID, &bitmap, NULL) IS ERR_Okay) { width = bitmap->ClipRight - bitmap->ClipLeft; height = bitmap->ClipBottom - bitmap->ClipTop; if (width > Self->BitmapWidth) { ActionTags(MT_DrawRectangle, bitmap, Self->BitmapWidth, 0, width - Self->BitmapWidth, Self->BitmapHeight, 0x000000, TRUE); } if (height > Self->BitmapHeight) { ActionTags(MT_DrawRectangle, bitmap, 0, Self->BitmapHeight, width, height - Self->BitmapHeight, 0x000000, TRUE); } Self->BitmapWidth = width; Self->BitmapHeight = height; UnlockDrawableBitmap(Self->DrawableID, bitmap); } return(ERR_Okay); } else return(ERR_Failed); } /***************************************************************************** ** Forest: Free */ static ERROR FOREST_Free(struct Forest *Self, APTR Void) { UnsubscribeTimer(Self->Head.UniqueID); return(ERR_Okay); } /***************************************************************************** ** Forest: Init */ static ERROR FOREST_Init(struct Forest *Self, APTR Void) { OBJECTPTR drawable; /*** Find the Drawable object that we are associated with ***/ if (!Self->DrawableID) { OBJECTID OwnerID; OwnerID = GetOwner(Self); while ((OwnerID) AND (GetClassID(OwnerID) != ID_RENDER)) { OwnerID = GetOwnerID(OwnerID); } if (!OwnerID) return(ObjectError(Self, ERH_Init, ERR_UnsupportedOwner)); else Self->DrawableID = OwnerID; } /*** Validate fields ***/ if (Self->MaxElements < 5) Self->MaxElements = 5; if (AccessObject(Self->DrawableID, 3000, &drawable) IS ERR_Okay) { struct Bitmap *bitmap; SubscribeActionTags(drawable, Self, AC_Redimension, TAGEND); if (LockDrawableBitmap(Self->DrawableID, &bitmap, NULL) IS ERR_Okay) { Action(AC_Clear, bitmap, NULL); Self->BitmapWidth = bitmap->ClipRight - bitmap->ClipLeft; Self->BitmapHeight = bitmap->ClipBottom - bitmap->ClipTop; UnlockDrawableBitmap(Self->DrawableID, bitmap); } ReleaseObject(drawable); } /*** Initialise the Timer ***/ return(SubscribeTimer(Self->Head.UniqueID, 40)); } /***************************************************************************** ** Forest: NewObject */ static ERROR FOREST_NewObject(struct Forest *Self, APTR Void) { Self->MaxElements = 50; Self->MinHeight = 20; Self->MaxHeight = 40; Self->MinAngle = 15; Self->MaxAngle = 35; Self->Conformity = 15; Self->IterationLevel = 1; return(ERR_Okay); } /***************************************************************************** ** Forest: Timer */ static ERROR FOREST_Timer(struct Forest *Self, struct acTimer *Args) { struct Bitmap *bitmap; struct RGB rgb; WORD width, height, x, y, x2, y2, len; FLOAT a, as; if (LockDrawableBitmap(Self->DrawableID, &bitmap, NULL) != ERR_Okay) return(ERR_LockFailed); width = bitmap->ClipRight - bitmap->ClipLeft; height = bitmap->ClipBottom - bitmap->ClipTop; Self->Time += Args->TimeElapsed; if (Self->TreeCount < Self->MaxElements) { x = RANGE_RAND(0, width); y = RANGE_RAND(0, height + Self->MaxHeight); a = -3.1416 / 2.0 + DEGTORAD(RandomNumber(2 * Self->Conformity) - Self->Conformity); as = DEGTORAD(RANGE_RAND(Self->MinAngle, Self->MaxAngle)); len = ((RANGE_RAND(Self->MinHeight, Self->MaxHeight) * (width / 20)) / 50) + 2; rgb.Red = RandomNumber(180); rgb.Green = RandomNumber(180); rgb.Blue = RandomNumber(180); x2 = x + (WORD)(COSF(a) * ((FLOAT)len)); y2 = y + (WORD)(SINF(a) * ((FLOAT)len)); ActionTags(MT_DrawRGBLine, bitmap, x, y, x2, y2, rgb.Red, rgb.Green, rgb.Blue); ActionTags(MT_DrawRGBLine, bitmap, x+1, y, x2+1, y2, rgb.Red, rgb.Green, rgb.Blue); DrawTree(Self, bitmap, x2, y2, (len * 90) / 100, a, as, rgb, 1); Self->TreeCount++; DelayMsg(MT_Expose, Self->DrawableID, NULL); } /*** If the timer has reached its maximal count, reset the forest and start again ***/ if (Self->Time >= 10000) { Action(AC_Clear, bitmap, NULL); Self->Time = 0; Self->TreeCount = 0; } UnlockDrawableBitmap(Self->DrawableID, bitmap); return(ERR_Okay); } /**************************************************************************** ** Internal: DrawTree() ** Short: Draws a single forest tree. */ void DrawTree(struct Forest *Self, struct Bitmap *Bitmap, WORD x, WORD y, LONG len, FLOAT a, FLOAT as, struct RGB rgb, LONG level) { WORD x1, y1, x2, y2, redspeed, greenspeed, bluespeed; FLOAT a1, a2; redspeed = (255 - rgb.Red) / (Self->IterationLevel + 12); greenspeed = (255 - rgb.Green) / (Self->IterationLevel + 12); bluespeed = (255 - rgb.Blue) / (Self->IterationLevel + 12); /* left */ a1 = a + as + DEGTORAD(RandomNumber(2 * Self->Conformity) - Self->Conformity); x1 = x + (short)(COSF(a1) * ((FLOAT)len)); y1 = y + (short)(SINF(a1) * ((FLOAT)len)); /* right */ a2 = a - as + DEGTORAD(RandomNumber(2 * Self->Conformity) - Self->Conformity); x2 = x + (short)(COSF(a2) * ((FLOAT)len)); y2 = y + (short)(SINF(a2) * ((FLOAT)len)); ActionTags(MT_DrawRGBLine, Bitmap, x, y, x1, y1, rgb.Red, rgb.Green, rgb.Blue); ActionTags(MT_DrawRGBLine, Bitmap, x, y, x2, y2, rgb.Red, rgb.Green, rgb.Blue); if (level < 2) { ActionTags(MT_DrawRGBLine, Bitmap, x+1, y, x1+1, y1, rgb.Red, rgb.Green, rgb.Blue); ActionTags(MT_DrawRGBLine, Bitmap, x+1, y, x2+1, y2, rgb.Red, rgb.Green, rgb.Blue); } rgb.Red = (rgb.Red + redspeed) % 255; rgb.Green = (rgb.Green + greenspeed) % 255; rgb.Blue = (rgb.Blue + bluespeed) % 255; len = (len * 900) / 1000; if (level < (9+Self->IterationLevel)) { DrawTree(Self, Bitmap, x1, y1, len, a1, as, rgb, level + 1); DrawTree(Self, Bitmap, x2, y2, len, a2, as, rgb, level + 1); } } /***************************************************************************** ###FIELD### Name: Conformity Short: Defines the level of conformity used to generate each tree. Type: LONG Status: Read/Write ###DESCRIPTION### <p>Changing this field allows you to alter the conformity of the generated trees. Low values will generate many trees that conform to a similar shape, while high values will generate more variants. The field cannot be set to a value greater than 30.</p> ###END### *****************************************************************************/ static ERROR SET_Conformity(struct Forest *Self, LONG *Value) { if (*Value < 0) Self->Conformity = 0; else if (*Value > 30) Self->Conformity = 30; else Self->Conformity = *Value; return(ERR_Okay); } /***************************************************************************** ###FIELD### Name: IterationLevel Short: Determines the amount of branches generated for each tree. Type: LONG Status: Read/Write ###DESCRIPTION### <p>The iteration level defines how many 'branches' each tree will have. The higher the value, the more the fractals will look like genuine trees. The minimum value is 0 and the maximum value is 5. Note that the processor time required to draw each tree will increase when more branches need to be drawn.</p> ###END### *****************************************************************************/ static ERROR SET_IterationLevel(struct Forest *Self, LONG *Value) { if (*Value < 0) Self->IterationLevel = 0; else if (*Value > 5) Self->IterationLevel = 5; else Self->IterationLevel = *Value; return(ERR_Okay); } /***************************************************************************** ###FIELD### Name: MaxElements Short: The total number of trees drawn by the generator is defined by this field. Type: LONG Status: Read/Init ###DESCRIPTION### <p>You may specify the exact amount of trees to be drawn by the fractal generator by setting the MaxElements field. It is recommended that you use a setting between 50 and 100 for a reasonable effect.</p> ###END### *****************************************************************************/ static ERROR SET_MaxElements(struct Forest *Self, LONG *Value) { if (*Value > 100) Self->MaxElements = 100; else if (*Value < 1) Self->MaxElements = 1; else Self->MaxElements = *Value; return(ERR_Okay); } /***************************************************************************** ###FIELD### Name: MinHeight Short: The minimum height allowed for each tree. Type: LONG Status: Read/Write ###DESCRIPTION### <p>The minimum height of each tree is defined in this field. The height is calculated proportionatly from this value, so the setting is not pixel relative. The minimum possible height is 3, and the value cannot be greater than that of the MaxHeight field.</p> ###SEE ALSO### Field: MaxHeight ###END### *****************************************************************************/ static ERROR SET_MinHeight(struct Forest *Self, LONG *Value) { if (*Value < 5) Self->MinHeight = 5; else if (*Value >= Self->MaxHeight) Self->MinHeight = Self->MaxHeight - 3; else Self->MinHeight = *Value; return(ERR_Okay); } /***************************************************************************** ###FIELD### Name: MaxHeight Short: The maximum height allowed for each tree. Type: LONG Status: Read/Write ###DESCRIPTION### <p>The maximum height of each tree is defined in this field. The height is calculated proportionatly from this value, so the setting is not pixel relative. The maximum possible height is 100, and the value cannot be less than that of the MinHeight field.</p> ###SEE ALSO### Field: MinHeight ###END### *****************************************************************************/ static ERROR SET_MaxHeight(struct Forest *Self, LONG *Value) { if (*Value > 100) Self->MaxHeight = 100; else if (*Value < Self->MinHeight) Self->MaxHeight = Self->MinHeight + 3; else Self->MaxHeight = *Value; return(ERR_Okay); } /***************************************************************************** ###FIELD### Name: MinAngle Short: Defines the minimum possible angle in each branch' differential. Type: LONG Status: Read/Write ###DESCRIPTION### <p>Manipulating the MinAngle and MaxAngle fields allows you to control the breadth of the trees. Setting low MinAngle and MaxAngle values will create thin-looking trees, higher values will create broader looking trees.</p> <p>The MaxAngle field cannot be set to a number greater than 50 and the MinAngle cannot be set to a value less than three.</p> ###SEE ALSO### Field: MaxAngle ###END### *****************************************************************************/ static ERROR SET_MinAngle(struct Forest *Self, LONG *Value) { if (*Value < 3) Self->MinAngle = 3; else if (*Value >= Self->MaxAngle) Self->MinAngle = Self->MaxAngle - 3; else Self->MinAngle = *Value; return(ERR_Okay); } /***************************************************************************** ###FIELD### Name: MaxAngle Short: Defines the maximum possible angle in each branch' differential. Type: LONG Status: Read/Write ###DESCRIPTION### <p>Manipulating the MinAngle and MaxAngle fields allows you to control the breadth of the trees. Setting low MinAngle and MaxAngle values will create thin-looking trees, higher values will create broader looking trees.</p> <p>The MaxAngle field cannot be set to a number greater than 50 and the MinAngle cannot be set to a value less than three.</p> ###SEE ALSO### Field: MinAngle ###END### *****************************************************************************/ static ERROR SET_MaxAngle(struct Forest *Self, LONG *Value) { if (*Value < Self->MinAngle) Self->MaxAngle = Self->MinAngle + 3; else if (*Value > 50) Self->MaxAngle = 50; else Self->MaxAngle = *Value; return(ERR_Okay); }