xref: /haiku/src/add-ons/screen_savers/leaves/Leaves.cpp (revision c14bca2958fb7b0c34d5464ccfdd87038f909a0c)
1 /*
2  * Copyright 2010-2013, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT license.
4  *
5  * Authors:
6  *		Deyan Genovski, deangenovski@gmail.com
7  *		Geoffry Song, goffrie@gmail.com
8  */
9 
10 
11 #include "Leaves.h"
12 
13 #include <stdlib.h>
14 #include <time.h>
15 
16 #include <AffineTransform.h>
17 #include <Catalog.h>
18 #include <GradientLinear.h>
19 #include <Shape.h>
20 #include <Slider.h>
21 #include <TextView.h>
22 
23 
24 #undef B_TRANSLATION_CONTEXT
25 #define B_TRANSLATION_CONTEXT "Leaves"
26 
27 
28 // path data for the leaf shape
29 static const BPoint kLeafBegin(56.24793f, 15.46287f);
30 static BPoint kLeafCurves[][3] = {
31 	{ BPoint(61.14, 28.89), BPoint(69.78, 38.25), BPoint(83.48, 44.17) },
32 	{ BPoint(99.46, 37.52), BPoint(113.27, 29.61), BPoint(134.91, 30.86) },
33 	{ BPoint(130.58, 36.53), BPoint(126.74, 42.44), BPoint(123.84, 48.81) },
34 	{ BPoint(131.81, 42.22), BPoint(137.53, 38.33), BPoint(144.37, 33.10) },
35 	{ BPoint(169.17, 23.55), BPoint(198.90, 15.55), BPoint(232.05, 10.51) },
36 	{ BPoint(225.49, 18.37), BPoint(219.31, 28.17), BPoint(217.41, 40.24) },
37 	{ BPoint(227.70, 26.60), BPoint(239.97, 14.63), BPoint(251.43, 8.36) },
38 	{ BPoint(288.89, 9.12), BPoint(322.73, 14.33), BPoint(346.69, 31.67) },
39 	{ BPoint(330.49, 37.85), BPoint(314.36, 44.25), BPoint(299.55, 54.17) },
40 	{ BPoint(292.48, 52.54), BPoint(289.31, 49.70), BPoint(285.62, 47.03) },
41 	{ BPoint(283.73, 54.61), BPoint(284.46, 57.94), BPoint(285.62, 60.60) },
42 	{ BPoint(259.78, 76.14), BPoint(233.24, 90.54), BPoint(202.41, 98.10) },
43 	{ BPoint(194.43, 95.36), BPoint(185.96, 92.39), BPoint(179.63, 88.33) },
44 	{ BPoint(180.15, 94.75), BPoint(182.73, 99.76), BPoint(185.62, 104.53) },
45 	{ BPoint(154.83, 119.46), BPoint(133.21, 118.97), BPoint(125.62, 94.88) },
46 	{ BPoint(124.70, 98.79), BPoint(124.11, 103.67), BPoint(124.19, 110.60) },
47 	{ BPoint(116.42, 111.81), BPoint(85.82, 99.60), BPoint(83.25, 51.96) },
48 	{ BPoint(62.50, 42.57), BPoint(58.12, 33.18), BPoint(50.98, 23.81) } };
49 static const int kLeafCurveCount = sizeof(kLeafCurves) / sizeof(kLeafCurves[0]);
50 static const float kLeafWidth = 372.f;
51 static const float kLeafHeight = 121.f;
52 
53 // leaf colors
54 static const rgb_color kColors[][2] = {
55 	{ {255, 114, 0, 255}, {255, 159, 0, 255} },
56 	{ {50, 160, 40, 255}, {125, 210, 32, 255} },
57 	{ {250, 190, 30, 255}, {255, 214, 125, 255} } };
58 static const int kColorCount = sizeof(kColors) / sizeof(kColors[0]);
59 
60 // bounds for settings
61 static const int kMinimumDropRate = 2, kMaximumDropRate = 50;
62 static const int kMinimumLeafSize = 50, kMaximumLeafSize = 1000;
63 static const int kMaximumSizeVariation = 200;
64 
65 // message constants
66 enum {
67 	MSG_SET_DROP_RATE		= 'drop',
68 	MSG_SET_LEAF_SIZE		= 'size',
69 	MSG_SET_SIZE_VARIATION	= 'svry',
70 };
71 
72 
73 extern "C" BScreenSaver*
instantiate_screen_saver(BMessage * msg,image_id image)74 instantiate_screen_saver(BMessage* msg, image_id image)
75 {
76 	return new Leaves(msg, image);
77 }
78 
79 
Leaves(BMessage * archive,image_id id)80 Leaves::Leaves(BMessage* archive, image_id id)
81 	:
82 	BScreenSaver(archive, id),
83 	fDropRate(10),
84 	fLeafSize(150),
85 	fSizeVariation(50)
86 {
87 	if (archive) {
88 		if (archive->FindInt32("Leaves drop rate", &fDropRate) != B_OK)
89 			fDropRate = 10;
90 		if (archive->FindInt32("Leaves size", &fLeafSize) != B_OK)
91 			fLeafSize = 150;
92 		if (archive->FindInt32("Leaves variation", &fSizeVariation) != B_OK)
93 			fSizeVariation = 50;
94 	}
95 }
96 
97 
98 void
StartConfig(BView * view)99 Leaves::StartConfig(BView* view)
100 {
101 	BRect bounds = view->Bounds();
102 	bounds.InsetBy(10, 10);
103 	BRect frame(0, 0, bounds.Width(), 20);
104 
105 	BSlider* dropRateSlider = new BSlider(frame, "drop rate",
106 		B_TRANSLATE("Drop rate:"), new BMessage(MSG_SET_DROP_RATE),
107 		kMinimumDropRate, kMaximumDropRate,	B_BLOCK_THUMB,
108 		B_FOLLOW_LEFT_RIGHT | B_FOLLOW_BOTTOM);
109 	dropRateSlider->SetValue(fDropRate);
110 	dropRateSlider->ResizeToPreferred();
111 	bounds.bottom -= dropRateSlider->Bounds().Height() * 1.5;
112 	dropRateSlider->MoveTo(bounds.LeftBottom());
113 	view->AddChild(dropRateSlider);
114 
115 	BSlider* leafSizeSlider = new BSlider(frame, "leaf size",
116 		B_TRANSLATE("Leaf size:"), new BMessage(MSG_SET_LEAF_SIZE),
117 		kMinimumLeafSize, kMaximumLeafSize,	B_BLOCK_THUMB,
118 		B_FOLLOW_LEFT_RIGHT | B_FOLLOW_BOTTOM);
119 	leafSizeSlider->SetValue(fLeafSize);
120 	leafSizeSlider->ResizeToPreferred();
121 	bounds.bottom -= leafSizeSlider->Bounds().Height() * 1.5;
122 	leafSizeSlider->MoveTo(bounds.LeftBottom());
123 	view->AddChild(leafSizeSlider);
124 
125 	BSlider* sizeVariationSlider = new BSlider(frame, "variation",
126 		B_TRANSLATE("Size variation:"),	new BMessage(MSG_SET_SIZE_VARIATION),
127 		0, kMaximumSizeVariation, B_BLOCK_THUMB,
128 		B_FOLLOW_LEFT_RIGHT | B_FOLLOW_BOTTOM);
129 	sizeVariationSlider->SetValue(fSizeVariation);
130 	sizeVariationSlider->ResizeToPreferred();
131 	bounds.bottom -= sizeVariationSlider->Bounds().Height() * 1.5;
132 	sizeVariationSlider->MoveTo(bounds.LeftBottom());
133 	view->AddChild(sizeVariationSlider);
134 
135 	BTextView* textView = new BTextView(bounds, B_EMPTY_STRING,
136 		bounds.OffsetToCopy(0., 0.), B_FOLLOW_ALL, B_WILL_DRAW);
137 	textView->SetViewColor(view->ViewColor());
138 	BString name = B_TRANSLATE("Leaves");
139 	BString text = name;
140 	text << "\n\n";
141 	text << B_TRANSLATE("by Deyan Genovski, Geoffry Song");
142 	text << "\n\n";
143 
144 	textView->Insert(text.String());
145 	textView->SetStylable(true);
146 	textView->SetFontAndColor(0, name.Length(), be_bold_font);
147 	textView->MakeEditable(false);
148 	view->AddChild(textView);
149 
150 	BWindow* window = view->Window();
151 	if (window) window->AddHandler(this);
152 
153 	dropRateSlider->SetTarget(this);
154 	leafSizeSlider->SetTarget(this);
155 	sizeVariationSlider->SetTarget(this);
156 }
157 
158 
159 status_t
StartSaver(BView * view,bool preview)160 Leaves::StartSaver(BView* view, bool preview)
161 {
162 	SetTickSize(10000000 / fDropRate);
163 	srand48(time(NULL));
164 
165 	view->SetLineMode(B_ROUND_CAP, B_ROUND_JOIN);
166 
167 	return B_OK;
168 }
169 
170 
171 status_t
SaveState(BMessage * into) const172 Leaves::SaveState(BMessage* into) const
173 {
174 	status_t status;
175 	if ((status = into->AddInt32("Leaves drop rate", fDropRate)) != B_OK)
176 		return status;
177 	if ((status = into->AddInt32("Leaves size", fLeafSize)) != B_OK)
178 		return status;
179 	if ((status = into->AddInt32("Leaves variation", fSizeVariation)) != B_OK)
180 		return status;
181 	return B_OK;
182 }
183 
184 
185 void
MessageReceived(BMessage * message)186 Leaves::MessageReceived(BMessage* message)
187 {
188 	switch (message->what) {
189 		case MSG_SET_DROP_RATE:
190 		{
191 			int32 dropRate;
192 			if (message->FindInt32("be:value", &dropRate) == B_OK)
193 				fDropRate = dropRate;
194 			break;
195 		}
196 
197 		case MSG_SET_LEAF_SIZE:
198 		{
199 			int32 leafSize;
200 			if (message->FindInt32("be:value", &leafSize) == B_OK)
201 				fLeafSize = leafSize;
202 			break;
203 		}
204 
205 		case MSG_SET_SIZE_VARIATION:
206 		{
207 			int32 sizeVariation;
208 			if (message->FindInt32("be:value", &sizeVariation) == B_OK)
209 				fSizeVariation = sizeVariation;
210 			break;
211 		}
212 
213 		default:
214 			BHandler::MessageReceived(message);
215 	}
216 }
217 
218 
219 void
Draw(BView * view,int32 frame)220 Leaves::Draw(BView* view, int32 frame)
221 {
222 	float scale = fLeafSize / kLeafWidth / (kMaximumLeafSize * 2);
223 	scale *= view->Bounds().Width();
224 	scale += scale * drand48() * fSizeVariation / 100.;
225 
226 	BAffineTransform transform;
227 	transform.TranslateBy(-kLeafWidth / 2, -kLeafHeight / 2);
228 		// draw the leaf centered on the point
229 	transform.RotateBy(drand48() * 2. * M_PI);
230 	if ((rand() & 64) == 0) transform.ScaleBy(-1., 1.);
231 		// flip half of the time
232 	transform.ScaleBy(scale);
233 	transform.TranslateBy(_RandomPoint(view->Bounds()));
234 
235 	BPoint center = transform.Apply(BPoint(kLeafWidth / 2, kLeafHeight / 2));
236 	BPoint gradientOffset = BPoint(60 * scale, 80 * scale);
237 	BGradientLinear gradient(center - gradientOffset, center + gradientOffset);
238 	int color = (rand() / 7) % kColorCount;
239 	gradient.AddColor(kColors[color][0], 0.f);
240 	gradient.AddColor(kColors[color][1], 255.f);
241 
242 	BShape leafShape;
243 	leafShape.MoveTo(transform.Apply(kLeafBegin));
244 	for (int i = 0; i < kLeafCurveCount; ++i) {
245 		BPoint control[3];
246 		for (int j = 0; j < 3; ++j)
247 			control[j] = transform.Apply(kLeafCurves[i][j]);
248 		leafShape.BezierTo(control);
249 	}
250 	leafShape.Close();
251 
252 	view->PushState();
253 	view->SetDrawingMode(B_OP_ALPHA);
254 	view->SetHighColor(0, 0, 0, 50);
255 	for (int i = 2; i >= 0; --i) {
256 		view->SetOrigin(i * 0.1, i * 0.3);
257 		view->SetPenSize(i * 2);
258 		view->StrokeShape(&leafShape);
259 	}
260 	view->PopState();
261 	view->FillShape(&leafShape, gradient);
262 }
263 
264 
265 inline BPoint
_RandomPoint(const BRect & bound)266 Leaves::_RandomPoint(const BRect& bound)
267 {
268 	return BPoint(drand48() * (bound.right - bound.left) + bound.left,
269 		drand48() * (bound.bottom - bound.top) + bound.top);
270 }
271