1/*
2 * Copyright 2010-2011, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Geoffry Song, goffrie@gmail.com
7 *		Ryan Leavengood, leavengood@gmail.com
8 */
9
10
11#include "Butterfly.h"
12
13#include <math.h>
14#include <stdlib.h>
15
16#include <Catalog.h>
17#include <DefaultSettingsView.h>
18#include <OS.h>
19#include <View.h>
20
21#undef B_TRANSLATION_CONTEXT
22#define B_TRANSLATION_CONTEXT "Screensaver Butterfly"
23
24
25extern "C" BScreenSaver*
26instantiate_screen_saver(BMessage* archive, image_id imageId)
27{
28	return new Butterfly(archive, imageId);
29}
30
31
32// #pragma mark -
33
34
35Butterfly::Butterfly(BMessage* archive, image_id imageId)
36	:
37	BScreenSaver(archive, imageId)
38{
39}
40
41
42void
43Butterfly::StartConfig(BView* view)
44{
45	BPrivate::BuildDefaultSettingsView(view, "Butterfly",
46		B_TRANSLATE("by Geoffry Song"));
47}
48
49
50status_t
51Butterfly::StartSaver(BView* view, bool preview)
52{
53	view->SetLowColor(0, 0, 0);
54	view->FillRect(view->Bounds(), B_SOLID_LOW);
55	view->SetDrawingMode(B_OP_ALPHA);
56	view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY);
57	view->SetLineMode(B_ROUND_CAP, B_ROUND_JOIN);
58	if (!preview)
59		view->SetPenSize(2.0);
60
61	SetTickSize(20000);
62
63	// Set fBase to a random radian value scaled by 1000. The scaling produces
64	// more interesting results.
65	srand48(system_time());
66	fBase = drand48() * 2 * M_PI * 1000;
67
68	// calculate transformation
69	BRect bounds = view->Bounds();
70	fScale = MIN(bounds.Width(), bounds.Height()) * 0.1f;
71	fTrans.Set(bounds.Width() * 0.5f, bounds.Height() * 0.5f);
72	fBounds = bounds;
73
74	fLast[0] = _Iterate();
75	fLast[1] = _Iterate();
76	fLast[2] = _Iterate();
77
78	return B_OK;
79}
80
81
82void
83Butterfly::Draw(BView* view, int32 frame)
84{
85	if (frame == 1024) {
86		// calculate bounding box ( (-5.92,-5.92) to (5.92, 5.92) )
87		fBounds.Set(-5.92f * fScale + fTrans.x, -5.92f * fScale + fTrans.y,
88			5.92f * fScale + fTrans.x, 5.92f * fScale + fTrans.y);
89	}
90	if ((frame & 3) == 0) {
91		// fade out
92		view->SetHighColor(0, 0, 0, 4);
93		view->FillRect(fBounds);
94	}
95	// create a color from a hue of (fBase * 15) degrees
96	view->SetHighColor(_HueToColor(fBase * 15));
97	BPoint p = _Iterate();
98
99	// cubic Hermite interpolation from fLast[1] to fLast[2]
100	// calculate tangents for a Catmull-Rom spline
101	//(these values need to be halved)
102	BPoint m1 = fLast[2] - fLast[0]; // tangent for fLast[1]
103	BPoint m2 = p - fLast[1]; // tangent for fLast[2]
104
105	// draw Bezier from fLast[1] to fLast[2] with control points
106	// fLast[1] + m1/3, fLast[2] - m2/3
107	m1.x /= 6;
108	m1.y /= 6;
109	m2.x /= 6;
110	m2.y /= 6;
111	BPoint control[4] = { fLast[1], fLast[1] + m1, fLast[2] - m2, fLast[2] };
112	view->StrokeBezier(control);
113
114	fLast[0] = fLast[1];
115	fLast[1] = fLast[2];
116	fLast[2] = p;
117}
118
119
120//! Convert from a hue in degrees to a fully saturated color
121inline rgb_color
122Butterfly::_HueToColor(float hue)
123{
124	// convert from [0..360) to [0..1530)
125	int h = static_cast<int>(fmodf(hue, 360) * 4.25f);
126	int x = 255 - abs(h % 510 - 255);
127
128	rgb_color result = {0, 0, 0, 255};
129	if (h < 255) {
130		result.red = 255;
131		result.green = x;
132	} else if (h < 510) {
133		result.red = x;
134		result.green = 255;
135	} else if (h < 765) {
136		result.green = 255;
137		result.blue = x;
138	} else if (h < 1020) {
139		result.green = x;
140		result.blue = 255;
141	} else if (h < 1275) {
142		result.red = x;
143		result.blue = 255;
144	} else {
145		result.red = 255;
146		result.blue = x;
147	}
148	return result;
149}
150
151
152inline BPoint
153Butterfly::_Iterate()
154{
155	float r = powf(M_E, cosf(fBase)) - 2 * cosf(4 * fBase)
156		- powf(sinf(fBase / 12), 5);
157	// rotate and move it a bit
158	BPoint p(sinf(fBase * 1.01f) * r + cosf(fBase * 1.02f) * 0.2f,
159		cosf(fBase * 1.01f) * r + sinf(fBase * 1.02f) * 0.2f);
160	// transform to view coordinates
161	p.x *= fScale;
162	p.y *= fScale;
163	p += fTrans;
164	// move on
165	fBase += 0.05f;
166	return p;
167}
168