1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-13 00:04:19 +00:00
JUCE/extras/NetworkGraphicsDemo/Source/Demos.h

497 lines
18 KiB
C++

/*
==============================================================================
This file is part of the JUCE 6 technical preview.
Copyright (c) 2020 - Raw Material Software Limited
You may use this code under the terms of the GPL v3
(see www.gnu.org/licenses).
For this technical preview, this file is not subject to commercial licensing.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
struct BlankCanvas : public AnimatedContent
{
String getName() const override { return "Blank Canvas"; }
void reset() override {}
void handleTouch (Point<float>) override {}
void generateCanvas (Graphics&, SharedCanvasDescription&, Rectangle<float>) override {}
};
//==============================================================================
struct GridLines : public AnimatedContent
{
String getName() const override { return "Grid Lines"; }
void reset() override {}
void handleTouch (Point<float>) override {}
void generateCanvas (Graphics& g, SharedCanvasDescription& canvas, Rectangle<float>) override
{
auto limits = canvas.getLimits();
float lineThickness = 0.1f;
g.setColour (Colours::blue);
g.drawRect (canvas.getLimits(), lineThickness);
for (float y = limits.getY(); y < limits.getBottom(); y += 2.0f)
g.drawLine (limits.getX(), y, limits.getRight(), y, lineThickness);
for (float x = limits.getX(); x < limits.getRight(); x += 2.0f)
g.drawLine (x, limits.getY(), x, limits.getBottom(), lineThickness);
g.setColour (Colours::darkred);
g.drawLine (limits.getX(), limits.getCentreY(), limits.getRight(), limits.getCentreY(), lineThickness);
g.drawLine (limits.getCentreX(), limits.getY(), limits.getCentreX(), limits.getBottom(), lineThickness);
g.setColour (Colours::lightgrey);
g.drawLine (limits.getX(), limits.getY(), limits.getRight(), limits.getBottom(), lineThickness);
g.drawLine (limits.getX(), limits.getBottom(), limits.getRight(), limits.getY(), lineThickness);
}
};
//==============================================================================
struct BackgroundLogo : public AnimatedContent
{
BackgroundLogo()
{
static const char logoData[] = R"blahblah(
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 239.2 239.2" enable-background="new 0 0 239.2 239.2" xml:space="preserve">
<path fill="#6CC04A" d="M118.8,201.3c-44.6,0-81-36.3-81-81s36.3-81,81-81s81,36.3,81,81S163.4,201.3,118.8,201.3z M118.8,44.8c-41.7,0-75.6,33.9-75.6,75.6s33.9,75.6,75.6,75.6s75.6-33.9,75.6-75.6S160.4,44.8,118.8,44.8z"/>
<path fill="#3B5CAD" d="M182.6,117.6c1.4,0,2.7-0.5,3.7-1.5c1.1-1.1,1.6-2.5,1.4-4c-1.5-12.7-6.5-24.7-14.4-34.8c-1-1.2-2.3-1.9-3.8-1.9c-1.3,0-2.6,0.5-3.6,1.5l-39,39c-0.6,0.6-0.2,1.6,0.7,1.6L182.6,117.6z"/>
<path fill="#E73E51" d="M169.5,165.2L169.5,165.2c1.5,0,2.8-0.7,3.8-1.9c7.9-10.1,12.9-22.1,14.4-34.8c0.2-1.5-0.3-2.9-1.4-4c-1-1-2.3-1.5-3.7-1.5l-55,0c-0.9,0-1.3,1-0.7,1.6l39,39C166.9,164.7,168.2,165.2,169.5,165.2z"/>
<path fill="#E67E3C" d="M122.9,188L122.9,188c1,1,2.5,1.5,4,1.3c12.7-1.5,24.8-6.5,34.8-14.4c1.2-0.9,1.8-2.3,1.9-3.8c0-1.4-0.6-2.7-1.6-3.7l-38.9-38.9c-0.6-0.6-1.6-0.2-1.6,0.7l0,55.2C121.4,185.8,122,187,122.9,188z"/>
<path fill="#F0E049" d="M68,75.4c-1.5,0-2.8,0.7-3.8,1.9c-7.9,10.1-12.9,22.1-14.4,34.8c-0.2,1.5,0.3,2.9,1.4,4c1,1,2.3,1.5,3.7,1.5l55,0c0.9,0,1.3-1,0.7-1.6l-39-39C70.6,76,69.3,75.4,68,75.4z"/>
<path fill="#D5D755" d="M114.6,52.7c-1-1-2.5-1.5-4-1.3c-12.7,1.5-24.8,6.5-34.8,14.4c-1.2,0.9-1.8,2.3-1.9,3.8c0,1.4,0.6,2.7,1.6,3.7l38.9,38.9c0.6,0.6,1.6,0.2,1.6-0.7l0-55.2C116.1,54.9,115.5,53.6,114.6,52.7z"/>
<path fill="#9CB6D3" d="M163.7,69.6c0-1.5-0.7-2.8-1.9-3.8c-10.1-7.9-22.1-12.9-34.8-14.4c-1.5-0.2-2.9,0.3-4,1.4c-1,1-1.5,2.3-1.5,3.7l0,55c0,0.9,1,1.3,1.6,0.7l39-39C163.1,72.1,163.7,70.9,163.7,69.6z"/>
<path fill="#F5BD47" d="M109.9,123l-55,0c-1.4,0-2.7,0.5-3.7,1.5c-1.1,1.1-1.6,2.5-1.4,4c1.5,12.7,6.5,24.7,14.4,34.8c1,1.2,2.3,1.9,3.8,1.9c1.3,0,2.6-0.5,3.5-1.5c0,0,0,0,0,0l39-39C111.2,124,110.8,123,109.9,123z"/>
<path fill="#F19F53" d="M114.4,128.5l-38.9,38.9c-1,1-1.6,2.3-1.6,3.7c0,1.5,0.7,2.9,1.9,3.8c10,7.9,22.1,12.9,34.8,14.4c1.6,0.2,3-0.3,4-1.3c0.9-0.9,1.4-2.2,1.4-3.6c0,0,0,0,0,0l0-55.2C116.1,128.3,115,127.9,114.4,128.5z"/>
</svg>
)blahblah";
logo = Drawable::createFromSVG (*parseXML (logoData));
}
String getName() const override { return "Background Image"; }
void reset() override {}
void handleTouch (Point<float>) override {}
void generateCanvas (Graphics& g, SharedCanvasDescription& canvas, Rectangle<float>) override
{
logo->drawWithin (g, canvas.getLimits().reduced (3.0f), RectanglePlacement (RectanglePlacement::centred), 0.6f);
}
std::unique_ptr<Drawable> logo;
};
//==============================================================================
struct FlockDemo : public BackgroundLogo
{
String getName() const override { return "Flock"; }
void setNumBirds (int numBirds)
{
BackgroundLogo::reset();
birds.clear();
for (int i = numBirds; --i >= 0;)
birds.add ({});
centreOfGravity = {};
lastGravityMove = {};
fakeMouseTouchLengthToRun = 0;
fakeMouseTouchPosition = {};
fakeMouseTouchVelocity = {};
}
void reset() override
{
BackgroundLogo::reset();
setNumBirds (100);
}
void generateCanvas (Graphics& g, SharedCanvasDescription& canvas, Rectangle<float> activeArea) override
{
BackgroundLogo::generateCanvas (g, canvas, activeArea);
if (Time::getCurrentTime() > lastGravityMove + RelativeTime::seconds (0.5))
{
if (fakeMouseTouchLengthToRun > 0)
{
--fakeMouseTouchLengthToRun;
fakeMouseTouchPosition += fakeMouseTouchVelocity;
centreOfGravity = fakeMouseTouchPosition;
}
else
{
centreOfGravity = {};
if (rng.nextInt (300) == 2 && canvas.clients.size() > 0)
{
fakeMouseTouchLengthToRun = 50;
fakeMouseTouchPosition = canvas.clients.getReference (rng.nextInt (canvas.clients.size())).centre;
fakeMouseTouchVelocity = { rng.nextFloat() * 0.3f - 0.15f,
rng.nextFloat() * 0.3f - 0.15f };
}
}
}
g.setColour (Colours::white.withAlpha (0.2f));
if (! centreOfGravity.isOrigin())
g.fillEllipse (centreOfGravity.getX() - 1.0f, centreOfGravity.getY() - 1.0f, 2.0f, 2.0f);
for (int i = 0; i < birds.size(); ++i)
for (int j = i + 1; j < birds.size(); ++j)
attractBirds (birds.getReference(i), birds.getReference(j));
for (auto& b : birds)
{
if (! centreOfGravity.isOrigin())
b.move (centreOfGravity, 0.4f);
b.update();
b.draw (g);
b.bounceOffEdges (canvas.getLimits().expanded (1.0f));
}
for (int i = rings.size(); --i >= 0;)
{
if (rings.getReference(i).update())
rings.getReference(i).draw (g);
else
rings.remove (i);
}
}
bool isRingNear (Point<float> p) const
{
for (auto& r : rings)
if (r.centre.getDistanceFrom (p) < 1.0f)
return true;
return false;
}
void handleTouch (Point<float> position) override
{
lastGravityMove = Time::getCurrentTime();
centreOfGravity = position;
fakeMouseTouchLengthToRun = 0;
if (! isRingNear (position))
rings.add ({ position, 1.0f, 0.5f });
}
//==============================================================================
struct Bird
{
Bird()
{
Random randGen;
pos.x = randGen.nextFloat() * 10.0f - 5.0f;
pos.y = randGen.nextFloat() * 10.0f - 5.0f;
velocity.x = randGen.nextFloat() * 0.001f;
velocity.y = randGen.nextFloat() * 0.001f;
colour = Colour::fromHSV (randGen.nextFloat(), 0.2f, 0.9f, randGen.nextFloat() * 0.4f + 0.2f);
shape.addTriangle (0.0f, 0.0f, -0.3f, 1.0f, 0.3f, 1.0f);
shape = shape.createPathWithRoundedCorners (0.2f);
shape.applyTransform (AffineTransform::scale (randGen.nextFloat() + 1.0f));
}
Point<float> pos, velocity, acc;
Colour colour;
Path shape;
void move (Point<float> target, float strength)
{
auto r = target - pos;
float rSquared = jmax (0.1f, (r.x * r.x) + (r.y * r.y));
if (rSquared > 1.0f)
velocity += (r * strength / rSquared);
acc = {};
}
void accelerate (Point<float> acceleration)
{
acc += acceleration;
}
void bounceOffEdges (Rectangle<float> limits)
{
if (pos.x < limits.getX()) { velocity.x = std::abs (velocity.x); acc = {}; }
if (pos.x > limits.getRight()) { velocity.x = -std::abs (velocity.x); acc = {}; }
if (pos.y < limits.getY()) { velocity.y = std::abs (velocity.y); acc = {}; }
if (pos.y > limits.getBottom()) { velocity.y = -std::abs (velocity.y); acc = {}; }
}
void update()
{
velocity += acc;
float length = velocity.getDistanceFromOrigin();
const float maxSpeed = 0.5f;
if (length > maxSpeed)
velocity = getVectorWithLength (velocity, maxSpeed);
pos += velocity;
}
void draw (Graphics& g)
{
g.setColour (colour);
g.fillPath (shape, AffineTransform::rotation (Point<float>().getAngleToPoint (velocity)).translated (pos));
}
};
static Point<float> getVectorWithLength (Point<float> v, float newLength)
{
return v * (newLength / v.getDistanceFromOrigin());
}
static void attractBirds (Bird& b1, Bird& b2)
{
auto delta = b1.pos - b2.pos;
const float zoneRadius = 10.0f;
const float low = 0.4f;
const float high = 0.65f;
const float strength = 0.01f;
const float distanceSquared = (delta.x * delta.x) * (delta.y * delta.y);
if (distanceSquared < zoneRadius * zoneRadius && distanceSquared > 0.01f)
{
float proportion = distanceSquared / (zoneRadius * zoneRadius);
if (proportion < low)
{
const float F = (low / proportion - 1.0f) * strength * 0.003f;
delta = getVectorWithLength (delta, F);
b1.accelerate (delta);
b2.accelerate (-delta);
}
else if (proportion < high)
{
const float regionSize = high - low;
const float adjustedProportion = (proportion - low) / regionSize;
const float F = (0.5f - std::cos (adjustedProportion * MathConstants<float>::twoPi) * 0.5f + 0.5f) * strength;
b1.accelerate (getVectorWithLength (b2.velocity, F));
b2.accelerate (getVectorWithLength (b1.velocity, F));
}
else
{
const float regionSize = 1.0f - high;
const float adjustedProportion = (proportion - high) / regionSize;
const float F = (0.5f - std::cos (adjustedProportion * MathConstants<float>::twoPi) * 0.5f + 0.5f) * strength;
delta = getVectorWithLength (delta, F);
b1.accelerate (-delta);
b2.accelerate (delta);
}
}
}
Random rng;
Array<Bird> birds;
Point<float> centreOfGravity;
Time lastGravityMove;
int fakeMouseTouchLengthToRun = 0;
Point<float> fakeMouseTouchPosition, fakeMouseTouchVelocity;
//==============================================================================
struct Ring
{
Point<float> centre;
float diameter, opacity;
bool update()
{
diameter += 0.7f;
opacity -= 0.01f;
return opacity > 0;
}
void draw (Graphics& g)
{
const float thickness = 0.2f;
auto r = Rectangle<float> (diameter, diameter).withCentre (centre);
Path p;
p.addEllipse (r);
p.addEllipse (r.reduced (thickness));
p.setUsingNonZeroWinding (false);
g.setColour (Colours::white.withAlpha (opacity));
g.fillPath (p);
}
};
Array<Ring> rings;
};
//==============================================================================
struct FlockWithText : public FlockDemo
{
FlockWithText()
{
messages.add ("JUCE is our cross-platform C++ framework\n\n"
"In this demo, the same C++ app is running natively on NUMDEVICES devices,\n"
"which are sharing their graphic state via the network");
messages.add ("No other libraries were needed to create this demo.\n"
"JUCE provides thousands of classes for cross-platform GUI,\n"
"audio, networking, data-structures and many other common tasks");
messages.add ("As well as a code library, JUCE provides tools for managing\n"
"cross-platform projects that are built with Xcode,\n"
"Visual Studio, Android Studio, GCC and other compilers");
messages.add ("JUCE can be used to build desktop or mobile apps, and also\n"
"audio plug-ins in the VST2, VST3, AudioUnit, AAX and RTAS formats");
}
String getName() const override { return "Flock with text"; }
void reset() override
{
FlockDemo::reset();
currentMessage = 0;
currentMessageStart = {};
clientIndex = 0;
}
void generateCanvas (Graphics& g, SharedCanvasDescription& canvas, Rectangle<float> activeArea) override
{
FlockDemo::generateCanvas (g, canvas, activeArea);
const float textSize = 0.5f; // inches
const float textBlockWidth = 20.0f; // inches
tick();
Graphics::ScopedSaveState ss (g);
const float scale = 20.0f; // scaled to allow the fonts to use more reasonable sizes
g.addTransform (AffineTransform::scale (1.0f / scale));
String text = String (messages[currentMessage]).replace ("NUMDEVICES", String (canvas.clients.size()));
AttributedString as;
as.append (text, Font (textSize * scale), Colour (0x80ffffff).withMultipliedAlpha (alpha));
as.setJustification (Justification::centred);
auto middle = canvas.clients[clientIndex % canvas.clients.size()].centre * scale;
as.draw (g, Rectangle<float> (textBlockWidth * scale, textBlockWidth * scale).withCentre (middle));
}
void tick()
{
const double displayTimeSeconds = 5.0;
const double fadeTimeSeconds = 1.0;
Time now = Time::getCurrentTime();
const double secondsSinceStart = (now - currentMessageStart).inSeconds();
if (secondsSinceStart > displayTimeSeconds)
{
currentMessageStart = now;
currentMessage = (currentMessage + 1) % messages.size();
++clientIndex;
alpha = 0;
}
else if (secondsSinceStart > displayTimeSeconds - fadeTimeSeconds)
{
alpha = (float) jlimit (0.0, 1.0, (displayTimeSeconds - secondsSinceStart) / fadeTimeSeconds);
}
else if (secondsSinceStart < fadeTimeSeconds)
{
alpha = (float) jlimit (0.0, 1.0, secondsSinceStart / fadeTimeSeconds);
}
}
StringArray messages;
int currentMessage = 0, clientIndex = 0;
float alpha = 0;
Point<float> centre;
Time currentMessageStart;
};
//==============================================================================
struct SmallFlock : public FlockDemo
{
String getName() const override { return "Small Flock"; }
void reset() override
{
setNumBirds (20);
}
};
//==============================================================================
struct BigFlock : public FlockDemo
{
String getName() const override { return "Big Flock"; }
void reset() override
{
setNumBirds (200);
}
};
//==============================================================================
template <int numHorizontalLogos>
struct MultiLogo : public BackgroundLogo
{
String getName() const override { return "Multi-Logo " + String ((int) numHorizontalLogos); }
void generateCanvas (Graphics& g, SharedCanvasDescription& canvas, Rectangle<float>) override
{
float indent = 0.5f;
float logoSize = canvas.getLimits().getWidth() / numHorizontalLogos;
auto limits = canvas.getLimits();
for (float x = limits.getX(); x < limits.getRight(); x += logoSize)
{
for (float y = limits.getY(); y < limits.getBottom(); y += logoSize)
{
logo->drawWithin (g, Rectangle<float> (x, y, logoSize, logoSize).reduced (indent),
RectanglePlacement (RectanglePlacement::centred), 0.5f);
}
}
}
};
//==============================================================================
void createAllDemos (OwnedArray<AnimatedContent>& demos)
{
demos.add (new FlockDemo());
demos.add (new FlockWithText());
demos.add (new SmallFlock());
demos.add (new BigFlock());
demos.add (new BackgroundLogo());
demos.add (new MultiLogo<5>());
demos.add (new MultiLogo<10>());
demos.add (new GridLines());
demos.add (new BlankCanvas());
}