Множество Мандельброта

Мно́жество Мандельбро́та — это множество таких точек c на комплексной плоскости, для которых рекуррентное соотношение при задаёт ограниченную последовательность. То есть, это множество таких c, для которых существует такое действительное R, что неравенство |zn|<R выполняется при всех натуральных n.
Построение множества
[править]Несложно доказать, что как только модуль zn окажется больше 2 (или, в терминах действительной и мнимой частей, xn2+yn2>4), последовательность станет стремиться к бесконечности. В случае |c|≤2 это можно доказать с помощью метода математической индукции. При |c|>2 точка c заведомо не принадлежит множеству Мандельброта, что также можно вывести методом индукции, используя равенство z0=0. (Хотя в этом случае может существовать другое z0, для которого соответствующая последовательность ограничена по модулю, но для некоторого n выполняется неравенство |zn|>2.)
Сравнение |zn| с этим числом (в англоязычной литературе его называют «bail-out») позволяет выделять точки, не попадающие внутрь множества. Для точек, лежащих внутри множества, последовательность не будет иметь тенденции к бесконечности и никогда не достигнет этого числа, поэтому после определённого числа итераций расчёт необходимо принудительно завершить. Максимальное число итераций, после которых число считается попавшим внутрь множества, задается в программе.
Изображение, полученное таким способом, является лишь приближением к реальному множеству Мандельброта. Более качественные результаты можно получать, увеличивая максимальное количество итераций, однако при этом пропорционально вырастает и время расчётов.
Примеры программ
[править]Консольный рендерер Мандельброта с методом возмущений и предела 10−308 (на языке программирования C++)
[править]Ключевые особенности
- Расчёт опорной траектории на 1000 бит всего один раз.
- Реактивный расчёт миллиарда пикселей на аппаратном double.
- Расчет можно выполнить существенно быстрее, если использовать билинейную аппроксимацию.
- При использовании чисел с плавающей запятой двойной точности (порядка 10⁻¹⁵) теория возмущений позволяет приблизиться к уровню 10⁻³⁰⁸ - не дальше.
- Революционный алгоритм Reference Reset to Zero.
- Настоящий SSAA 8x8 для идеально сглаженного изображения без алиасинга.
- Параллелизм OpenMP для высокоскоростного многопоточного рендеринга.
#include <atomic>
#include <cmath>
#include <cstdint>
#include <cstdio>
#include <vector>
#include <algorithm>
#include <mpfr.h>
#include <omp.h>
using std::vector;
using std::min;
const char * CENTER_RE = "-1.99999543561201124623198345433951143502785679245726844745821388800402678499411681518036306219179273434395557574279985918047221291197081186140687781560831995";
const char * CENTER_IM = "-0.00000000000000000000000026198152173811047783694060060607013913873144250985383083459221663448338433592617272786772587281530484110756597337683912309313885172";
const char * VIEW_SIZE = "1.15e-119";
const int WIDTH = 2160;
const int HEIGHT = 2160;
const int AA = 8;
const int MAX_ITER = 50000;
const double ESCAPE_RADIUS_SQUARED = 50000.0;
const int PALETTE_FRAME = 200;
const char * OUTPUT_FILE = "Mandelbrot Set Image 112.bmp";
const mpfr_prec_t PRECISION_BITS = 1000;
const int REF_SIZE = MAX_ITER + 200;
struct Complex {
double re;
double im;
};
#pragma pack(push, 1)
struct BMPHeader {
uint16_t type{0x4D42};
uint32_t size{0};
uint32_t reserved{0};
uint32_t offBits{54};
uint32_t structSize{40};
int32_t width{0};
int32_t height{0};
uint16_t planes{1};
uint16_t bitCount{24};
uint32_t compression{0};
uint32_t sizeImage{0};
int32_t xPixelsPerMeter{2834};
int32_t yPixelsPerMeter{2834};
uint32_t colorsUsed{0};
uint32_t colorsImportant{0};
};
#pragma pack(pop)
int main() {
const double startTime = omp_get_wtime();
const long rawWidth = static_cast<long>(WIDTH) * AA;
const long rawHeight = static_cast<long>(HEIGHT) * AA;
mpfr_t centerRe, centerIm, zReMp, zImMp, tmp1, tmp2, viewSizeMp;
mpfr_inits2(PRECISION_BITS, centerRe, centerIm, zReMp, zImMp, tmp1, tmp2, viewSizeMp, static_cast<mpfr_ptr>(nullptr));
mpfr_set_str(centerRe, CENTER_RE, 10, MPFR_RNDN);
mpfr_set_str(centerIm, CENTER_IM, 10, MPFR_RNDN);
mpfr_set_str(viewSizeMp, VIEW_SIZE, 10, MPFR_RNDN);
const double sampleStep = mpfr_get_d(viewSizeMp, MPFR_RNDN) / rawWidth;
vector<Complex> referenceOrbit;
referenceOrbit.reserve(REF_SIZE);
mpfr_set_ui(zReMp, 0, MPFR_RNDN);
mpfr_set_ui(zImMp, 0, MPFR_RNDN);
for (int iter = 0; iter < REF_SIZE - 1; ++iter) {
Complex z{mpfr_get_d(zReMp, MPFR_RNDN), mpfr_get_d(zImMp, MPFR_RNDN)};
referenceOrbit.push_back(z);
if (z.re * z.re + z.im * z.im > ESCAPE_RADIUS_SQUARED) {
break;
}
mpfr_mul(tmp1, zReMp, zImMp, MPFR_RNDN);
mpfr_sqr(tmp2, zReMp, MPFR_RNDN);
mpfr_sqr(zReMp, zImMp, MPFR_RNDN);
mpfr_sub(zReMp, tmp2, zReMp, MPFR_RNDN);
mpfr_add(zReMp, zReMp, centerRe, MPFR_RNDN);
mpfr_mul_2ui(tmp1, tmp1, 1, MPFR_RNDN);
mpfr_add(zImMp, tmp1, centerIm, MPFR_RNDN);
}
const int referenceLength = static_cast<int>(referenceOrbit.size());
mpfr_clears(centerRe, centerIm, zReMp, zImMp, tmp1, tmp2, viewSizeMp, static_cast<mpfr_ptr>(nullptr));
std::fprintf(stderr, "Reference orbit: %d points\n", referenceLength);
std::fprintf(stderr, "Precomputing skip100 matrices...\n");
vector<Complex> coeff_A(REF_SIZE, {1.0, 0.0});
vector<Complex> coeff_B(REF_SIZE, {0.0, 0.0});
vector<double> rad_R(REF_SIZE, 2.0);
vector<double> aS_squared(REF_SIZE, 0.0);
for (int i = 0; i < referenceLength; ++i) {
double r2 = referenceOrbit[i].re * referenceOrbit[i].re + referenceOrbit[i].im * referenceOrbit[i].im;
aS_squared[i] = (r2 < ESCAPE_RADIUS_SQUARED) ? r2 : 0.0;
}
const int loop_limit = min(static_cast<int>(MAX_ITER), referenceLength - 105);
#pragma omp parallel for
for (int i = 0; i < loop_limit; ++i) {
double min_r2 = ESCAPE_RADIUS_SQUARED;
for (int k = 0; k < 100; ++k) {
if (i + k >= referenceLength) break;
if (aS_squared[i + k] < min_r2) min_r2 = aS_squared[i + k];
}
rad_R[i] = std::sqrt(min_r2);
for (int k = 0; k < 100; ++k) {
if (i + k >= referenceLength) break;
double r_re = referenceOrbit[i + k].re;
double r_im = referenceOrbit[i + k].im;
double next_A_re = 2.0 * (r_re * coeff_A[i].re - r_im * coeff_A[i].im);
double next_A_im = 2.0 * (r_re * coeff_A[i].im + r_im * coeff_A[i].re);
double next_B_re = 2.0 * (r_re * coeff_B[i].re - r_im * coeff_B[i].im) + 1.0;
double next_B_im = 2.0 * (r_re * coeff_B[i].im + r_im * coeff_B[i].re);
coeff_A[i].re = next_A_re; coeff_A[i].im = next_A_im;
coeff_B[i].re = next_B_re; coeff_B[i].im = next_B_im;
}
}
const double PI = 3.14159265358979323846;
uint8_t palette[256][3];
for (int i = 0; i < 255; ++i) {
palette[i][0] = static_cast<uint8_t>(std::lround(127.0 + 127.0 * std::cos(2.0 * PI * i / 255.0)));
palette[i][1] = static_cast<uint8_t>(std::lround(127.0 + 127.0 * std::sin(2.0 * PI * i / 255.0)));
palette[i][2] = palette[i][1];
}
palette[255][0] = 255; palette[255][1] = 255; palette[255][2] = 255;
const int rowBytes = (WIDTH * 3 + 3) & ~3;
vector<uint8_t> image(static_cast<size_t>(rowBytes) * HEIGHT, 0);
std::atomic<int> completedRows{0};
const Complex * reference = referenceOrbit.data();
#pragma omp parallel for schedule(dynamic)
for (int y = 0; y < HEIGHT; ++y) {
uint8_t * row = image.data() + static_cast<size_t>(y) * rowBytes;
for (int x = 0; x < WIDTH; ++x) {
unsigned blueSum = 0; unsigned greenSum = 0; unsigned redSum = 0;
for (int sampleY = 0; sampleY < AA; ++sampleY) {
const double deltaCIm = (static_cast<long>(y) * AA + sampleY - rawHeight / 2) * sampleStep;
for (int sampleX = 0; sampleX < AA; ++sampleX) {
const double deltaCRe = (static_cast<long>(x) * AA + sampleX - rawWidth / 2) * sampleStep;
double deltaRe = 0.0; double deltaIm = 0.0;
double zRe = 0.0; double zIm = 0.0;
int referenceIndex = 0;
int iter = 0;
while (iter < MAX_ITER) {
if (zRe * zRe + zIm * zIm >= ESCAPE_RADIUS_SQUARED) {
break;
}
double eps_abs2 = deltaRe * deltaRe + deltaIm * deltaIm;
double limit_r2 = 1e-60 * rad_R[referenceIndex] * rad_R[referenceIndex];
if (eps_abs2 < limit_r2 && (referenceIndex + 100 < loop_limit) && (iter + 100 < MAX_ITER)) {
double backup_deltaRe = deltaRe; double backup_deltaIm = deltaIm;
int backup_refIdx = referenceIndex; int backup_iter = iter;
double next_eps_re = (coeff_A[referenceIndex].re * deltaRe - coeff_A[referenceIndex].im * deltaIm) +
(coeff_B[referenceIndex].re * deltaCRe - coeff_B[referenceIndex].im * deltaCIm);
double next_eps_im = (coeff_A[referenceIndex].re * deltaIm + coeff_A[referenceIndex].im * deltaRe) +
(coeff_B[referenceIndex].re * deltaCIm + coeff_B[referenceIndex].im * deltaCRe);
deltaRe = next_eps_re; deltaIm = next_eps_im;
referenceIndex += 100; iter += 100;
zRe = reference[referenceIndex].re + deltaRe;
zIm = reference[referenceIndex].im + deltaIm;
if (zRe * zRe + zIm * zIm >= ESCAPE_RADIUS_SQUARED) {
deltaRe = backup_deltaRe; deltaIm = backup_deltaIm;
referenceIndex = backup_refIdx; iter = backup_iter;
} else {
continue;
}
}
const double a = 2.0 * reference[referenceIndex].re + deltaRe;
const double b = 2.0 * reference[referenceIndex].im + deltaIm;
const double nextDeltaRe = a * deltaRe - b * deltaIm + deltaCRe;
deltaIm = a * deltaIm + b * deltaRe + deltaCIm;
deltaRe = nextDeltaRe;
++referenceIndex; ++iter;
zRe = reference[referenceIndex].re + deltaRe;
zIm = reference[referenceIndex].im + deltaIm;
if (zRe * zRe + zIm * zIm < deltaRe * deltaRe + deltaIm * deltaIm || referenceIndex >= loop_limit) {
deltaRe = zRe; deltaIm = zIm; referenceIndex = 0;
}
}
const int remaining = MAX_ITER - iter;
const uint8_t colorIndex = (remaining == 0) ? 255 : static_cast<uint8_t>(remaining % 254);
const int paletteIndex = (colorIndex == 255) ? 255 : (colorIndex - PALETTE_FRAME + 255) % 255;
blueSum += palette[paletteIndex][0];
greenSum += palette[paletteIndex][1];
redSum += palette[paletteIndex][2];
}
}
const int samples = AA * AA;
row[x * 3 + 0] = static_cast<uint8_t>(blueSum / samples);
row[x * 3 + 1] = static_cast<uint8_t>(greenSum / samples);
row[x * 3 + 2] = static_cast<uint8_t>(redSum / samples);
}
const int done = ++completedRows;
if (done % 50 == 0 || done == HEIGHT) {
std::fprintf(stderr, "\rProgress: %d/%d rows (%.1f%%)", done, HEIGHT, 100.0 * done / HEIGHT);
}
}
BMPHeader header;
header.width = WIDTH; header.height = HEIGHT;
header.sizeImage = static_cast<uint32_t>(image.size());
header.size = header.sizeImage + 54;
FILE * file = std::fopen(OUTPUT_FILE, "wb");
if (!file) { std::perror(OUTPUT_FILE); return 1; }
std::fwrite(&header, sizeof(header), 1, file);
std::fwrite(image.data(), 1, image.size(), file);
std::fclose(file);
std::fprintf(stderr, "\nDone! Saved to %s in %.2f seconds.\n", OUTPUT_FILE, omp_get_wtime() - startTime);
return 0;
}
uses
System.Drawing,
System.Windows.Forms,
System.Threading,
FormsABC;
procedure DrawMandelbrot(g: Graphics; w,h: integer; scale: real; dx,dy: integer);
const max = 10;
begin
for var ix:=0 to w-1 do
for var iy:=0 to h-1 do
begin
var x := 0.0;
var y := 0.0;
var cx := scale * (ix - dx);
var cy := scale * (iy - dy);
var i := 1;
while i<255 do
begin
var x1 := x*x-y*y+cx;
var y1 := 2*x*y+cy;
x := x1;
y := y1;
if (abs(x)>max) and (abs(y)>max) then break;
i += 1;
end;
if i>=255 then
g.FillRectangle(Brushes.Red,ix,iy,1,1)
else
g.FillRectangle(new SolidBrush(Color.FromArgb(255,255-i,255-i)),ix,iy,1,1)
end;
end;
var
Scale := new RealField('Масштаб: ');
l1 := new FlowBreak;
dx := new IntegerField('dx: ');
l2 := new FlowBreak;
dy := new IntegerField('dy: ');
l3 := new FlowBreak(20);
b := new Button(' Нарисовать ');
p: PaintBox;
procedure Draw;
begin
var g := p.Graphics;
DrawMandelbrot(g,p.Width,p.Height,Scale.Value,dx.Value,dy.Value);
p.Invalidate;
end;
procedure My(o: Object);
begin
Draw;
end;
procedure Click;
begin
ThreadPool.QueueUserWorkItem(My);
end;
begin
MainForm.Title := 'Множество Мандельброта';
MainForm.SetSize(700, 600);
MainPanel.Dock := Dockstyle.Left;
MainPanel.Width := 120;
Scale.Value := 0.0035;
dx.Value := 430;
dy.Value := 280;
b.Click += Click;
ParentControl := MainForm;
p := new PaintBox;
p.Dock := DockStyle.Fill;
ThreadPool.QueueUserWorkItem(My);
end.
Пример программы построения множества (на языке программирования C++ с использованием SFML)
[править]///Heared.hpp
#pragma once
#include <SFML\Graphics.hpp>
//#include <boost\math\bindings\rr.hpp>
typedef long double TYPE;
template<typename T>
struct Complex
{
T re;
T im;
Complex operator += (const Complex & other)
{
re += other.re;
im += other.im;
return *this;
}
Complex operator + (const Complex & other)
{
Complex c(*this);
return c += other;
}
Complex operator *= (const Complex & other)
{
Complex c;
c.re = re * other.re - im * other.im;
c.im = re * other.im + im * other.re;
*this = c;
return *this;
}
Complex operator * (const Complex & other)
{
Complex c(*this);
return c *= other;
}
Complex operator = (const Complex & other)
{
re = other.re;
im = other.im;
return *this;
}
Complex(): re((T)0), im((T)0)
{
}
Complex(const Complex& other) : re(other.re), im(other.im)
{
}
Complex(const T& re, const T& im) : re(re), im(im)
{
}
Complex(const T& re) : re(re), im((T)0)
{
}
T module_sqr()const
{
return re * re + im * im;
}
};
//void drawM(sf::Vector2<TYPE> x, sf::Vector2<TYPE> y);
///draw.cpp
#include "Header.hpp"
void step_clr(sf::Color & clr)
{
clr.r = clr.r + 5 < 0x100 ? clr.r + 5 : 0xff;
clr.g = clr.g + 3 < 0x100 ? clr.g + 5 : 0xff;
clr.b = clr.b + 2 < 0x100 ? clr.b + 5 : 0xff;
}
void drawM(sf::Vector2<TYPE> x, sf::Vector2<TYPE> y)
{
const TYPE epsilon = 0.005;
sf::Vector2i sizewindow((int)((x.y - x.x) / epsilon), (int)((y.y - y.x) / epsilon));
sf::RenderWindow window(sf::VideoMode(sizewindow.x, sizewindow.y), "Mandelbroth");
sf::Image im;
const int max_it = 250;
const int infinity_sqr = 10000;
im.create(sizewindow.x, sizewindow.y);
sf::Color clr;
int xim = 0, yim = 0;
for (TYPE currx = x.x; currx <= x.y; currx += epsilon, ++xim)
{
yim = 0;
while (xim >= sizewindow.x)
--xim;
for (TYPE curry = y.x; curry <= y.y; curry += epsilon, ++yim)
{
while (yim >= sizewindow.y)
--yim;
clr = sf::Color::Black;
Complex<TYPE> curr;
im.setPixel(xim, yim, sf::Color::Black);
for (int i = 0; i < max_it; ++i)
{
if (curr.module_sqr() >= infinity_sqr)
{
im.setPixel(xim, yim, clr);
break;
}
curr = curr * curr + Complex<TYPE>(currx, curry);
step_clr(clr);
}
}
}
sf::Sprite s;
sf::Texture t;
t.loadFromImage(im);
s.setTexture(t);
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
window.clear();
window.draw(s);
window.display();
}
}
///main.cpp
#include "Header.hpp"
#include "draw.cpp"
#include <iostream>
int main()
{
TYPE x1, x2, y1, y2;
//std::cin >> x1 >> x2 >> y1 >> y2;
x1 = -2.5;
x2 = 2;
y1 = -1.5;
y2 = 1.5;
drawM(sf::Vector2<TYPE>(x1, x2), sf::Vector2<TYPE>(y1, y2));
}
Добавление цвета
[править]
Строго математически, изображения множеств Мандельброта и Жюлиа должны быть чёрно-белыми. Точка либо попадает внутрь множества, либо нет. Несмотря на это, с помощью компьютера мы можем построить и цветные изображения. Самым распространённым способом является раскрашивание точек снаружи множества в цвет, соответствующий количеству итераций, за которое точка уходит в «бесконечность» или, с точки зрения программы, на определённое расстояние от нуля.
Порядок определения, попадает ли точка z0 внутрь множества (традиционно закрашиваемого чёрным цветом) или нет (закрашивается цветом, зависящим от скорости движения к бесконечности) следующий: на каждой итерации для zn=xn+yn·i вычисляется значение модуля , которое затем сравнивается с «границей бесконечности» (обычно берётся значение, равное 2). Здесь важно обратить внимание, что уже на данном этапе можно ввести определённую оптимизацию вычислений, если проверять не , а , что значительно снизит время расчётов.
Таким образом, если |zn|2 ≤ 4 при любом числе итераций (на практике — при всех вычисленных итерациях), то цвет точки чёрный, в противном случае он зависит от последнего значения n, при котором |zn|2 ≤ 4. Значение n, фактически, обозначает скорость движения zn в бесконечность, и может быть просто индексом в таблице цветов, или использоваться как параметр в более сложном алгоритме.
Данный алгоритм определяет, что если точка удаляется больше чем на 2 от начала координат, то она лежит снаружи множества Мандельброта. Для того, чтобы определить, что точка лежит внутри множества есть много способов. Самое простое решение — ограничить количество итераций неким максимумом. Если точка не вышла за указанную границу, можно считать, что она находится внутри множества.
Точкам около границы множества нужно больше итераций для ухода в бесконечность. Поэтому такие области прорисовываются заметно дольше. Чем дальше от границ множества, тем выше скорость ухода в бесконечность. Для таких точек требуется меньше итераций.
Пример добавления цвета (на PHP)
[править]<?php
// Default: http://<host>/<dir>/<filename>.php?iter1=64&width=600&height=400&coef=32
function BN($n, $l, $r) {return $n>$l && $n<=$r;}
function SQR($a) {return $a*$a;}
define("COEF", $_GET["coef"]);
$iter1 = $_GET["iter1"];
$width = $_GET["width"];
$height = $_GET["height"];
header("Content-type: image/png");
$img = imagecreatetruecolor($width, $height);
$iter2 = 0.01/($width/300);
$yy = -1;
for ($y = -1; $y < 1; $y = $y + $iter2) {
$yy++; $xx=-1;
for($x = -2; $x < 1; $x = $x + $iter2) {
$xx++;
$Cx = $x;
$Cy = $y;
$X = $x;
$Y = $y;
$ix = 0;
$iy = 0;
$n = 0;
while ((SQR($ix) + SQR($iy) < 4) and ($n < $iter1)) {
$ix = SQR($X) - SQR($Y) + $Cx;
$iy = 2*$X*$Y + $Cy;
$X = $ix;
$Y = $iy;
$n++;
}
if(BN($n,0,7)) $col = imagecolorallocate($img,COEF*$n,0,0);
elseif(BN($n,7,14)) $col = imagecolorallocate($img,COEF*$n,COEF*$n,0);
elseif(BN($n,14,21))$col = imagecolorallocate($img,COEF*$n,0,COEF*$n);
elseif(BN($n,21,28))$col = imagecolorallocate($img,0,COEF*$n,0);
elseif(BN($n,28,35))$col = imagecolorallocate($img,COEF*$n,COEF*$n,0);
elseif(BN($n,35,42))$col = imagecolorallocate($img,0,COEF*$n,COEF*$n);
elseif(BN($n,42,49))$col = imagecolorallocate($img,0,0,COEF*$n);
elseif(BN($n,49,56))$col = imagecolorallocate($img,COEF*$n,0,COEF*$n);
elseif(BN($n,56,64))$col = imagecolorallocate($img,0,COEF*$n,COEF*$n);
imagesetpixel($img, $xx, $yy, $col);
}
}
imagepng($img);
imagedestroy($img);
?>
Пример добавления цвета (на C++)
[править]///draw.cpp
#include "Header.hpp"
void step_clr(float & red, float & green, float & blue)
{
red = (int)(red + 5) < 0xff ? red + 5 : 0xff;
if((int)red == 0xff)
green = (int)(green + 3.5) < 0xff ? green + 3.5 : 0xff;
if((int)green == 0xff)
blue = (int)(blue + 2.2) < 0xff ? blue + 2.2 : 0xff;
}
void drawM(sf::Vector2<TYPE> x, sf::Vector2<TYPE> y)
{
const TYPE epsilon = 0.005;
sf::Vector2i sizewindow((int)((x.y - x.x) / epsilon), (int)((y.y - y.x) / epsilon));
sf::RenderWindow window(sf::VideoMode(sizewindow.x, sizewindow.y), "Mandelbroth");
sf::Image im;
const int max_it = 250;
const int infinity_sqr = 10000;
im.create(sizewindow.x, sizewindow.y);
sf::Color clr;
int xim = 0, yim = 0;
for (TYPE currx = x.x; currx <= x.y; currx += epsilon, ++xim)
{
yim = 0;
while (xim >= sizewindow.x)
--xim;
for (TYPE curry = y.x; curry <= y.y; curry += epsilon, ++yim)
{
while (yim >= sizewindow.y)
--yim;
clr = sf::Color::Black;
Complex<TYPE> curr;
float red = 0, green = 0, blue = 0;
im.setPixel(xim, yim, sf::Color::Black);
for (int i = 0; i < max_it; ++i)
{
if (curr.module_sqr() >= infinity_sqr)
{
clr = sf::Color(red, green, blue);
im.setPixel(xim, yim, clr);
break;
}
curr = curr * curr + Complex<TYPE>(currx, curry);
step_clr(red, green, blue);
}
}
}
sf::Sprite s;
sf::Texture t;
t.loadFromImage(im);
s.setTexture(t);
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
window.clear();
window.draw(s);
window.display();
}
}
