1/*
2 * Copyright 2010-2011, 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#include "Leaves.h"
10
11#include <stdlib.h>
12#include <time.h>
13
14#include <AffineTransform.h>
15#include <Catalog.h>
16#include <GradientLinear.h>
17#include <Shape.h>
18#include <Slider.h>
19#include <TextView.h>
20
21
22#undef B_TRANSLATION_CONTEXT
23#define B_TRANSLATION_CONTEXT "Leaves"
24
25
26// path data for the leaf shape
27static const BPoint kLeafBegin(56.24793f, 15.46287f);
28static BPoint kLeafCurves[][3] = {
29	{ BPoint(61.14, 28.89), BPoint(69.78, 38.25), BPoint(83.48, 44.17) },
30	{ BPoint(99.46, 37.52), BPoint(113.27, 29.61), BPoint(134.91, 30.86) },
31	{ BPoint(130.58, 36.53), BPoint(126.74, 42.44), BPoint(123.84, 48.81) },
32	{ BPoint(131.81, 42.22), BPoint(137.53, 38.33), BPoint(144.37, 33.10) },
33	{ BPoint(169.17, 23.55), BPoint(198.90, 15.55), BPoint(232.05, 10.51) },
34	{ BPoint(225.49, 18.37), BPoint(219.31, 28.17), BPoint(217.41, 40.24) },
35	{ BPoint(227.70, 26.60), BPoint(239.97, 14.63), BPoint(251.43, 8.36) },
36	{ BPoint(288.89, 9.12), BPoint(322.73, 14.33), BPoint(346.69, 31.67) },
37	{ BPoint(330.49, 37.85), BPoint(314.36, 44.25), BPoint(299.55, 54.17) },
38	{ BPoint(292.48, 52.54), BPoint(289.31, 49.70), BPoint(285.62, 47.03) },
39	{ BPoint(283.73, 54.61), BPoint(284.46, 57.94), BPoint(285.62, 60.60) },
40	{ BPoint(259.78, 76.14), BPoint(233.24, 90.54), BPoint(202.41, 98.10) },
41	{ BPoint(194.43, 95.36), BPoint(185.96, 92.39), BPoint(179.63, 88.33) },
42	{ BPoint(180.15, 94.75), BPoint(182.73, 99.76), BPoint(185.62, 104.53) },
43	{ BPoint(154.83, 119.46), BPoint(133.21, 118.97), BPoint(125.62, 94.88) },
44	{ BPoint(124.70, 98.79), BPoint(124.11, 103.67), BPoint(124.19, 110.60) },
45	{ BPoint(116.42, 111.81), BPoint(85.82, 99.60), BPoint(83.25, 51.96) },
46	{ BPoint(62.50, 42.57), BPoint(58.12, 33.18), BPoint(50.98, 23.81) } };
47static const int kLeafCurveCount = sizeof(kLeafCurves) / sizeof(kLeafCurves[0]);
48static const float kLeafWidth = 372.f;
49static const float kLeafHeight = 121.f;
50
51// leaf colors
52static const rgb_color kColors[][2] = {
53	{ {255, 114, 0, 255}, {255, 159, 0, 255} },
54	{ {50, 160, 40, 255}, {125, 210, 32, 255} },
55	{ {250, 190, 30, 255}, {255, 214, 125, 255} } };
56static const int kColorCount = sizeof(kColors) / sizeof(kColors[0]);
57
58// bounds for settings
59static const int kMinimumDropRate = 2, kMaximumDropRate = 50;
60static const int kMinimumLeafSize = 50, kMaximumLeafSize = 1000;
61static const int kMaximumSizeVariation = 200;
62
63enum {
64	MSG_SET_DROP_RATE		= 'drop',
65	MSG_SET_LEAF_SIZE		= 'size',
66	MSG_SET_SIZE_VARIATION	= 'svry',
67};
68
69
70extern "C" BScreenSaver*
71instantiate_screen_saver(BMessage* msg, image_id image)
72{
73	return new Leaves(msg, image);
74}
75
76
77Leaves::Leaves(BMessage* archive, image_id id)
78	:
79	BScreenSaver(archive, id),
80	fDropRateSlider(NULL),
81	fLeafSizeSlider(NULL),
82	fSizeVariationSlider(NULL),
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
98void
99Leaves::StartConfig(BView* view)
100{
101	BRect bounds = view->Bounds();
102	bounds.InsetBy(10, 10);
103	BRect frame(0, 0, bounds.Width(), 20);
104
105	fDropRateSlider = 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	fDropRateSlider->SetValue(fDropRate);
110	fDropRateSlider->ResizeToPreferred();
111	bounds.bottom -= fDropRateSlider->Bounds().Height() * 1.5;
112	fDropRateSlider->MoveTo(bounds.LeftBottom());
113	view->AddChild(fDropRateSlider);
114
115	fLeafSizeSlider = 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	fLeafSizeSlider->SetValue(fLeafSize);
120	fLeafSizeSlider->ResizeToPreferred();
121	bounds.bottom -= fLeafSizeSlider->Bounds().Height() * 1.5;
122	fLeafSizeSlider->MoveTo(bounds.LeftBottom());
123	view->AddChild(fLeafSizeSlider);
124
125	fSizeVariationSlider = 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	fSizeVariationSlider->SetValue(fSizeVariation);
130	fSizeVariationSlider->ResizeToPreferred();
131	bounds.bottom -= fSizeVariationSlider->Bounds().Height() * 1.5;
132	fSizeVariationSlider->MoveTo(bounds.LeftBottom());
133	view->AddChild(fSizeVariationSlider);
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	fDropRateSlider->SetTarget(this);
154	fLeafSizeSlider->SetTarget(this);
155	fSizeVariationSlider->SetTarget(this);
156}
157
158
159status_t
160Leaves::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
171status_t
172Leaves::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
185void
186Leaves::MessageReceived(BMessage* message)
187{
188	switch (message->what) {
189		case MSG_SET_DROP_RATE:
190			fDropRate = fDropRateSlider->Value();
191			break;
192		case MSG_SET_LEAF_SIZE:
193			fLeafSize = fLeafSizeSlider->Value();
194			break;
195		case MSG_SET_SIZE_VARIATION:
196			fSizeVariation = fSizeVariationSlider->Value();
197			break;
198		default:
199			BHandler::MessageReceived(message);
200	}
201}
202
203
204void
205Leaves::Draw(BView* view, int32 frame)
206{
207	float scale = fLeafSize / kLeafWidth / (kMaximumLeafSize * 2);
208	scale *= view->Bounds().Width();
209	scale += scale * drand48() * fSizeVariation / 100.;
210
211	BAffineTransform transform;
212	transform.TranslateBy(-kLeafWidth / 2, -kLeafHeight / 2);
213		// draw the leaf centered on the point
214	transform.RotateBy(drand48() * 2. * M_PI);
215	if ((rand() & 64) == 0) transform.ScaleBy(-1., 1.);
216		// flip half of the time
217	transform.ScaleBy(scale);
218	transform.TranslateBy(_RandomPoint(view->Bounds()));
219
220	BPoint center = transform.Apply(BPoint(kLeafWidth / 2, kLeafHeight / 2));
221	BPoint gradientOffset = BPoint(60 * scale, 80 * scale);
222	BGradientLinear gradient(center - gradientOffset, center + gradientOffset);
223	int color = (rand() / 7) % kColorCount;
224	gradient.AddColor(kColors[color][0], 0.f);
225	gradient.AddColor(kColors[color][1], 255.f);
226
227	BShape leafShape;
228	leafShape.MoveTo(transform.Apply(kLeafBegin));
229	for (int i = 0; i < kLeafCurveCount; ++i) {
230		BPoint control[3];
231		for (int j = 0; j < 3; ++j)
232			control[j] = transform.Apply(kLeafCurves[i][j]);
233		leafShape.BezierTo(control);
234	}
235	leafShape.Close();
236
237	view->PushState();
238	view->SetDrawingMode(B_OP_ALPHA);
239	view->SetHighColor(0, 0, 0, 50);
240	for (int i = 2; i >= 0; --i) {
241		view->SetOrigin(i * 0.1, i * 0.3);
242		view->SetPenSize(i * 2);
243		view->StrokeShape(&leafShape);
244	}
245	view->PopState();
246	view->FillShape(&leafShape, gradient);
247}
248
249
250inline BPoint
251Leaves::_RandomPoint(const BRect& bound)
252{
253	return BPoint(drand48() * (bound.right - bound.left) + bound.left,
254		drand48() * (bound.bottom - bound.top) + bound.top);
255}
256
257