- clean working copy for leaving SVN

This commit is contained in:
2022-06-28 19:04:46 +02:00
parent 62a2d47cb3
commit 762b8f0e7d
1966 changed files with 699872 additions and 126 deletions
@@ -0,0 +1,299 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-12 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../JuceDemoHeader.h"
//==============================================================================
/** This will be the source of our balls and can be dragged around. */
class BallGeneratorComponent : public Component
{
public:
BallGeneratorComponent()
{
}
void paint (Graphics& g) override
{
Rectangle<float> area (getLocalBounds().toFloat().reduced (2.0f));
g.setColour (Colours::orange);
g.drawRoundedRectangle (area, 10.0f, 2.0f);
AttributedString s;
s.setJustification (Justification::centred);
s.setWordWrap (AttributedString::none);
s.append ("Drag Me!");
s.setColour (Colours::white);
s.draw (g, area);
}
void resized() override
{
// Just set the limits of our constrainer so that we don't drag ourselves off the screen
constrainer.setMinimumOnscreenAmounts (getHeight(), getWidth(), getHeight(), getWidth());
}
void mouseDown (const MouseEvent& e) override
{
// Prepares our dragger to drag this Component
dragger.startDraggingComponent (this, e);
}
void mouseDrag (const MouseEvent& e) override
{
// Moves this Component according to the mouse drag event and applies our constraints to it
dragger.dragComponent (this, e, &constrainer);
}
private:
ComponentBoundsConstrainer constrainer;
ComponentDragger dragger;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BallGeneratorComponent)
};
//==============================================================================
struct BallComponent : public Component
{
BallComponent (const Point<float>& pos)
: position (pos),
speed (Random::getSystemRandom().nextFloat() * 4.0f - 2.0f,
Random::getSystemRandom().nextFloat() * -6.0f - 2.0f),
colour (Colours::white)
{
setSize (20, 20);
step();
}
bool step()
{
position += speed;
speed.y += 0.1f;
setCentrePosition ((int) position.x,
(int) position.y);
if (Component* parent = getParentComponent())
return isPositiveAndBelow (position.x, (float) parent->getWidth())
&& position.y < (float) parent->getHeight();
return position.y < 400.0f && position.x >= -10.0f;
}
void paint (Graphics& g)
{
g.setColour (colour);
g.fillEllipse (2.0f, 2.0f, getWidth() - 4.0f, getHeight() - 4.0f);
g.setColour (Colours::darkgrey);
g.drawEllipse (2.0f, 2.0f, getWidth() - 4.0f, getHeight() - 4.0f, 1.0f);
}
Point<float> position, speed;
Colour colour;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BallComponent)
};
//==============================================================================
class AnimationDemo : public Component,
private Button::Listener,
private Timer
{
public:
AnimationDemo()
{
setOpaque (true);
setSize (620, 620);
for (int i = 11; --i >= 0;)
{
Button* b = createButton();
componentsToAnimate.add (b);
addAndMakeVisible (b);
b->addListener (this);
}
addAndMakeVisible (ballGenerator);
ballGenerator.centreWithSize (80, 50);
cycleCount = 2;
for (int i = 0; i < componentsToAnimate.size(); ++i)
componentsToAnimate.getUnchecked (i)->setBounds (getLocalBounds().reduced (250, 250));
for (int i = 0; i < componentsToAnimate.size(); ++i)
{
const int newIndex = (i + 3) % componentsToAnimate.size();
const float angle = newIndex * 2.0f * float_Pi / componentsToAnimate.size();
const float radius = getWidth() * 0.35f;
Rectangle<int> r (getWidth() / 2 + (int) (radius * std::sin (angle)) - 50,
getHeight() / 2 + (int) (radius * std::cos (angle)) - 50,
100, 100);
animator.animateComponent (componentsToAnimate.getUnchecked(i),
r.reduced (10),
1.0f,
500 + i * 100,
false,
0.0,
0.0);
}
startTimerHz (60);
}
void paint (Graphics& g) override
{
fillTiledBackground (g);
}
private:
OwnedArray<Component> componentsToAnimate;
OwnedArray<BallComponent> balls;
BallGeneratorComponent ballGenerator;
ComponentAnimator animator;
int cycleCount;
Button* createRandomButton()
{
DrawablePath normal, over;
Path star1;
star1.addStar (Point<float>(), 5, 20.0f, 50.0f, 0.2f);
normal.setPath (star1);
normal.setFill (Colours::red);
Path star2;
star2.addStar (Point<float>(), 7, 30.0f, 50.0f, 0.0f);
over.setPath (star2);
over.setFill (Colours::pink);
over.setStrokeFill (Colours::black);
over.setStrokeThickness (5.0f);
Image juceIcon = ImageCache::getFromMemory (BinaryData::juce_icon_png,
BinaryData::juce_icon_pngSize);
DrawableImage down;
down.setImage (juceIcon);
down.setOverlayColour (Colours::black.withAlpha (0.3f));
if (Random::getSystemRandom().nextInt (10) > 2)
{
int type = Random::getSystemRandom().nextInt (3);
DrawableButton* d = new DrawableButton ("Button",
type == 0 ? DrawableButton::ImageOnButtonBackground
: (type == 1 ? DrawableButton::ImageFitted
: DrawableButton::ImageAboveTextLabel));
d->setImages (&normal,
Random::getSystemRandom().nextBool() ? &over : nullptr,
Random::getSystemRandom().nextBool() ? &down : nullptr);
if (Random::getSystemRandom().nextBool())
{
d->setColour (DrawableButton::backgroundColourId, getRandomBrightColour());
d->setColour (DrawableButton::backgroundOnColourId, getRandomBrightColour());
}
d->setClickingTogglesState (Random::getSystemRandom().nextBool());
return d;
}
ImageButton* b = new ImageButton ("ImageButton");
b->setImages (true, true, true,
juceIcon, 0.7f, Colours::transparentBlack,
juceIcon, 1.0f, getRandomDarkColour().withAlpha (0.2f),
juceIcon, 1.0f, getRandomBrightColour().withAlpha (0.8f),
0.5f);
return b;
}
Button* createButton()
{
Image juceIcon = ImageCache::getFromMemory (BinaryData::juce_icon_png,
BinaryData::juce_icon_pngSize);
ImageButton* b = new ImageButton ("ImageButton");
b->setImages (true, true, true,
juceIcon, 1.0f, Colours::transparentBlack,
juceIcon, 1.0f, Colours::white,
juceIcon, 1.0f, Colours::white,
0.5f);
return b;
}
void buttonClicked (Button*) override
{
for (int i = 0; i < componentsToAnimate.size(); ++i)
{
const int newIndex = (i + 3 * cycleCount) % componentsToAnimate.size();
const float angle = newIndex * 2.0f * float_Pi / componentsToAnimate.size();
const float radius = getWidth() * 0.35f;
Rectangle<int> r (getWidth() / 2 + (int) (radius * std::sin (angle)) - 50,
getHeight() / 2 + (int) (radius * std::cos (angle)) - 50,
100, 100);
animator.animateComponent (componentsToAnimate.getUnchecked(i),
r.reduced (10),
1.0f,
900 + (int) (300 * std::sin (angle)),
false,
0.0,
0.0);
}
++cycleCount;
}
void timerCallback() override
{
// Go through each of our balls and update their position
for (int i = balls.size(); --i >= 0;)
if (! balls.getUnchecked(i)->step())
balls.remove (i);
// Randomly generate new balls
if (Random::getSystemRandom().nextInt (100) < 4)
{
BallComponent* ball = new BallComponent (ballGenerator.getBounds().getCentre().toFloat());
addAndMakeVisible (ball);
balls.add (ball);
}
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AnimationDemo)
};
// This static object will register this demo type in a global list of demos..
static JuceDemoType<AnimationDemo> demo ("10 Components: Animation");
@@ -0,0 +1,371 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-12 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../JuceDemoHeader.h"
#include "AudioLiveScrollingDisplay.h"
class LatencyTester : public AudioIODeviceCallback,
private Timer
{
public:
LatencyTester (TextEditor& resultsBox_)
: playingSampleNum (0),
recordedSampleNum (-1),
sampleRate (0),
testIsRunning (false),
resultsBox (resultsBox_)
{
MainAppWindow::getSharedAudioDeviceManager().addAudioCallback (this);
}
~LatencyTester()
{
MainAppWindow::getSharedAudioDeviceManager().removeAudioCallback (this);
}
//==============================================================================
void beginTest()
{
resultsBox.moveCaretToEnd();
resultsBox.insertTextAtCaret (newLine + newLine + "Starting test..." + newLine);
resultsBox.moveCaretToEnd();
startTimer (50);
const ScopedLock sl (lock);
createTestSound();
recordedSound.clear();
playingSampleNum = recordedSampleNum = 0;
testIsRunning = true;
}
void timerCallback()
{
if (testIsRunning && recordedSampleNum >= recordedSound.getNumSamples())
{
testIsRunning = false;
stopTimer();
// Test has finished, so calculate the result..
const int latencySamples = calculateLatencySamples();
resultsBox.moveCaretToEnd();
resultsBox.insertTextAtCaret (getMessageDescribingResult (latencySamples));
resultsBox.moveCaretToEnd();
}
}
String getMessageDescribingResult (int latencySamples)
{
String message;
if (latencySamples >= 0)
{
message << newLine
<< "Results:" << newLine
<< latencySamples << " samples (" << String (latencySamples * 1000.0 / sampleRate, 1)
<< " milliseconds)" << newLine
<< "The audio device reports an input latency of "
<< deviceInputLatency << " samples, output latency of "
<< deviceOutputLatency << " samples." << newLine
<< "So the corrected latency = "
<< (latencySamples - deviceInputLatency - deviceOutputLatency)
<< " samples (" << String ((latencySamples - deviceInputLatency - deviceOutputLatency) * 1000.0 / sampleRate, 2)
<< " milliseconds)";
}
else
{
message << newLine
<< "Couldn't detect the test signal!!" << newLine
<< "Make sure there's no background noise that might be confusing it..";
}
return message;
}
//==============================================================================
void audioDeviceAboutToStart (AudioIODevice* device)
{
testIsRunning = false;
playingSampleNum = recordedSampleNum = 0;
sampleRate = device->getCurrentSampleRate();
deviceInputLatency = device->getInputLatencyInSamples();
deviceOutputLatency = device->getOutputLatencyInSamples();
recordedSound.setSize (1, (int) (0.9 * sampleRate));
recordedSound.clear();
}
void audioDeviceStopped()
{
// (nothing to do here)
}
void audioDeviceIOCallback (const float** inputChannelData,
int numInputChannels,
float** outputChannelData,
int numOutputChannels,
int numSamples)
{
const ScopedLock sl (lock);
if (testIsRunning)
{
float* const recordingBuffer = recordedSound.getWritePointer (0);
const float* const playBuffer = testSound.getReadPointer (0);
for (int i = 0; i < numSamples; ++i)
{
if (recordedSampleNum < recordedSound.getNumSamples())
{
float inputSamp = 0;
for (int j = numInputChannels; --j >= 0;)
if (inputChannelData[j] != 0)
inputSamp += inputChannelData[j][i];
recordingBuffer [recordedSampleNum] = inputSamp;
}
++recordedSampleNum;
float outputSamp = (playingSampleNum < testSound.getNumSamples()) ? playBuffer [playingSampleNum] : 0;
for (int j = numOutputChannels; --j >= 0;)
if (outputChannelData[j] != 0)
outputChannelData[j][i] = outputSamp;
++playingSampleNum;
}
}
else
{
// We need to clear the output buffers, in case they're full of junk..
for (int i = 0; i < numOutputChannels; ++i)
if (outputChannelData[i] != 0)
zeromem (outputChannelData[i], sizeof (float) * (size_t) numSamples);
}
}
private:
AudioSampleBuffer testSound, recordedSound;
Array<int> spikePositions;
int playingSampleNum, recordedSampleNum;
CriticalSection lock;
double sampleRate;
bool testIsRunning;
TextEditor& resultsBox;
int deviceInputLatency, deviceOutputLatency;
// create a test sound which consists of a series of randomly-spaced audio spikes..
void createTestSound()
{
const int length = ((int) sampleRate) / 4;
testSound.setSize (1, length);
testSound.clear();
Random rand;
for (int i = 0; i < length; ++i)
testSound.setSample (0, i, (rand.nextFloat() - rand.nextFloat() + rand.nextFloat() - rand.nextFloat()) * 0.06f);
spikePositions.clear();
int spikePos = 0;
int spikeDelta = 50;
while (spikePos < length - 1)
{
spikePositions.add (spikePos);
testSound.setSample (0, spikePos, 0.99f);
testSound.setSample (0, spikePos + 1, -0.99f);
spikePos += spikeDelta;
spikeDelta += spikeDelta / 6 + rand.nextInt (5);
}
}
// Searches a buffer for a set of spikes that matches those in the test sound
int findOffsetOfSpikes (const AudioSampleBuffer& buffer) const
{
const float minSpikeLevel = 5.0f;
const double smooth = 0.975;
const float* s = buffer.getReadPointer (0);
const int spikeDriftAllowed = 5;
Array<int> spikesFound;
spikesFound.ensureStorageAllocated (100);
double runningAverage = 0;
int lastSpike = 0;
for (int i = 0; i < buffer.getNumSamples() - 10; ++i)
{
const float samp = std::abs (s[i]);
if (samp > runningAverage * minSpikeLevel && i > lastSpike + 20)
{
lastSpike = i;
spikesFound.add (i);
}
runningAverage = runningAverage * smooth + (1.0 - smooth) * samp;
}
int bestMatch = -1;
int bestNumMatches = spikePositions.size() / 3; // the minimum number of matches required
if (spikesFound.size() < bestNumMatches)
return -1;
for (int offsetToTest = 0; offsetToTest < buffer.getNumSamples() - 2048; ++offsetToTest)
{
int numMatchesHere = 0;
int foundIndex = 0;
for (int refIndex = 0; refIndex < spikePositions.size(); ++refIndex)
{
const int referenceSpike = spikePositions.getUnchecked (refIndex) + offsetToTest;
int spike = 0;
while ((spike = spikesFound.getUnchecked (foundIndex)) < referenceSpike - spikeDriftAllowed
&& foundIndex < spikesFound.size() - 1)
++foundIndex;
if (spike >= referenceSpike - spikeDriftAllowed && spike <= referenceSpike + spikeDriftAllowed)
++numMatchesHere;
}
if (numMatchesHere > bestNumMatches)
{
bestNumMatches = numMatchesHere;
bestMatch = offsetToTest;
if (numMatchesHere == spikePositions.size())
break;
}
}
return bestMatch;
}
int calculateLatencySamples() const
{
// Detect the sound in both our test sound and the recording of it, and measure the difference
// in their start times..
const int referenceStart = findOffsetOfSpikes (testSound);
jassert (referenceStart >= 0);
const int recordedStart = findOffsetOfSpikes (recordedSound);
return (recordedStart < 0) ? -1
: (recordedStart - referenceStart);
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LatencyTester);
};
//==============================================================================
class AudioLatencyDemo : public Component,
private Button::Listener
{
public:
AudioLatencyDemo()
{
setOpaque (true);
addAndMakeVisible (liveAudioScroller = new LiveScrollingAudioDisplay());
addAndMakeVisible (resultsBox);
resultsBox.setMultiLine (true);
resultsBox.setReturnKeyStartsNewLine (true);
resultsBox.setReadOnly (true);
resultsBox.setScrollbarsShown (true);
resultsBox.setCaretVisible (false);
resultsBox.setPopupMenuEnabled (true);
resultsBox.setColour (TextEditor::backgroundColourId, Colour (0x32ffffff));
resultsBox.setColour (TextEditor::outlineColourId, Colour (0x1c000000));
resultsBox.setColour (TextEditor::shadowColourId, Colour (0x16000000));
resultsBox.setText ("Running this test measures the round-trip latency between the audio output and input "
"devices you\'ve got selected.\n\n"
"It\'ll play a sound, then try to measure the time at which the sound arrives "
"back at the audio input. Obviously for this to work you need to have your "
"microphone somewhere near your speakers...");
addAndMakeVisible (startTestButton);
startTestButton.addListener (this);
startTestButton.setButtonText ("Test Latency");
MainAppWindow::getSharedAudioDeviceManager().addAudioCallback (liveAudioScroller);
}
~AudioLatencyDemo()
{
MainAppWindow::getSharedAudioDeviceManager().removeAudioCallback (liveAudioScroller);
startTestButton.removeListener (this);
latencyTester = nullptr;
liveAudioScroller = nullptr;
}
void startTest()
{
if (latencyTester == nullptr)
latencyTester = new LatencyTester (resultsBox);
latencyTester->beginTest();
}
void paint (Graphics& g) override
{
fillTiledBackground (g);
}
void resized() override
{
liveAudioScroller->setBounds (8, 8, getWidth() - 16, 64);
startTestButton.setBounds (8, getHeight() - 41, 168, 32);
resultsBox.setBounds (8, 88, getWidth() - 16, getHeight() - 137);
}
private:
ScopedPointer<LatencyTester> latencyTester;
ScopedPointer<LiveScrollingAudioDisplay> liveAudioScroller;
TextButton startTestButton;
TextEditor resultsBox;
void buttonClicked (Button* buttonThatWasClicked) override
{
if (buttonThatWasClicked == &startTestButton)
startTest();
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioLatencyDemo)
};
// This static object will register this demo type in a global list of demos..
static JuceDemoType<AudioLatencyDemo> demo ("31 Audio: Latency Detector");
@@ -0,0 +1,142 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-11 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#ifndef __AUDIOLIVESCROLLINGDISPLAY_H_4C3BD3A7__
#define __AUDIOLIVESCROLLINGDISPLAY_H_4C3BD3A7__
#include "../JuceDemoHeader.h"
//==============================================================================
/* This component scrolls a continuous waveform showing the audio that's
coming into whatever audio inputs this object is connected to.
*/
class LiveScrollingAudioDisplay : public Component,
public AudioIODeviceCallback,
private Timer
{
public:
LiveScrollingAudioDisplay()
: nextSample (0), subSample (0), accumulator (0)
{
setOpaque (true);
clear();
startTimerHz (75); // use a timer to keep repainting this component
}
//==============================================================================
void audioDeviceAboutToStart (AudioIODevice*) override
{
clear();
}
void audioDeviceStopped() override
{
clear();
}
void audioDeviceIOCallback (const float** inputChannelData, int numInputChannels,
float** outputChannelData, int numOutputChannels,
int numSamples) override
{
for (int i = 0; i < numSamples; ++i)
{
float inputSample = 0;
for (int chan = 0; chan < numInputChannels; ++chan)
if (inputChannelData[chan] != nullptr)
inputSample += std::abs (inputChannelData[chan][i]); // find the sum of all the channels
pushSample (10.0f * inputSample); // boost the level to make it more easily visible.
}
// We need to clear the output buffers before returning, in case they're full of junk..
for (int j = 0; j < numOutputChannels; ++j)
if (outputChannelData[j] != nullptr)
zeromem (outputChannelData[j], sizeof (float) * (size_t) numSamples);
}
private:
float samples[1024];
int nextSample, subSample;
float accumulator;
void clear()
{
zeromem (samples, sizeof (samples));
accumulator = 0;
subSample = 0;
}
void paint (Graphics& g) override
{
g.fillAll (Colours::black);
const float midY = getHeight() * 0.5f;
int samplesAgo = (nextSample + numElementsInArray (samples) - 1);
RectangleList<float> waveform;
waveform.ensureStorageAllocated ((int) numElementsInArray (samples));
for (int x = jmin (getWidth(), (int) numElementsInArray (samples)); --x >= 0;)
{
const float sampleSize = midY * samples [samplesAgo-- % numElementsInArray (samples)];
waveform.addWithoutMerging (Rectangle<float> ((float) x, midY - sampleSize, 1.0f, sampleSize * 2.0f));
}
g.setColour (Colours::lightgreen);
g.fillRectList (waveform);
}
void timerCallback() override
{
repaint();
}
void pushSample (const float newSample)
{
accumulator += newSample;
if (subSample == 0)
{
const int inputSamplesPerPixel = 200;
samples[nextSample] = accumulator / inputSamplesPerPixel;
nextSample = (nextSample + 1) % numElementsInArray (samples);
subSample = inputSamplesPerPixel;
accumulator = 0;
}
else
{
--subSample;
}
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LiveScrollingAudioDisplay);
};
#endif // __AUDIOLIVESCROLLINGDISPLAY_H_4C3BD3A7__
@@ -0,0 +1,424 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-12 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../JuceDemoHeader.h"
//==============================================================================
class DemoThumbnailComp : public Component,
public ChangeListener,
public FileDragAndDropTarget,
public ChangeBroadcaster,
private ScrollBar::Listener,
private Timer
{
public:
DemoThumbnailComp (AudioFormatManager& formatManager,
AudioTransportSource& transportSource_,
Slider& slider)
: transportSource (transportSource_),
zoomSlider (slider),
scrollbar (false),
thumbnailCache (5),
thumbnail (512, formatManager, thumbnailCache),
isFollowingTransport (false)
{
thumbnail.addChangeListener (this);
addAndMakeVisible (scrollbar);
scrollbar.setRangeLimits (visibleRange);
scrollbar.setAutoHide (false);
scrollbar.addListener (this);
currentPositionMarker.setFill (Colours::white.withAlpha (0.85f));
addAndMakeVisible (currentPositionMarker);
}
~DemoThumbnailComp()
{
scrollbar.removeListener (this);
thumbnail.removeChangeListener (this);
}
void setFile (const File& file)
{
if (! file.isDirectory())
{
thumbnail.setSource (new FileInputSource (file));
const Range<double> newRange (0.0, thumbnail.getTotalLength());
scrollbar.setRangeLimits (newRange);
setRange (newRange);
startTimerHz (40);
}
}
File getLastDroppedFile() const noexcept { return lastFileDropped; }
void setZoomFactor (double amount)
{
if (thumbnail.getTotalLength() > 0)
{
const double newScale = jmax (0.001, thumbnail.getTotalLength() * (1.0 - jlimit (0.0, 0.99, amount)));
const double timeAtCentre = xToTime (getWidth() / 2.0f);
setRange (Range<double> (timeAtCentre - newScale * 0.5, timeAtCentre + newScale * 0.5));
}
}
void setRange (Range<double> newRange)
{
visibleRange = newRange;
scrollbar.setCurrentRange (visibleRange);
updateCursorPosition();
repaint();
}
void setFollowsTransport (bool shouldFollow)
{
isFollowingTransport = shouldFollow;
}
void paint (Graphics& g) override
{
g.fillAll (Colours::darkgrey);
g.setColour (Colours::lightblue);
if (thumbnail.getTotalLength() > 0.0)
{
Rectangle<int> thumbArea (getLocalBounds());
thumbArea.removeFromBottom (scrollbar.getHeight() + 4);
thumbnail.drawChannels (g, thumbArea.reduced (2),
visibleRange.getStart(), visibleRange.getEnd(), 1.0f);
}
else
{
g.setFont (14.0f);
g.drawFittedText ("(No audio file selected)", getLocalBounds(), Justification::centred, 2);
}
}
void resized() override
{
scrollbar.setBounds (getLocalBounds().removeFromBottom (14).reduced (2));
}
void changeListenerCallback (ChangeBroadcaster*) override
{
// this method is called by the thumbnail when it has changed, so we should repaint it..
repaint();
}
bool isInterestedInFileDrag (const StringArray& /*files*/) override
{
return true;
}
void filesDropped (const StringArray& files, int /*x*/, int /*y*/) override
{
lastFileDropped = File (files[0]);
sendChangeMessage();
}
void mouseDown (const MouseEvent& e) override
{
mouseDrag (e);
}
void mouseDrag (const MouseEvent& e) override
{
if (canMoveTransport())
transportSource.setPosition (jmax (0.0, xToTime ((float) e.x)));
}
void mouseUp (const MouseEvent&) override
{
transportSource.start();
}
void mouseWheelMove (const MouseEvent&, const MouseWheelDetails& wheel) override
{
if (thumbnail.getTotalLength() > 0.0)
{
double newStart = visibleRange.getStart() - wheel.deltaX * (visibleRange.getLength()) / 10.0;
newStart = jlimit (0.0, jmax (0.0, thumbnail.getTotalLength() - (visibleRange.getLength())), newStart);
if (canMoveTransport())
setRange (Range<double> (newStart, newStart + visibleRange.getLength()));
if (wheel.deltaY != 0.0f)
zoomSlider.setValue (zoomSlider.getValue() - wheel.deltaY);
repaint();
}
}
private:
AudioTransportSource& transportSource;
Slider& zoomSlider;
ScrollBar scrollbar;
AudioThumbnailCache thumbnailCache;
AudioThumbnail thumbnail;
Range<double> visibleRange;
bool isFollowingTransport;
File lastFileDropped;
DrawableRectangle currentPositionMarker;
float timeToX (const double time) const
{
return getWidth() * (float) ((time - visibleRange.getStart()) / (visibleRange.getLength()));
}
double xToTime (const float x) const
{
return (x / getWidth()) * (visibleRange.getLength()) + visibleRange.getStart();
}
bool canMoveTransport() const noexcept
{
return ! (isFollowingTransport && transportSource.isPlaying());
}
void scrollBarMoved (ScrollBar* scrollBarThatHasMoved, double newRangeStart) override
{
if (scrollBarThatHasMoved == &scrollbar)
if (! (isFollowingTransport && transportSource.isPlaying()))
setRange (visibleRange.movedToStartAt (newRangeStart));
}
void timerCallback() override
{
if (canMoveTransport())
updateCursorPosition();
else
setRange (visibleRange.movedToStartAt (transportSource.getCurrentPosition() - (visibleRange.getLength() / 2.0)));
}
void updateCursorPosition()
{
currentPositionMarker.setVisible (transportSource.isPlaying() || isMouseButtonDown());
currentPositionMarker.setRectangle (Rectangle<float> (timeToX (transportSource.getCurrentPosition()) - 0.75f, 0,
1.5f, (float) (getHeight() - scrollbar.getHeight())));
}
};
//==============================================================================
class AudioPlaybackDemo : public Component,
private FileBrowserListener,
private Button::Listener,
private Slider::Listener,
private ChangeListener
{
public:
AudioPlaybackDemo()
: deviceManager (MainAppWindow::getSharedAudioDeviceManager()),
thread ("audio file preview"),
directoryList (nullptr, thread),
fileTreeComp (directoryList)
{
addAndMakeVisible (zoomLabel);
zoomLabel.setText ("zoom:", dontSendNotification);
zoomLabel.setFont (Font (15.00f, Font::plain));
zoomLabel.setJustificationType (Justification::centredRight);
zoomLabel.setEditable (false, false, false);
zoomLabel.setColour (TextEditor::textColourId, Colours::black);
zoomLabel.setColour (TextEditor::backgroundColourId, Colour (0x00000000));
addAndMakeVisible (followTransportButton);
followTransportButton.setButtonText ("Follow Transport");
followTransportButton.addListener (this);
addAndMakeVisible (explanation);
explanation.setText ("Select an audio file in the treeview above, and this page will display its waveform, and let you play it..", dontSendNotification);
explanation.setFont (Font (14.00f, Font::plain));
explanation.setJustificationType (Justification::bottomRight);
explanation.setEditable (false, false, false);
explanation.setColour (TextEditor::textColourId, Colours::black);
explanation.setColour (TextEditor::backgroundColourId, Colour (0x00000000));
addAndMakeVisible (zoomSlider);
zoomSlider.setRange (0, 1, 0);
zoomSlider.setSliderStyle (Slider::LinearHorizontal);
zoomSlider.setTextBoxStyle (Slider::NoTextBox, false, 80, 20);
zoomSlider.addListener (this);
zoomSlider.setSkewFactor (2);
addAndMakeVisible (thumbnail = new DemoThumbnailComp (formatManager, transportSource, zoomSlider));
thumbnail->addChangeListener (this);
addAndMakeVisible (startStopButton);
startStopButton.setButtonText ("Play/Stop");
startStopButton.addListener (this);
startStopButton.setColour (TextButton::buttonColourId, Colour (0xff79ed7f));
addAndMakeVisible (fileTreeComp);
// audio setup
formatManager.registerBasicFormats();
directoryList.setDirectory (File::getSpecialLocation (File::userHomeDirectory), true, true);
thread.startThread (3);
fileTreeComp.setColour (FileTreeComponent::backgroundColourId, Colours::lightgrey.withAlpha (0.6f));
fileTreeComp.addListener (this);
deviceManager.addAudioCallback (&audioSourcePlayer);
audioSourcePlayer.setSource (&transportSource);
setOpaque (true);
}
~AudioPlaybackDemo()
{
transportSource.setSource (nullptr);
audioSourcePlayer.setSource (nullptr);
deviceManager.removeAudioCallback (&audioSourcePlayer);
fileTreeComp.removeListener (this);
thumbnail->removeChangeListener (this);
followTransportButton.removeListener (this);
zoomSlider.removeListener (this);
}
void paint (Graphics& g) override
{
fillTiledBackground (g);
}
void resized() override
{
Rectangle<int> r (getLocalBounds().reduced (4));
Rectangle<int> controls (r.removeFromBottom (90));
explanation.setBounds (controls.removeFromRight (controls.getWidth() / 3));
Rectangle<int> zoom (controls.removeFromTop (25));
zoomLabel.setBounds (zoom.removeFromLeft (50));
zoomSlider.setBounds (zoom);
followTransportButton.setBounds (controls.removeFromTop (25));
startStopButton.setBounds (controls);
r.removeFromBottom (6);
thumbnail->setBounds (r.removeFromBottom (140));
r.removeFromBottom (6);
fileTreeComp.setBounds (r);
}
private:
AudioDeviceManager& deviceManager;
AudioFormatManager formatManager;
TimeSliceThread thread;
DirectoryContentsList directoryList;
AudioSourcePlayer audioSourcePlayer;
AudioTransportSource transportSource;
ScopedPointer<AudioFormatReaderSource> currentAudioFileSource;
ScopedPointer<DemoThumbnailComp> thumbnail;
Label zoomLabel, explanation;
Slider zoomSlider;
ToggleButton followTransportButton;
TextButton startStopButton;
FileTreeComponent fileTreeComp;
//==============================================================================
void showFile (const File& file)
{
loadFileIntoTransport (file);
zoomSlider.setValue (0, dontSendNotification);
thumbnail->setFile (file);
}
void loadFileIntoTransport (const File& audioFile)
{
// unload the previous file source and delete it..
transportSource.stop();
transportSource.setSource (nullptr);
currentAudioFileSource = nullptr;
AudioFormatReader* reader = formatManager.createReaderFor (audioFile);
if (reader != nullptr)
{
currentAudioFileSource = new AudioFormatReaderSource (reader, true);
// ..and plug it into our transport source
transportSource.setSource (currentAudioFileSource,
32768, // tells it to buffer this many samples ahead
&thread, // this is the background thread to use for reading-ahead
reader->sampleRate); // allows for sample rate correction
}
}
void selectionChanged() override
{
showFile (fileTreeComp.getSelectedFile());
}
void fileClicked (const File&, const MouseEvent&) override {}
void fileDoubleClicked (const File&) override {}
void browserRootChanged (const File&) override {}
void sliderValueChanged (Slider* sliderThatWasMoved) override
{
if (sliderThatWasMoved == &zoomSlider)
thumbnail->setZoomFactor (zoomSlider.getValue());
}
void buttonClicked (Button* buttonThatWasClicked) override
{
if (buttonThatWasClicked == &startStopButton)
{
if (transportSource.isPlaying())
{
transportSource.stop();
}
else
{
transportSource.setPosition (0);
transportSource.start();
}
}
else if (buttonThatWasClicked == &followTransportButton)
{
thumbnail->setFollowsTransport (followTransportButton.getToggleState());
}
}
void changeListenerCallback (ChangeBroadcaster* source) override
{
if (source == thumbnail)
showFile (thumbnail->getLastDroppedFile());
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioPlaybackDemo);
};
// This static object will register this demo type in a global list of demos..
static JuceDemoType<AudioPlaybackDemo> demo ("31 Audio: File Playback");
@@ -0,0 +1,304 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-12 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../JuceDemoHeader.h"
#include "AudioLiveScrollingDisplay.h"
//==============================================================================
/** A simple class that acts as an AudioIODeviceCallback and writes the
incoming audio data to a WAV file.
*/
class AudioRecorder : public AudioIODeviceCallback
{
public:
AudioRecorder (AudioThumbnail& thumbnailToUpdate)
: thumbnail (thumbnailToUpdate),
backgroundThread ("Audio Recorder Thread"),
sampleRate (0), nextSampleNum (0), activeWriter (nullptr)
{
backgroundThread.startThread();
}
~AudioRecorder()
{
stop();
}
//==============================================================================
void startRecording (const File& file)
{
stop();
if (sampleRate > 0)
{
// Create an OutputStream to write to our destination file...
file.deleteFile();
ScopedPointer<FileOutputStream> fileStream (file.createOutputStream());
if (fileStream != nullptr)
{
// Now create a WAV writer object that writes to our output stream...
WavAudioFormat wavFormat;
AudioFormatWriter* writer = wavFormat.createWriterFor (fileStream, sampleRate, 1, 16, StringPairArray(), 0);
if (writer != nullptr)
{
fileStream.release(); // (passes responsibility for deleting the stream to the writer object that is now using it)
// Now we'll create one of these helper objects which will act as a FIFO buffer, and will
// write the data to disk on our background thread.
threadedWriter = new AudioFormatWriter::ThreadedWriter (writer, backgroundThread, 32768);
// Reset our recording thumbnail
thumbnail.reset (writer->getNumChannels(), writer->getSampleRate());
nextSampleNum = 0;
// And now, swap over our active writer pointer so that the audio callback will start using it..
const ScopedLock sl (writerLock);
activeWriter = threadedWriter;
}
}
}
}
void stop()
{
// First, clear this pointer to stop the audio callback from using our writer object..
{
const ScopedLock sl (writerLock);
activeWriter = nullptr;
}
// Now we can delete the writer object. It's done in this order because the deletion could
// take a little time while remaining data gets flushed to disk, so it's best to avoid blocking
// the audio callback while this happens.
threadedWriter = nullptr;
}
bool isRecording() const
{
return activeWriter != nullptr;
}
//==============================================================================
void audioDeviceAboutToStart (AudioIODevice* device) override
{
sampleRate = device->getCurrentSampleRate();
}
void audioDeviceStopped() override
{
sampleRate = 0;
}
void audioDeviceIOCallback (const float** inputChannelData, int /*numInputChannels*/,
float** outputChannelData, int numOutputChannels,
int numSamples) override
{
const ScopedLock sl (writerLock);
if (activeWriter != nullptr)
{
activeWriter->write (inputChannelData, numSamples);
// Create an AudioSampleBuffer to wrap our incomming data, note that this does no allocations or copies, it simply references our input data
const AudioSampleBuffer buffer (const_cast<float**> (inputChannelData), thumbnail.getNumChannels(), numSamples);
thumbnail.addBlock (nextSampleNum, buffer, 0, numSamples);
nextSampleNum += numSamples;
}
// We need to clear the output buffers, in case they're full of junk..
for (int i = 0; i < numOutputChannels; ++i)
if (outputChannelData[i] != nullptr)
FloatVectorOperations::clear (outputChannelData[i], numSamples);
}
private:
AudioThumbnail& thumbnail;
TimeSliceThread backgroundThread; // the thread that will write our audio data to disk
ScopedPointer<AudioFormatWriter::ThreadedWriter> threadedWriter; // the FIFO used to buffer the incoming data
double sampleRate;
int64 nextSampleNum;
CriticalSection writerLock;
AudioFormatWriter::ThreadedWriter* volatile activeWriter;
};
//==============================================================================
class RecordingThumbnail : public Component,
private ChangeListener
{
public:
RecordingThumbnail()
: thumbnailCache (10),
thumbnail (512, formatManager, thumbnailCache),
displayFullThumb (false)
{
formatManager.registerBasicFormats();
thumbnail.addChangeListener (this);
}
~RecordingThumbnail()
{
thumbnail.removeChangeListener (this);
}
AudioThumbnail& getAudioThumbnail() { return thumbnail; }
void setDisplayFullThumbnail (bool displayFull)
{
displayFullThumb = displayFull;
repaint();
}
void paint (Graphics& g) override
{
g.fillAll (Colours::darkgrey);
g.setColour (Colours::lightgrey);
if (thumbnail.getTotalLength() > 0.0)
{
const double endTime = displayFullThumb ? thumbnail.getTotalLength()
: jmax (30.0, thumbnail.getTotalLength());
Rectangle<int> thumbArea (getLocalBounds());
thumbnail.drawChannels (g, thumbArea.reduced (2), 0.0, endTime, 1.0f);
}
else
{
g.setFont (14.0f);
g.drawFittedText ("(No file recorded)", getLocalBounds(), Justification::centred, 2);
}
}
private:
AudioFormatManager formatManager;
AudioThumbnailCache thumbnailCache;
AudioThumbnail thumbnail;
bool displayFullThumb;
void changeListenerCallback (ChangeBroadcaster* source) override
{
if (source == &thumbnail)
repaint();
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RecordingThumbnail)
};
//==============================================================================
class AudioRecordingDemo : public Component,
private Button::Listener
{
public:
AudioRecordingDemo()
: deviceManager (MainAppWindow::getSharedAudioDeviceManager()),
recorder (recordingThumbnail.getAudioThumbnail())
{
setOpaque (true);
addAndMakeVisible (liveAudioScroller);
addAndMakeVisible (explanationLabel);
explanationLabel.setText ("This page demonstrates how to record a wave file from the live audio input..\n\nPressing record will start recording a file in your \"Documents\" folder.", dontSendNotification);
explanationLabel.setFont (Font (15.00f, Font::plain));
explanationLabel.setJustificationType (Justification::topLeft);
explanationLabel.setEditable (false, false, false);
explanationLabel.setColour (TextEditor::textColourId, Colours::black);
explanationLabel.setColour (TextEditor::backgroundColourId, Colour (0x00000000));
addAndMakeVisible (recordButton);
recordButton.setButtonText ("Record");
recordButton.addListener (this);
recordButton.setColour (TextButton::buttonColourId, Colour (0xffff5c5c));
recordButton.setColour (TextButton::textColourOnId, Colours::black);
addAndMakeVisible (recordingThumbnail);
deviceManager.addAudioCallback (&liveAudioScroller);
deviceManager.addAudioCallback (&recorder);
}
~AudioRecordingDemo()
{
deviceManager.removeAudioCallback (&recorder);
deviceManager.removeAudioCallback (&liveAudioScroller);
}
void paint (Graphics& g) override
{
fillTiledBackground (g);
}
void resized() override
{
Rectangle<int> area (getLocalBounds());
liveAudioScroller.setBounds (area.removeFromTop (80).reduced (8));
recordingThumbnail.setBounds (area.removeFromTop (80).reduced (8));
recordButton.setBounds (area.removeFromTop (36).removeFromLeft (140).reduced (8));
explanationLabel.setBounds (area.reduced (8));
}
private:
AudioDeviceManager& deviceManager;
LiveScrollingAudioDisplay liveAudioScroller;
RecordingThumbnail recordingThumbnail;
AudioRecorder recorder;
Label explanationLabel;
TextButton recordButton;
void startRecording()
{
const File file (File::getSpecialLocation (File::userDocumentsDirectory)
.getNonexistentChildFile ("Juce Demo Audio Recording", ".wav"));
recorder.startRecording (file);
recordButton.setButtonText ("Stop");
recordingThumbnail.setDisplayFullThumbnail (false);
}
void stopRecording()
{
recorder.stop();
recordButton.setButtonText ("Record");
recordingThumbnail.setDisplayFullThumbnail (true);
}
void buttonClicked (Button* button) override
{
if (button == &recordButton)
{
if (recorder.isRecording())
stopRecording();
else
startRecording();
}
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioRecordingDemo)
};
// This static object will register this demo type in a global list of demos..
static JuceDemoType<AudioRecordingDemo> demo ("31 Audio: Recording");
@@ -0,0 +1,132 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-12 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../JuceDemoHeader.h"
//==============================================================================
class AudioSettingsDemo : public Component,
public ChangeListener
{
public:
AudioSettingsDemo()
{
setOpaque (true);
addAndMakeVisible (audioSetupComp
= new AudioDeviceSelectorComponent (MainAppWindow::getSharedAudioDeviceManager(),
0, 256, 0, 256, true, true, true, false));
addAndMakeVisible (diagnosticsBox);
diagnosticsBox.setMultiLine (true);
diagnosticsBox.setReturnKeyStartsNewLine (true);
diagnosticsBox.setReadOnly (true);
diagnosticsBox.setScrollbarsShown (true);
diagnosticsBox.setCaretVisible (false);
diagnosticsBox.setPopupMenuEnabled (true);
diagnosticsBox.setColour (TextEditor::backgroundColourId, Colour (0x32ffffff));
diagnosticsBox.setColour (TextEditor::outlineColourId, Colour (0x1c000000));
diagnosticsBox.setColour (TextEditor::shadowColourId, Colour (0x16000000));
MainAppWindow::getSharedAudioDeviceManager().addChangeListener (this);
logMessage ("Audio device diagnostics:\n");
dumpDeviceInfo();
}
~AudioSettingsDemo()
{
MainAppWindow::getSharedAudioDeviceManager().removeChangeListener (this);
}
void paint (Graphics& g) override
{
fillTiledBackground (g);
}
void resized() override
{
Rectangle<int> r (getLocalBounds().reduced (4));
audioSetupComp->setBounds (r.removeFromTop (proportionOfHeight (0.65f)));
diagnosticsBox.setBounds (r);
}
void dumpDeviceInfo()
{
AudioDeviceManager& dm = MainAppWindow::getSharedAudioDeviceManager();
logMessage ("--------------------------------------");
logMessage ("Current audio device type: " + (dm.getCurrentDeviceTypeObject() != nullptr
? dm.getCurrentDeviceTypeObject()->getTypeName()
: "<none>"));
if (AudioIODevice* device = dm.getCurrentAudioDevice())
{
logMessage ("Current audio device: " + device->getName().quoted());
logMessage ("Sample rate: " + String (device->getCurrentSampleRate()) + " Hz");
logMessage ("Block size: " + String (device->getCurrentBufferSizeSamples()) + " samples");
logMessage ("Bit depth: " + String (device->getCurrentBitDepth()));
logMessage ("Input channel names: " + device->getInputChannelNames().joinIntoString (", "));
logMessage ("Active input channels: " + getListOfActiveBits (device->getActiveInputChannels()));
logMessage ("Output channel names: " + device->getOutputChannelNames().joinIntoString (", "));
logMessage ("Active output channels: " + getListOfActiveBits (device->getActiveOutputChannels()));
}
else
{
logMessage ("No audio device open");
}
}
void logMessage (const String& m)
{
diagnosticsBox.moveCaretToEnd();
diagnosticsBox.insertTextAtCaret (m + newLine);
}
private:
ScopedPointer<AudioDeviceSelectorComponent> audioSetupComp;
TextEditor diagnosticsBox;
void changeListenerCallback (ChangeBroadcaster*) override
{
dumpDeviceInfo();
}
static String getListOfActiveBits (const BitArray& b)
{
StringArray bits;
for (int i = 0; i <= b.getHighestBit(); ++i)
if (b[i])
bits.add (String (i));
return bits.joinIntoString (", ");
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSettingsDemo);
};
// This static object will register this demo type in a global list of demos..
static JuceDemoType<AudioSettingsDemo> demo ("30 Audio: Settings");
@@ -0,0 +1,316 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-12 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../JuceDemoHeader.h"
#include "AudioLiveScrollingDisplay.h"
//==============================================================================
/** Our demo synth sound is just a basic sine wave.. */
struct SineWaveSound : public SynthesiserSound
{
SineWaveSound() {}
bool appliesToNote (int /*midiNoteNumber*/) override { return true; }
bool appliesToChannel (int /*midiChannel*/) override { return true; }
};
//==============================================================================
/** Our demo synth voice just plays a sine wave.. */
struct SineWaveVoice : public SynthesiserVoice
{
SineWaveVoice() : currentAngle (0), angleDelta (0), level (0), tailOff (0)
{
}
bool canPlaySound (SynthesiserSound* sound) override
{
return dynamic_cast<SineWaveSound*> (sound) != nullptr;
}
void startNote (int midiNoteNumber, float velocity,
SynthesiserSound*, int /*currentPitchWheelPosition*/) override
{
currentAngle = 0.0;
level = velocity * 0.15;
tailOff = 0.0;
double cyclesPerSecond = MidiMessage::getMidiNoteInHertz (midiNoteNumber);
double cyclesPerSample = cyclesPerSecond / getSampleRate();
angleDelta = cyclesPerSample * 2.0 * double_Pi;
}
void stopNote (float /*velocity*/, bool allowTailOff) override
{
if (allowTailOff)
{
// start a tail-off by setting this flag. The render callback will pick up on
// this and do a fade out, calling clearCurrentNote() when it's finished.
if (tailOff == 0.0) // we only need to begin a tail-off if it's not already doing so - the
// stopNote method could be called more than once.
tailOff = 1.0;
}
else
{
// we're being told to stop playing immediately, so reset everything..
clearCurrentNote();
angleDelta = 0.0;
}
}
void pitchWheelMoved (int /*newValue*/) override
{
// can't be bothered implementing this for the demo!
}
void controllerMoved (int /*controllerNumber*/, int /*newValue*/) override
{
// not interested in controllers in this case.
}
void renderNextBlock (AudioSampleBuffer& outputBuffer, int startSample, int numSamples) override
{
if (angleDelta != 0.0)
{
if (tailOff > 0)
{
while (--numSamples >= 0)
{
const float currentSample = (float) (std::sin (currentAngle) * level * tailOff);
for (int i = outputBuffer.getNumChannels(); --i >= 0;)
outputBuffer.addSample (i, startSample, currentSample);
currentAngle += angleDelta;
++startSample;
tailOff *= 0.99;
if (tailOff <= 0.005)
{
clearCurrentNote();
angleDelta = 0.0;
break;
}
}
}
else
{
while (--numSamples >= 0)
{
const float currentSample = (float) (std::sin (currentAngle) * level);
for (int i = outputBuffer.getNumChannels(); --i >= 0;)
outputBuffer.addSample (i, startSample, currentSample);
currentAngle += angleDelta;
++startSample;
}
}
}
}
private:
double currentAngle, angleDelta, level, tailOff;
};
//==============================================================================
// This is an audio source that streams the output of our demo synth.
struct SynthAudioSource : public AudioSource
{
SynthAudioSource (MidiKeyboardState& keyState) : keyboardState (keyState)
{
// Add some voices to our synth, to play the sounds..
for (int i = 4; --i >= 0;)
{
synth.addVoice (new SineWaveVoice()); // These voices will play our custom sine-wave sounds..
synth.addVoice (new SamplerVoice()); // and these ones play the sampled sounds
}
// ..and add a sound for them to play...
setUsingSineWaveSound();
}
void setUsingSineWaveSound()
{
synth.clearSounds();
synth.addSound (new SineWaveSound());
}
void setUsingSampledSound()
{
WavAudioFormat wavFormat;
ScopedPointer<AudioFormatReader> audioReader (wavFormat.createReaderFor (new MemoryInputStream (BinaryData::cello_wav,
BinaryData::cello_wavSize,
false),
true));
BigInteger allNotes;
allNotes.setRange (0, 128, true);
synth.clearSounds();
synth.addSound (new SamplerSound ("demo sound",
*audioReader,
allNotes,
74, // root midi note
0.1, // attack time
0.1, // release time
10.0 // maximum sample length
));
}
void prepareToPlay (int /*samplesPerBlockExpected*/, double sampleRate) override
{
midiCollector.reset (sampleRate);
synth.setCurrentPlaybackSampleRate (sampleRate);
}
void releaseResources() override
{
}
void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) override
{
// the synth always adds its output to the audio buffer, so we have to clear it
// first..
bufferToFill.clearActiveBufferRegion();
// fill a midi buffer with incoming messages from the midi input.
MidiBuffer incomingMidi;
midiCollector.removeNextBlockOfMessages (incomingMidi, bufferToFill.numSamples);
// pass these messages to the keyboard state so that it can update the component
// to show on-screen which keys are being pressed on the physical midi keyboard.
// This call will also add midi messages to the buffer which were generated by
// the mouse-clicking on the on-screen keyboard.
keyboardState.processNextMidiBuffer (incomingMidi, 0, bufferToFill.numSamples, true);
// and now get the synth to process the midi events and generate its output.
synth.renderNextBlock (*bufferToFill.buffer, incomingMidi, 0, bufferToFill.numSamples);
}
//==============================================================================
// this collects real-time midi messages from the midi input device, and
// turns them into blocks that we can process in our audio callback
MidiMessageCollector midiCollector;
// this represents the state of which keys on our on-screen keyboard are held
// down. When the mouse is clicked on the keyboard component, this object also
// generates midi messages for this, which we can pass on to our synth.
MidiKeyboardState& keyboardState;
// the synth itself!
Synthesiser synth;
};
//==============================================================================
class AudioSynthesiserDemo : public Component,
private Button::Listener
{
public:
AudioSynthesiserDemo()
: deviceManager (MainAppWindow::getSharedAudioDeviceManager()),
synthAudioSource (keyboardState),
keyboardComponent (keyboardState, MidiKeyboardComponent::horizontalKeyboard)
{
addAndMakeVisible (keyboardComponent);
addAndMakeVisible (sineButton);
sineButton.setButtonText ("Use sine wave");
sineButton.setRadioGroupId (321);
sineButton.addListener (this);
sineButton.setToggleState (true, dontSendNotification);
addAndMakeVisible (sampledButton);
sampledButton.setButtonText ("Use sampled sound");
sampledButton.setRadioGroupId (321);
sampledButton.addListener (this);
addAndMakeVisible (liveAudioDisplayComp);
deviceManager.addAudioCallback (&liveAudioDisplayComp);
audioSourcePlayer.setSource (&synthAudioSource);
deviceManager.addAudioCallback (&audioSourcePlayer);
deviceManager.addMidiInputCallback (String::empty, &(synthAudioSource.midiCollector));
setOpaque (true);
setSize (640, 480);
}
~AudioSynthesiserDemo()
{
audioSourcePlayer.setSource (nullptr);
deviceManager.removeMidiInputCallback (String::empty, &(synthAudioSource.midiCollector));
deviceManager.removeAudioCallback (&audioSourcePlayer);
deviceManager.removeAudioCallback (&liveAudioDisplayComp);
}
//==============================================================================
void paint (Graphics& g) override
{
fillTiledBackground (g);
}
void resized() override
{
keyboardComponent.setBounds (8, 96, getWidth() - 16, 64);
sineButton.setBounds (16, 176, 150, 24);
sampledButton.setBounds (16, 200, 150, 24);
liveAudioDisplayComp.setBounds (8, 8, getWidth() - 16, 64);
}
private:
AudioDeviceManager& deviceManager;
MidiKeyboardState keyboardState;
AudioSourcePlayer audioSourcePlayer;
SynthAudioSource synthAudioSource;
MidiKeyboardComponent keyboardComponent;
ToggleButton sineButton;
ToggleButton sampledButton;
LiveScrollingAudioDisplay liveAudioDisplayComp;
//==============================================================================
void buttonClicked (Button* buttonThatWasClicked) override
{
if (buttonThatWasClicked == &sineButton)
synthAudioSource.setUsingSineWaveSound();
else if (buttonThatWasClicked == &sampledButton)
synthAudioSource.setUsingSampledSound();
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSynthesiserDemo)
};
// This static object will register this demo type in a global list of demos..
static JuceDemoType<AudioSynthesiserDemo> demo ("31 Audio: Synthesisers");
@@ -0,0 +1,338 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-12 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../JuceDemoHeader.h"
// (These classes and random functions are used inside the 3rd-party Box2D demo code)
inline float32 RandomFloat() { return Random::getSystemRandom().nextFloat() * 2.0f - 1.0f; }
inline float32 RandomFloat (float32 lo, float32 hi) { return Random::getSystemRandom().nextFloat() * (hi - lo) + lo; }
struct Settings
{
Settings()
: viewCenter (0.0f, 20.0f),
hz (60.0f),
velocityIterations (8),
positionIterations (3),
drawShapes (1),
drawJoints (1),
drawAABBs (0),
drawPairs (0),
drawContactPoints (0),
drawContactNormals (0),
drawContactForces (0),
drawFrictionForces (0),
drawCOMs (0),
drawStats (0),
drawProfile (0),
enableWarmStarting (1),
enableContinuous (1),
enableSubStepping (0),
pause (0),
singleStep (0)
{}
b2Vec2 viewCenter;
float32 hz;
int velocityIterations;
int positionIterations;
int drawShapes;
int drawJoints;
int drawAABBs;
int drawPairs;
int drawContactPoints;
int drawContactNormals;
int drawContactForces;
int drawFrictionForces;
int drawCOMs;
int drawStats;
int drawProfile;
int enableWarmStarting;
int enableContinuous;
int enableSubStepping;
int pause;
int singleStep;
};
struct Test
{
Test() : m_world (new b2World (b2Vec2 (0.0f, -10.0f))) {}
virtual ~Test() {}
virtual void Keyboard (unsigned char /*key*/) {}
virtual void KeyboardUp (unsigned char /*key*/) {}
ScopedPointer<b2World> m_world;
};
#include "Box2DTests/AddPair.h"
#include "Box2DTests/ApplyForce.h"
#include "Box2DTests/Dominos.h"
#include "Box2DTests/Chain.h"
//==============================================================================
/** This list box just displays a StringArray and broadcasts a change message when the
selected row changes.
*/
class Box2DTestList : public ListBoxModel,
public ChangeBroadcaster
{
public:
Box2DTestList (const StringArray& testList) : tests (testList)
{
}
int getNumRows() override { return tests.size(); }
void paintListBoxItem (int row, Graphics& g, int w, int h, bool rowIsSelected) override
{
if (rowIsSelected)
g.fillAll (LookAndFeel::getDefaultLookAndFeel().findColour (TextEditor::highlightColourId));
const Font f (h * 0.7f);
g.setColour (Colours::black);
g.setFont (f);
g.drawText (tests[row], Rectangle<int> (0, 0, w, h).reduced (2),
Justification::centredLeft, true);
}
void selectedRowsChanged (int /*lastRowSelected*/) override
{
sendChangeMessage();
}
private:
StringArray tests;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Box2DTestList)
};
//==============================================================================
struct Box2DRenderComponent : public Component
{
Box2DRenderComponent()
{
setOpaque (true);
}
void paint (Graphics& g)
{
g.fillAll (Colours::white);
if (currentTest != nullptr)
{
Box2DRenderer renderer;
renderer.render (g, *currentTest->m_world,
-16.0f, 30.0f, 16.0f, -1.0f,
getLocalBounds().toFloat().reduced (8.0f));
}
}
ScopedPointer<Test> currentTest;
};
//==============================================================================
class Box2DDemo : public Component,
private Timer,
private ChangeListener
{
public:
enum Demos
{
addPair = 0,
applyForce,
dominoes,
chain,
numTests
};
Box2DDemo()
: testsList (getTestsList()),
testsListModel (testsList)
{
setOpaque (true);
setWantsKeyboardFocus (true);
testsListModel.addChangeListener (this);
addAndMakeVisible (renderComponent);
addAndMakeVisible (testsListBox);
testsListBox.setModel (&testsListModel);
testsListBox.selectRow (dominoes);
testsListBox.setColour (ListBox::backgroundColourId, Colours::lightgrey);
addAndMakeVisible (instructions);
instructions.setMultiLine (true);
instructions.setReadOnly (true);
instructions.setColour (TextEditor::backgroundColourId, Colours::lightgrey);
startTimerHz (60);
}
~Box2DDemo()
{
testsListModel.removeChangeListener (this);
}
void paint (Graphics& g) override
{
fillTiledBackground (g);
}
void resized() override
{
Rectangle<int> r (getLocalBounds().reduced (4));
Rectangle<int> area (r.removeFromBottom (150));
testsListBox.setBounds (area.removeFromLeft (150));
area.removeFromLeft (4);
instructions.setBounds (area);
r.removeFromBottom (6);
renderComponent.setBounds (r);
}
bool keyPressed (const KeyPress& key) override
{
if (renderComponent.currentTest != nullptr)
{
// We override this to avoid the system beeping for an unused keypress
switch (key.getTextCharacter())
{
case 'a':
case 'w':
case 'd':
return true;
default:
break;
}
}
return false;
}
private:
StringArray testsList;
Box2DTestList testsListModel;
Box2DRenderComponent renderComponent;
ListBox testsListBox;
TextEditor instructions;
static Test* createTest (int index)
{
switch (index)
{
case addPair: return new AddPair();
case applyForce: return new ApplyForce();
case dominoes: return new Dominos();
case chain: return new Chain();
default: break;
}
return nullptr;
}
static String getInstructions (int index)
{
switch (index)
{
case applyForce:
{
String s;
s << "Keys:" << newLine << newLine << "Left: \'a\'" << newLine << "Right: \'d\'" << newLine << "Forward: \'w\'";
return s;
}
default:
break;
}
return String::empty;
}
void checkKeys()
{
if (renderComponent.currentTest == nullptr)
return;
checkKeyCode ('a');
checkKeyCode ('w');
checkKeyCode ('d');
}
void checkKeyCode (const int keyCode)
{
if (KeyPress::isKeyCurrentlyDown (keyCode))
renderComponent.currentTest->Keyboard ((unsigned char) keyCode);
}
void timerCallback() override
{
if (renderComponent.currentTest == nullptr)
return;
grabKeyboardFocus();
checkKeys();
renderComponent.currentTest->m_world->Step (1.0f / 60.0f, 6, 2);
repaint();
}
void changeListenerCallback (ChangeBroadcaster* source) override
{
if (source == &testsListModel)
{
const int index = testsListBox.getSelectedRow();
renderComponent.currentTest = createTest (index);
instructions.setText (getInstructions (index));
repaint();
}
}
static StringArray getTestsList()
{
const char* tests[] =
{
"Add Pair Stress Test",
"Apply Force",
"Dominoes",
"Chain"
};
jassert (numElementsInArray (tests) == numTests);
return StringArray (tests, numElementsInArray (tests));
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Box2DDemo)
};
// This static object will register this demo type in a global list of demos..
static JuceDemoType<Box2DDemo> demo ("29 Graphics: Box 2D");
@@ -0,0 +1,51 @@
#ifndef AddPair_H
#define AddPair_H
class AddPair : public Test
{
public:
AddPair()
{
m_world->SetGravity(b2Vec2(0.0f,0.0f));
{
b2CircleShape shape;
shape.m_p.SetZero();
shape.m_radius = 0.1f;
float minX = -6.0f;
float maxX = 0.0f;
float minY = 4.0f;
float maxY = 6.0f;
for (int i = 0; i < 400; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = b2Vec2(RandomFloat(minX,maxX),RandomFloat(minY,maxY));
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 0.01f);
}
}
{
b2PolygonShape shape;
shape.SetAsBox(1.5f, 1.5f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-40.0f,5.0f);
bd.bullet = true;
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 1.0f);
body->SetLinearVelocity(b2Vec2(150.0f, 0.0f));
}
}
static Test* Create()
{
return new AddPair;
}
};
#endif
@@ -0,0 +1,180 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef APPLY_FORCE_H
#define APPLY_FORCE_H
class ApplyForce : public Test
{
public:
ApplyForce()
{
m_world->SetGravity(b2Vec2(0.0f, 0.0f));
const float32 k_restitution = 0.4f;
b2Body* ground;
{
b2BodyDef bd;
bd.position.Set(0.0f, 20.0f);
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
b2FixtureDef sd;
sd.shape = &shape;
sd.density = 0.0f;
sd.restitution = k_restitution;
// Left vertical
shape.Set(b2Vec2(-20.0f, -20.0f), b2Vec2(-20.0f, 20.0f));
ground->CreateFixture(&sd);
// Right vertical
shape.Set(b2Vec2(20.0f, -20.0f), b2Vec2(20.0f, 20.0f));
ground->CreateFixture(&sd);
// Top horizontal
shape.Set(b2Vec2(-20.0f, 20.0f), b2Vec2(20.0f, 20.0f));
ground->CreateFixture(&sd);
// Bottom horizontal
shape.Set(b2Vec2(-20.0f, -20.0f), b2Vec2(20.0f, -20.0f));
ground->CreateFixture(&sd);
}
{
b2Transform xf1;
xf1.q.Set(0.3524f * b2_pi);
xf1.p = xf1.q.GetXAxis();
b2Vec2 vertices[3];
vertices[0] = b2Mul(xf1, b2Vec2(-1.0f, 0.0f));
vertices[1] = b2Mul(xf1, b2Vec2(1.0f, 0.0f));
vertices[2] = b2Mul(xf1, b2Vec2(0.0f, 0.5f));
b2PolygonShape poly1;
poly1.Set(vertices, 3);
b2FixtureDef sd1;
sd1.shape = &poly1;
sd1.density = 4.0f;
b2Transform xf2;
xf2.q.Set(-0.3524f * b2_pi);
xf2.p = -xf2.q.GetXAxis();
vertices[0] = b2Mul(xf2, b2Vec2(-1.0f, 0.0f));
vertices[1] = b2Mul(xf2, b2Vec2(1.0f, 0.0f));
vertices[2] = b2Mul(xf2, b2Vec2(0.0f, 0.5f));
b2PolygonShape poly2;
poly2.Set(vertices, 3);
b2FixtureDef sd2;
sd2.shape = &poly2;
sd2.density = 2.0f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.angularDamping = 5.0f;
bd.linearDamping = 0.1f;
bd.position.Set(0.0f, 2.0f);
bd.angle = b2_pi;
bd.allowSleep = false;
m_body = m_world->CreateBody(&bd);
m_body->CreateFixture(&sd1);
m_body->CreateFixture(&sd2);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.5f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
fd.friction = 0.3f;
for (int i = 0; i < 10; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 5.0f + 1.54f * i);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
float32 gravity = 10.0f;
float32 I = body->GetInertia();
float32 mass = body->GetMass();
// For a circle: I = 0.5 * m * r * r ==> r = sqrt(2 * I / m)
float32 radius = b2Sqrt(2.0f * I / mass);
b2FrictionJointDef jd;
jd.localAnchorA.SetZero();
jd.localAnchorB.SetZero();
jd.bodyA = ground;
jd.bodyB = body;
jd.collideConnected = true;
jd.maxForce = mass * gravity;
jd.maxTorque = mass * radius * gravity;
m_world->CreateJoint(&jd);
}
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'w':
{
b2Vec2 f = m_body->GetWorldVector(b2Vec2(0.0f, -200.0f));
b2Vec2 p = m_body->GetWorldPoint(b2Vec2(0.0f, 2.0f));
m_body->ApplyForce(f, p);
}
break;
case 'a':
{
m_body->ApplyTorque(50.0f);
}
break;
case 'd':
{
m_body->ApplyTorque(-50.0f);
}
break;
}
}
static Test* Create()
{
return new ApplyForce;
}
b2Body* m_body;
};
#endif
@@ -0,0 +1,159 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef BODY_TYPES_H
#define BODY_TYPES_H
class BodyTypes : public Test
{
public:
BodyTypes()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-20.0f, 0.0f), b2Vec2(20.0f, 0.0f));
b2FixtureDef fd;
fd.shape = &shape;
ground->CreateFixture(&fd);
}
// Define attachment
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 3.0f);
m_attachment = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.5f, 2.0f);
m_attachment->CreateFixture(&shape, 2.0f);
}
// Define platform
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-4.0f, 5.0f);
m_platform = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.5f, 4.0f, b2Vec2(4.0f, 0.0f), 0.5f * b2_pi);
b2FixtureDef fd;
fd.shape = &shape;
fd.friction = 0.6f;
fd.density = 2.0f;
m_platform->CreateFixture(&fd);
b2RevoluteJointDef rjd;
rjd.Initialize(m_attachment, m_platform, b2Vec2(0.0f, 5.0f));
rjd.maxMotorTorque = 50.0f;
rjd.enableMotor = true;
m_world->CreateJoint(&rjd);
b2PrismaticJointDef pjd;
pjd.Initialize(ground, m_platform, b2Vec2(0.0f, 5.0f), b2Vec2(1.0f, 0.0f));
pjd.maxMotorForce = 1000.0f;
pjd.enableMotor = true;
pjd.lowerTranslation = -10.0f;
pjd.upperTranslation = 10.0f;
pjd.enableLimit = true;
m_world->CreateJoint(&pjd);
m_speed = 3.0f;
}
// Create a payload
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 8.0f);
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.75f, 0.75f);
b2FixtureDef fd;
fd.shape = &shape;
fd.friction = 0.6f;
fd.density = 2.0f;
body->CreateFixture(&fd);
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'd':
m_platform->SetType(b2_dynamicBody);
break;
case 's':
m_platform->SetType(b2_staticBody);
break;
case 'k':
m_platform->SetType(b2_kinematicBody);
m_platform->SetLinearVelocity(b2Vec2(-m_speed, 0.0f));
m_platform->SetAngularVelocity(0.0f);
break;
}
}
void Step(Settings* settings)
{
// Drive the kinematic body.
if (m_platform->GetType() == b2_kinematicBody)
{
b2Vec2 p = m_platform->GetTransform().p;
b2Vec2 v = m_platform->GetLinearVelocity();
if ((p.x < -10.0f && v.x < 0.0f) ||
(p.x > 10.0f && v.x > 0.0f))
{
v.x = -v.x;
m_platform->SetLinearVelocity(v);
}
}
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "Keys: (d) dynamic, (s) static, (k) kinematic");
m_textLine += 15;
}
static Test* Create()
{
return new BodyTypes;
}
b2Body* m_attachment;
b2Body* m_platform;
float32 m_speed;
};
#endif
@@ -0,0 +1,155 @@
/*
* Copyright (c) 2008-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef BREAKABLE_TEST_H
#define BREAKABLE_TEST_H
// This is used to test sensor shapes.
class Breakable : public Test
{
public:
enum
{
e_count = 7
};
Breakable()
{
// Ground body
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
// Breakable dynamic body
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 40.0f);
bd.angle = 0.25f * b2_pi;
m_body1 = m_world->CreateBody(&bd);
m_shape1.SetAsBox(0.5f, 0.5f, b2Vec2(-0.5f, 0.0f), 0.0f);
m_piece1 = m_body1->CreateFixture(&m_shape1, 1.0f);
m_shape2.SetAsBox(0.5f, 0.5f, b2Vec2(0.5f, 0.0f), 0.0f);
m_piece2 = m_body1->CreateFixture(&m_shape2, 1.0f);
}
m_break = false;
m_broke = false;
}
void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse)
{
if (m_broke)
{
// The body already broke.
return;
}
// Should the body break?
int32 count = contact->GetManifold()->pointCount;
float32 maxImpulse = 0.0f;
for (int32 i = 0; i < count; ++i)
{
maxImpulse = b2Max(maxImpulse, impulse->normalImpulses[i]);
}
if (maxImpulse > 40.0f)
{
// Flag the body for breaking.
m_break = true;
}
}
void Break()
{
// Create two bodies from one.
b2Body* body1 = m_piece1->GetBody();
b2Vec2 center = body1->GetWorldCenter();
body1->DestroyFixture(m_piece2);
m_piece2 = NULL;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = body1->GetPosition();
bd.angle = body1->GetAngle();
b2Body* body2 = m_world->CreateBody(&bd);
m_piece2 = body2->CreateFixture(&m_shape2, 1.0f);
// Compute consistent velocities for new bodies based on
// cached velocity.
b2Vec2 center1 = body1->GetWorldCenter();
b2Vec2 center2 = body2->GetWorldCenter();
b2Vec2 velocity1 = m_velocity + b2Cross(m_angularVelocity, center1 - center);
b2Vec2 velocity2 = m_velocity + b2Cross(m_angularVelocity, center2 - center);
body1->SetAngularVelocity(m_angularVelocity);
body1->SetLinearVelocity(velocity1);
body2->SetAngularVelocity(m_angularVelocity);
body2->SetLinearVelocity(velocity2);
}
void Step(Settings* settings)
{
if (m_break)
{
Break();
m_broke = true;
m_break = false;
}
// Cache velocities to improve movement on breakage.
if (m_broke == false)
{
m_velocity = m_body1->GetLinearVelocity();
m_angularVelocity = m_body1->GetAngularVelocity();
}
Test::Step(settings);
}
static Test* Create()
{
return new Breakable;
}
b2Body* m_body1;
b2Vec2 m_velocity;
float32 m_angularVelocity;
b2PolygonShape m_shape1;
b2PolygonShape m_shape2;
b2Fixture* m_piece1;
b2Fixture* m_piece2;
bool m_broke;
bool m_break;
};
#endif
@@ -0,0 +1,125 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef BRIDGE_H
#define BRIDGE_H
class Bridge : public Test
{
public:
enum
{
e_count = 30
};
Bridge()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
fd.friction = 0.2f;
b2RevoluteJointDef jd;
b2Body* prevBody = ground;
for (int32 i = 0; i < e_count; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-14.5f + 1.0f * i, 5.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
b2Vec2 anchor(-15.0f + 1.0f * i, 5.0f);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
if (i == (e_count >> 1))
{
m_middle = body;
}
prevBody = body;
}
b2Vec2 anchor(-15.0f + 1.0f * e_count, 5.0f);
jd.Initialize(prevBody, ground, anchor);
m_world->CreateJoint(&jd);
}
for (int32 i = 0; i < 2; ++i)
{
b2Vec2 vertices[3];
vertices[0].Set(-0.5f, 0.0f);
vertices[1].Set(0.5f, 0.0f);
vertices[2].Set(0.0f, 1.5f);
b2PolygonShape shape;
shape.Set(vertices, 3);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-8.0f + 8.0f * i, 12.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
for (int32 i = 0; i < 3; ++i)
{
b2CircleShape shape;
shape.m_radius = 0.5f;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-6.0f + 6.0f * i, 10.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
}
static Test* Create()
{
return new Bridge;
}
b2Body* m_middle;
};
#endif
@@ -0,0 +1,136 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef BULLET_TEST_H
#define BULLET_TEST_H
class BulletTest : public Test
{
public:
BulletTest()
{
{
b2BodyDef bd;
bd.position.Set(0.0f, 0.0f);
b2Body* body = m_world->CreateBody(&bd);
b2EdgeShape edge;
edge.Set(b2Vec2(-10.0f, 0.0f), b2Vec2(10.0f, 0.0f));
body->CreateFixture(&edge, 0.0f);
b2PolygonShape shape;
shape.SetAsBox(0.2f, 1.0f, b2Vec2(0.5f, 1.0f), 0.0f);
body->CreateFixture(&shape, 0.0f);
}
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 4.0f);
b2PolygonShape box;
box.SetAsBox(2.0f, 0.1f);
m_body = m_world->CreateBody(&bd);
m_body->CreateFixture(&box, 1.0f);
box.SetAsBox(0.25f, 0.25f);
//m_x = RandomFloat(-1.0f, 1.0f);
m_x = 0.20352793f;
bd.position.Set(m_x, 10.0f);
bd.bullet = true;
m_bullet = m_world->CreateBody(&bd);
m_bullet->CreateFixture(&box, 100.0f);
m_bullet->SetLinearVelocity(b2Vec2(0.0f, -50.0f));
}
}
void Launch()
{
m_body->SetTransform(b2Vec2(0.0f, 4.0f), 0.0f);
m_body->SetLinearVelocity(b2Vec2_zero);
m_body->SetAngularVelocity(0.0f);
m_x = RandomFloat(-1.0f, 1.0f);
m_bullet->SetTransform(b2Vec2(m_x, 10.0f), 0.0f);
m_bullet->SetLinearVelocity(b2Vec2(0.0f, -50.0f));
m_bullet->SetAngularVelocity(0.0f);
extern int32 b2_gjkCalls, b2_gjkIters, b2_gjkMaxIters;
extern int32 b2_toiCalls, b2_toiIters, b2_toiMaxIters;
extern int32 b2_toiRootIters, b2_toiMaxRootIters;
b2_gjkCalls = 0;
b2_gjkIters = 0;
b2_gjkMaxIters = 0;
b2_toiCalls = 0;
b2_toiIters = 0;
b2_toiMaxIters = 0;
b2_toiRootIters = 0;
b2_toiMaxRootIters = 0;
}
void Step(Settings* settings)
{
Test::Step(settings);
extern int32 b2_gjkCalls, b2_gjkIters, b2_gjkMaxIters;
extern int32 b2_toiCalls, b2_toiIters;
extern int32 b2_toiRootIters, b2_toiMaxRootIters;
if (b2_gjkCalls > 0)
{
m_debugDraw.DrawString(5, m_textLine, "gjk calls = %d, ave gjk iters = %3.1f, max gjk iters = %d",
b2_gjkCalls, b2_gjkIters / float32(b2_gjkCalls), b2_gjkMaxIters);
m_textLine += 15;
}
if (b2_toiCalls > 0)
{
m_debugDraw.DrawString(5, m_textLine, "toi calls = %d, ave toi iters = %3.1f, max toi iters = %d",
b2_toiCalls, b2_toiIters / float32(b2_toiCalls), b2_toiMaxRootIters);
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "ave toi root iters = %3.1f, max toi root iters = %d",
b2_toiRootIters / float32(b2_toiCalls), b2_toiMaxRootIters);
m_textLine += 15;
}
if (m_stepCount % 60 == 0)
{
Launch();
}
}
static Test* Create()
{
return new BulletTest;
}
b2Body* m_body;
b2Body* m_bullet;
float32 m_x;
};
#endif
@@ -0,0 +1,211 @@
/*
* Copyright (c) 2006-2011 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef CANTILEVER_H
#define CANTILEVER_H
// It is difficult to make a cantilever made of links completely rigid with weld joints.
// You will have to use a high number of iterations to make them stiff.
// So why not go ahead and use soft weld joints? They behave like a revolute
// joint with a rotational spring.
class Cantilever : public Test
{
public:
enum
{
e_count = 8
};
Cantilever()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
b2WeldJointDef jd;
b2Body* prevBody = ground;
for (int32 i = 0; i < e_count; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-14.5f + 1.0f * i, 5.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
b2Vec2 anchor(-15.0f + 1.0f * i, 5.0f);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
prevBody = body;
}
}
{
b2PolygonShape shape;
shape.SetAsBox(1.0f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
b2WeldJointDef jd;
jd.frequencyHz = 5.0f;
jd.dampingRatio = 0.7f;
b2Body* prevBody = ground;
for (int32 i = 0; i < 3; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-14.0f + 2.0f * i, 15.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
b2Vec2 anchor(-15.0f + 2.0f * i, 15.0f);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
prevBody = body;
}
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
b2WeldJointDef jd;
b2Body* prevBody = ground;
for (int32 i = 0; i < e_count; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-4.5f + 1.0f * i, 5.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
if (i > 0)
{
b2Vec2 anchor(-5.0f + 1.0f * i, 5.0f);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
}
prevBody = body;
}
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
b2WeldJointDef jd;
jd.frequencyHz = 8.0f;
jd.dampingRatio = 0.7f;
b2Body* prevBody = ground;
for (int32 i = 0; i < e_count; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(5.5f + 1.0f * i, 10.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
if (i > 0)
{
b2Vec2 anchor(5.0f + 1.0f * i, 10.0f);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
}
prevBody = body;
}
}
for (int32 i = 0; i < 2; ++i)
{
b2Vec2 vertices[3];
vertices[0].Set(-0.5f, 0.0f);
vertices[1].Set(0.5f, 0.0f);
vertices[2].Set(0.0f, 1.5f);
b2PolygonShape shape;
shape.Set(vertices, 3);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-8.0f + 8.0f * i, 12.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
for (int32 i = 0; i < 2; ++i)
{
b2CircleShape shape;
shape.m_radius = 0.5f;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-6.0f + 6.0f * i, 10.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
}
static Test* Create()
{
return new Cantilever;
}
b2Body* m_middle;
};
#endif
@@ -0,0 +1,286 @@
/*
* Copyright (c) 2006-2011 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef CAR_H
#define CAR_H
// This is a fun demo that shows off the wheel joint
class Car : public Test
{
public:
Car()
{
m_hz = 4.0f;
m_zeta = 0.7f;
m_speed = 50.0f;
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 0.0f;
fd.friction = 0.6f;
shape.Set(b2Vec2(-20.0f, 0.0f), b2Vec2(20.0f, 0.0f));
ground->CreateFixture(&fd);
float32 hs[10] = {0.25f, 1.0f, 4.0f, 0.0f, 0.0f, -1.0f, -2.0f, -2.0f, -1.25f, 0.0f};
float32 x = 20.0f, y1 = 0.0f, dx = 5.0f;
for (int32 i = 0; i < 10; ++i)
{
float32 y2 = hs[i];
shape.Set(b2Vec2(x, y1), b2Vec2(x + dx, y2));
ground->CreateFixture(&fd);
y1 = y2;
x += dx;
}
for (int32 i = 0; i < 10; ++i)
{
float32 y2 = hs[i];
shape.Set(b2Vec2(x, y1), b2Vec2(x + dx, y2));
ground->CreateFixture(&fd);
y1 = y2;
x += dx;
}
shape.Set(b2Vec2(x, 0.0f), b2Vec2(x + 40.0f, 0.0f));
ground->CreateFixture(&fd);
x += 80.0f;
shape.Set(b2Vec2(x, 0.0f), b2Vec2(x + 40.0f, 0.0f));
ground->CreateFixture(&fd);
x += 40.0f;
shape.Set(b2Vec2(x, 0.0f), b2Vec2(x + 10.0f, 5.0f));
ground->CreateFixture(&fd);
x += 20.0f;
shape.Set(b2Vec2(x, 0.0f), b2Vec2(x + 40.0f, 0.0f));
ground->CreateFixture(&fd);
x += 40.0f;
shape.Set(b2Vec2(x, 0.0f), b2Vec2(x, 20.0f));
ground->CreateFixture(&fd);
}
// Teeter
{
b2BodyDef bd;
bd.position.Set(140.0f, 1.0f);
bd.type = b2_dynamicBody;
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape box;
box.SetAsBox(10.0f, 0.25f);
body->CreateFixture(&box, 1.0f);
b2RevoluteJointDef jd;
jd.Initialize(ground, body, body->GetPosition());
jd.lowerAngle = -8.0f * b2_pi / 180.0f;
jd.upperAngle = 8.0f * b2_pi / 180.0f;
jd.enableLimit = true;
m_world->CreateJoint(&jd);
body->ApplyAngularImpulse(100.0f);
}
// Bridge
{
int32 N = 20;
b2PolygonShape shape;
shape.SetAsBox(1.0f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
fd.friction = 0.6f;
b2RevoluteJointDef jd;
b2Body* prevBody = ground;
for (int32 i = 0; i < N; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(161.0f + 2.0f * i, -0.125f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
b2Vec2 anchor(160.0f + 2.0f * i, -0.125f);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
prevBody = body;
}
b2Vec2 anchor(160.0f + 2.0f * N, -0.125f);
jd.Initialize(prevBody, ground, anchor);
m_world->CreateJoint(&jd);
}
// Boxes
{
b2PolygonShape box;
box.SetAsBox(0.5f, 0.5f);
b2Body* body = NULL;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(230.0f, 0.5f);
body = m_world->CreateBody(&bd);
body->CreateFixture(&box, 0.5f);
bd.position.Set(230.0f, 1.5f);
body = m_world->CreateBody(&bd);
body->CreateFixture(&box, 0.5f);
bd.position.Set(230.0f, 2.5f);
body = m_world->CreateBody(&bd);
body->CreateFixture(&box, 0.5f);
bd.position.Set(230.0f, 3.5f);
body = m_world->CreateBody(&bd);
body->CreateFixture(&box, 0.5f);
bd.position.Set(230.0f, 4.5f);
body = m_world->CreateBody(&bd);
body->CreateFixture(&box, 0.5f);
}
// Car
{
b2PolygonShape chassis;
b2Vec2 vertices[8];
vertices[0].Set(-1.5f, -0.5f);
vertices[1].Set(1.5f, -0.5f);
vertices[2].Set(1.5f, 0.0f);
vertices[3].Set(0.0f, 0.9f);
vertices[4].Set(-1.15f, 0.9f);
vertices[5].Set(-1.5f, 0.2f);
chassis.Set(vertices, 6);
b2CircleShape circle;
circle.m_radius = 0.4f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 1.0f);
m_car = m_world->CreateBody(&bd);
m_car->CreateFixture(&chassis, 1.0f);
b2FixtureDef fd;
fd.shape = &circle;
fd.density = 1.0f;
fd.friction = 0.9f;
bd.position.Set(-1.0f, 0.35f);
m_wheel1 = m_world->CreateBody(&bd);
m_wheel1->CreateFixture(&fd);
bd.position.Set(1.0f, 0.4f);
m_wheel2 = m_world->CreateBody(&bd);
m_wheel2->CreateFixture(&fd);
b2WheelJointDef jd;
b2Vec2 axis(0.0f, 1.0f);
jd.Initialize(m_car, m_wheel1, m_wheel1->GetPosition(), axis);
jd.motorSpeed = 0.0f;
jd.maxMotorTorque = 20.0f;
jd.enableMotor = true;
jd.frequencyHz = m_hz;
jd.dampingRatio = m_zeta;
m_spring1 = (b2WheelJoint*)m_world->CreateJoint(&jd);
jd.Initialize(m_car, m_wheel2, m_wheel2->GetPosition(), axis);
jd.motorSpeed = 0.0f;
jd.maxMotorTorque = 10.0f;
jd.enableMotor = false;
jd.frequencyHz = m_hz;
jd.dampingRatio = m_zeta;
m_spring2 = (b2WheelJoint*)m_world->CreateJoint(&jd);
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'a':
m_spring1->SetMotorSpeed(m_speed);
break;
case 's':
m_spring1->SetMotorSpeed(0.0f);
break;
case 'd':
m_spring1->SetMotorSpeed(-m_speed);
break;
case 'q':
m_hz = b2Max(0.0f, m_hz - 1.0f);
m_spring1->SetSpringFrequencyHz(m_hz);
m_spring2->SetSpringFrequencyHz(m_hz);
break;
case 'e':
m_hz += 1.0f;
m_spring1->SetSpringFrequencyHz(m_hz);
m_spring2->SetSpringFrequencyHz(m_hz);
break;
}
}
void Step(Settings* settings)
{
m_debugDraw.DrawString(5, m_textLine, "Keys: left = a, brake = s, right = d, hz down = q, hz up = e");
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "frequency = %g hz, damping ratio = %g", m_hz, m_zeta);
m_textLine += 15;
settings->viewCenter.x = m_car->GetPosition().x;
Test::Step(settings);
}
static Test* Create()
{
return new Car;
}
b2Body* m_car;
b2Body* m_wheel1;
b2Body* m_wheel2;
float32 m_hz;
float32 m_zeta;
float32 m_speed;
b2WheelJoint* m_spring1;
b2WheelJoint* m_spring2;
};
#endif
@@ -0,0 +1,74 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef CHAIN_H
#define CHAIN_H
class Chain : public Test
{
public:
Chain()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.6f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
fd.friction = 0.2f;
b2RevoluteJointDef jd;
jd.collideConnected = false;
const float32 y = 25.0f;
b2Body* prevBody = ground;
for (int i = 0; i < 30; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.5f + i, y);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
b2Vec2 anchor(float32(i), y);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
prevBody = body;
}
}
}
static Test* Create()
{
return new Chain;
}
};
#endif
@@ -0,0 +1,253 @@
/*
* Copyright (c) 2006-2010 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef CHARACTER_COLLISION_H
#define CHARACTER_COLLISION_H
/// This is a test of typical character collision scenarios. This does not
/// show how you should implement a character in your application.
/// Instead this is used to test smooth collision on edge chains.
class CharacterCollision : public Test
{
public:
CharacterCollision()
{
// Ground body
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-20.0f, 0.0f), b2Vec2(20.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
// Collinear edges with no adjacency information.
// This shows the problematic case where a box shape can hit
// an internal vertex.
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-8.0f, 1.0f), b2Vec2(-6.0f, 1.0f));
ground->CreateFixture(&shape, 0.0f);
shape.Set(b2Vec2(-6.0f, 1.0f), b2Vec2(-4.0f, 1.0f));
ground->CreateFixture(&shape, 0.0f);
shape.Set(b2Vec2(-4.0f, 1.0f), b2Vec2(-2.0f, 1.0f));
ground->CreateFixture(&shape, 0.0f);
}
// Chain shape
{
b2BodyDef bd;
bd.angle = 0.25f * b2_pi;
b2Body* ground = m_world->CreateBody(&bd);
b2Vec2 vs[4];
vs[0].Set(5.0f, 7.0f);
vs[1].Set(6.0f, 8.0f);
vs[2].Set(7.0f, 8.0f);
vs[3].Set(8.0f, 7.0f);
b2ChainShape shape;
shape.CreateChain(vs, 4);
ground->CreateFixture(&shape, 0.0f);
}
// Square tiles. This shows that adjacency shapes may
// have non-smooth collision. There is no solution
// to this problem.
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(1.0f, 1.0f, b2Vec2(4.0f, 3.0f), 0.0f);
ground->CreateFixture(&shape, 0.0f);
shape.SetAsBox(1.0f, 1.0f, b2Vec2(6.0f, 3.0f), 0.0f);
ground->CreateFixture(&shape, 0.0f);
shape.SetAsBox(1.0f, 1.0f, b2Vec2(8.0f, 3.0f), 0.0f);
ground->CreateFixture(&shape, 0.0f);
}
// Square made from an edge loop. Collision should be smooth.
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2Vec2 vs[4];
vs[0].Set(-1.0f, 3.0f);
vs[1].Set(1.0f, 3.0f);
vs[2].Set(1.0f, 5.0f);
vs[3].Set(-1.0f, 5.0f);
b2ChainShape shape;
shape.CreateLoop(vs, 4);
ground->CreateFixture(&shape, 0.0f);
}
// Edge loop. Collision should be smooth.
{
b2BodyDef bd;
bd.position.Set(-10.0f, 4.0f);
b2Body* ground = m_world->CreateBody(&bd);
b2Vec2 vs[10];
vs[0].Set(0.0f, 0.0f);
vs[1].Set(6.0f, 0.0f);
vs[2].Set(6.0f, 2.0f);
vs[3].Set(4.0f, 1.0f);
vs[4].Set(2.0f, 2.0f);
vs[5].Set(0.0f, 2.0f);
vs[6].Set(-2.0f, 2.0f);
vs[7].Set(-4.0f, 3.0f);
vs[8].Set(-6.0f, 2.0f);
vs[9].Set(-6.0f, 0.0f);
b2ChainShape shape;
shape.CreateLoop(vs, 10);
ground->CreateFixture(&shape, 0.0f);
}
// Square character 1
{
b2BodyDef bd;
bd.position.Set(-3.0f, 8.0f);
bd.type = b2_dynamicBody;
bd.fixedRotation = true;
bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.5f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
body->CreateFixture(&fd);
}
// Square character 2
{
b2BodyDef bd;
bd.position.Set(-5.0f, 5.0f);
bd.type = b2_dynamicBody;
bd.fixedRotation = true;
bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.25f, 0.25f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
body->CreateFixture(&fd);
}
// Hexagon character
{
b2BodyDef bd;
bd.position.Set(-5.0f, 8.0f);
bd.type = b2_dynamicBody;
bd.fixedRotation = true;
bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
float32 angle = 0.0f;
float32 delta = b2_pi / 3.0f;
b2Vec2 vertices[6];
for (int32 i = 0; i < 6; ++i)
{
vertices[i].Set(0.5f * cosf(angle), 0.5f * sinf(angle));
angle += delta;
}
b2PolygonShape shape;
shape.Set(vertices, 6);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
body->CreateFixture(&fd);
}
// Circle character
{
b2BodyDef bd;
bd.position.Set(3.0f, 5.0f);
bd.type = b2_dynamicBody;
bd.fixedRotation = true;
bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
b2CircleShape shape;
shape.m_radius = 0.5f;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
body->CreateFixture(&fd);
}
// Circle character
{
b2BodyDef bd;
bd.position.Set(-7.0f, 6.0f);
bd.type = b2_dynamicBody;
bd.allowSleep = false;
m_character = m_world->CreateBody(&bd);
b2CircleShape shape;
shape.m_radius = 0.25f;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
fd.friction = 1.0f;
m_character->CreateFixture(&fd);
}
}
void Step(Settings* settings)
{
b2Vec2 v = m_character->GetLinearVelocity();
v.x = -5.0f;
m_character->SetLinearVelocity(v);
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "This tests various character collision shapes.");
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "Limitation: square and hexagon can snag on aligned boxes.");
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "Feature: edge chains have smooth collision inside and out.");
m_textLine += 15;
}
static Test* Create()
{
return new CharacterCollision;
}
b2Body* m_character;
};
#endif
@@ -0,0 +1,176 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef COLLISION_FILTERING_H
#define COLLISION_FILTERING_H
// This is a test of collision filtering.
// There is a triangle, a box, and a circle.
// There are 6 shapes. 3 large and 3 small.
// The 3 small ones always collide.
// The 3 large ones never collide.
// The boxes don't collide with triangles (except if both are small).
const int16 k_smallGroup = 1;
const int16 k_largeGroup = -1;
const uint16 k_defaultCategory = 0x0001;
const uint16 k_triangleCategory = 0x0002;
const uint16 k_boxCategory = 0x0004;
const uint16 k_circleCategory = 0x0008;
const uint16 k_triangleMask = 0xFFFF;
const uint16 k_boxMask = 0xFFFF ^ k_triangleCategory;
const uint16 k_circleMask = 0xFFFF;
class CollisionFiltering : public Test
{
public:
CollisionFiltering()
{
// Ground body
{
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
b2FixtureDef sd;
sd.shape = &shape;
sd.friction = 0.3f;
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&sd);
}
// Small triangle
b2Vec2 vertices[3];
vertices[0].Set(-1.0f, 0.0f);
vertices[1].Set(1.0f, 0.0f);
vertices[2].Set(0.0f, 2.0f);
b2PolygonShape polygon;
polygon.Set(vertices, 3);
b2FixtureDef triangleShapeDef;
triangleShapeDef.shape = &polygon;
triangleShapeDef.density = 1.0f;
triangleShapeDef.filter.groupIndex = k_smallGroup;
triangleShapeDef.filter.categoryBits = k_triangleCategory;
triangleShapeDef.filter.maskBits = k_triangleMask;
b2BodyDef triangleBodyDef;
triangleBodyDef.type = b2_dynamicBody;
triangleBodyDef.position.Set(-5.0f, 2.0f);
b2Body* body1 = m_world->CreateBody(&triangleBodyDef);
body1->CreateFixture(&triangleShapeDef);
// Large triangle (recycle definitions)
vertices[0] *= 2.0f;
vertices[1] *= 2.0f;
vertices[2] *= 2.0f;
polygon.Set(vertices, 3);
triangleShapeDef.filter.groupIndex = k_largeGroup;
triangleBodyDef.position.Set(-5.0f, 6.0f);
triangleBodyDef.fixedRotation = true; // look at me!
b2Body* body2 = m_world->CreateBody(&triangleBodyDef);
body2->CreateFixture(&triangleShapeDef);
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-5.0f, 10.0f);
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape p;
p.SetAsBox(0.5f, 1.0f);
body->CreateFixture(&p, 1.0f);
b2PrismaticJointDef jd;
jd.bodyA = body2;
jd.bodyB = body;
jd.enableLimit = true;
jd.localAnchorA.Set(0.0f, 4.0f);
jd.localAnchorB.SetZero();
jd.localAxisA.Set(0.0f, 1.0f);
jd.lowerTranslation = -1.0f;
jd.upperTranslation = 1.0f;
m_world->CreateJoint(&jd);
}
// Small box
polygon.SetAsBox(1.0f, 0.5f);
b2FixtureDef boxShapeDef;
boxShapeDef.shape = &polygon;
boxShapeDef.density = 1.0f;
boxShapeDef.restitution = 0.1f;
boxShapeDef.filter.groupIndex = k_smallGroup;
boxShapeDef.filter.categoryBits = k_boxCategory;
boxShapeDef.filter.maskBits = k_boxMask;
b2BodyDef boxBodyDef;
boxBodyDef.type = b2_dynamicBody;
boxBodyDef.position.Set(0.0f, 2.0f);
b2Body* body3 = m_world->CreateBody(&boxBodyDef);
body3->CreateFixture(&boxShapeDef);
// Large box (recycle definitions)
polygon.SetAsBox(2.0f, 1.0f);
boxShapeDef.filter.groupIndex = k_largeGroup;
boxBodyDef.position.Set(0.0f, 6.0f);
b2Body* body4 = m_world->CreateBody(&boxBodyDef);
body4->CreateFixture(&boxShapeDef);
// Small circle
b2CircleShape circle;
circle.m_radius = 1.0f;
b2FixtureDef circleShapeDef;
circleShapeDef.shape = &circle;
circleShapeDef.density = 1.0f;
circleShapeDef.filter.groupIndex = k_smallGroup;
circleShapeDef.filter.categoryBits = k_circleCategory;
circleShapeDef.filter.maskBits = k_circleMask;
b2BodyDef circleBodyDef;
circleBodyDef.type = b2_dynamicBody;
circleBodyDef.position.Set(5.0f, 2.0f);
b2Body* body5 = m_world->CreateBody(&circleBodyDef);
body5->CreateFixture(&circleShapeDef);
// Large circle
circle.m_radius *= 2.0f;
circleShapeDef.filter.groupIndex = k_largeGroup;
circleBodyDef.position.Set(5.0f, 6.0f);
b2Body* body6 = m_world->CreateBody(&circleBodyDef);
body6->CreateFixture(&circleShapeDef);
}
static Test* Create()
{
return new CollisionFiltering;
}
};
#endif
@@ -0,0 +1,188 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef COLLISION_PROCESSING_H
#define COLLISION_PROCESSING_H
#include <algorithm>
// This test shows collision processing and tests
// deferred body destruction.
class CollisionProcessing : public Test
{
public:
CollisionProcessing()
{
// Ground body
{
b2EdgeShape shape;
shape.Set(b2Vec2(-50.0f, 0.0f), b2Vec2(50.0f, 0.0f));
b2FixtureDef sd;
sd.shape = &shape;;
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&sd);
}
float32 xLo = -5.0f, xHi = 5.0f;
float32 yLo = 2.0f, yHi = 35.0f;
// Small triangle
b2Vec2 vertices[3];
vertices[0].Set(-1.0f, 0.0f);
vertices[1].Set(1.0f, 0.0f);
vertices[2].Set(0.0f, 2.0f);
b2PolygonShape polygon;
polygon.Set(vertices, 3);
b2FixtureDef triangleShapeDef;
triangleShapeDef.shape = &polygon;
triangleShapeDef.density = 1.0f;
b2BodyDef triangleBodyDef;
triangleBodyDef.type = b2_dynamicBody;
triangleBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
b2Body* body1 = m_world->CreateBody(&triangleBodyDef);
body1->CreateFixture(&triangleShapeDef);
// Large triangle (recycle definitions)
vertices[0] *= 2.0f;
vertices[1] *= 2.0f;
vertices[2] *= 2.0f;
polygon.Set(vertices, 3);
triangleBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
b2Body* body2 = m_world->CreateBody(&triangleBodyDef);
body2->CreateFixture(&triangleShapeDef);
// Small box
polygon.SetAsBox(1.0f, 0.5f);
b2FixtureDef boxShapeDef;
boxShapeDef.shape = &polygon;
boxShapeDef.density = 1.0f;
b2BodyDef boxBodyDef;
boxBodyDef.type = b2_dynamicBody;
boxBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
b2Body* body3 = m_world->CreateBody(&boxBodyDef);
body3->CreateFixture(&boxShapeDef);
// Large box (recycle definitions)
polygon.SetAsBox(2.0f, 1.0f);
boxBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
b2Body* body4 = m_world->CreateBody(&boxBodyDef);
body4->CreateFixture(&boxShapeDef);
// Small circle
b2CircleShape circle;
circle.m_radius = 1.0f;
b2FixtureDef circleShapeDef;
circleShapeDef.shape = &circle;
circleShapeDef.density = 1.0f;
b2BodyDef circleBodyDef;
circleBodyDef.type = b2_dynamicBody;
circleBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
b2Body* body5 = m_world->CreateBody(&circleBodyDef);
body5->CreateFixture(&circleShapeDef);
// Large circle
circle.m_radius *= 2.0f;
circleBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
b2Body* body6 = m_world->CreateBody(&circleBodyDef);
body6->CreateFixture(&circleShapeDef);
}
void Step(Settings* settings)
{
Test::Step(settings);
// We are going to destroy some bodies according to contact
// points. We must buffer the bodies that should be destroyed
// because they may belong to multiple contact points.
const int32 k_maxNuke = 6;
b2Body* nuke[k_maxNuke];
int32 nukeCount = 0;
// Traverse the contact results. Destroy bodies that
// are touching heavier bodies.
for (int32 i = 0; i < m_pointCount; ++i)
{
ContactPoint* point = m_points + i;
b2Body* body1 = point->fixtureA->GetBody();
b2Body* body2 = point->fixtureB->GetBody();
float32 mass1 = body1->GetMass();
float32 mass2 = body2->GetMass();
if (mass1 > 0.0f && mass2 > 0.0f)
{
if (mass2 > mass1)
{
nuke[nukeCount++] = body1;
}
else
{
nuke[nukeCount++] = body2;
}
if (nukeCount == k_maxNuke)
{
break;
}
}
}
// Sort the nuke array to group duplicates.
std::sort(nuke, nuke + nukeCount);
// Destroy the bodies, skipping duplicates.
int32 i = 0;
while (i < nukeCount)
{
b2Body* b = nuke[i++];
while (i < nukeCount && nuke[i] == b)
{
++i;
}
if (b != m_bomb)
{
m_world->DestroyBody(b);
}
}
}
static Test* Create()
{
return new CollisionProcessing;
}
};
#endif
@@ -0,0 +1,143 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef COMPOUND_SHAPES_H
#define COMPOUND_SHAPES_H
// TODO_ERIN test joints on compounds.
class CompoundShapes : public Test
{
public:
CompoundShapes()
{
{
b2BodyDef bd;
bd.position.Set(0.0f, 0.0f);
b2Body* body = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(50.0f, 0.0f), b2Vec2(-50.0f, 0.0f));
body->CreateFixture(&shape, 0.0f);
}
{
b2CircleShape circle1;
circle1.m_radius = 0.5f;
circle1.m_p.Set(-0.5f, 0.5f);
b2CircleShape circle2;
circle2.m_radius = 0.5f;
circle2.m_p.Set(0.5f, 0.5f);
for (int i = 0; i < 10; ++i)
{
float32 x = RandomFloat(-0.1f, 0.1f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(x + 5.0f, 1.05f + 2.5f * i);
bd.angle = RandomFloat(-b2_pi, b2_pi);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&circle1, 2.0f);
body->CreateFixture(&circle2, 0.0f);
}
}
{
b2PolygonShape polygon1;
polygon1.SetAsBox(0.25f, 0.5f);
b2PolygonShape polygon2;
polygon2.SetAsBox(0.25f, 0.5f, b2Vec2(0.0f, -0.5f), 0.5f * b2_pi);
for (int i = 0; i < 10; ++i)
{
float32 x = RandomFloat(-0.1f, 0.1f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(x - 5.0f, 1.05f + 2.5f * i);
bd.angle = RandomFloat(-b2_pi, b2_pi);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&polygon1, 2.0f);
body->CreateFixture(&polygon2, 2.0f);
}
}
{
b2Transform xf1;
xf1.q.Set(0.3524f * b2_pi);
xf1.p = xf1.q.GetXAxis();
b2Vec2 vertices[3];
b2PolygonShape triangle1;
vertices[0] = b2Mul(xf1, b2Vec2(-1.0f, 0.0f));
vertices[1] = b2Mul(xf1, b2Vec2(1.0f, 0.0f));
vertices[2] = b2Mul(xf1, b2Vec2(0.0f, 0.5f));
triangle1.Set(vertices, 3);
b2Transform xf2;
xf2.q.Set(-0.3524f * b2_pi);
xf2.p = -xf2.q.GetXAxis();
b2PolygonShape triangle2;
vertices[0] = b2Mul(xf2, b2Vec2(-1.0f, 0.0f));
vertices[1] = b2Mul(xf2, b2Vec2(1.0f, 0.0f));
vertices[2] = b2Mul(xf2, b2Vec2(0.0f, 0.5f));
triangle2.Set(vertices, 3);
for (int32 i = 0; i < 10; ++i)
{
float32 x = RandomFloat(-0.1f, 0.1f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(x, 2.05f + 2.5f * i);
bd.angle = 0.0f;
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&triangle1, 2.0f);
body->CreateFixture(&triangle2, 2.0f);
}
}
{
b2PolygonShape bottom;
bottom.SetAsBox( 1.5f, 0.15f );
b2PolygonShape left;
left.SetAsBox(0.15f, 2.7f, b2Vec2(-1.45f, 2.35f), 0.2f);
b2PolygonShape right;
right.SetAsBox(0.15f, 2.7f, b2Vec2(1.45f, 2.35f), -0.2f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set( 0.0f, 2.0f );
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&bottom, 4.0f);
body->CreateFixture(&left, 4.0f);
body->CreateFixture(&right, 4.0f);
}
}
static Test* Create()
{
return new CompoundShapes;
}
};
#endif
@@ -0,0 +1,167 @@
/*
* Copyright (c) 2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef CONFINED_H
#define CONFINED_H
class Confined : public Test
{
public:
enum
{
e_columnCount = 0,
e_rowCount = 0
};
Confined()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
// Floor
shape.Set(b2Vec2(-10.0f, 0.0f), b2Vec2(10.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
// Left wall
shape.Set(b2Vec2(-10.0f, 0.0f), b2Vec2(-10.0f, 20.0f));
ground->CreateFixture(&shape, 0.0f);
// Right wall
shape.Set(b2Vec2(10.0f, 0.0f), b2Vec2(10.0f, 20.0f));
ground->CreateFixture(&shape, 0.0f);
// Roof
shape.Set(b2Vec2(-10.0f, 20.0f), b2Vec2(10.0f, 20.0f));
ground->CreateFixture(&shape, 0.0f);
}
float32 radius = 0.5f;
b2CircleShape shape;
shape.m_p.SetZero();
shape.m_radius = radius;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
fd.friction = 0.1f;
for (int32 j = 0; j < e_columnCount; ++j)
{
for (int i = 0; i < e_rowCount; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-10.0f + (2.1f * j + 1.0f + 0.01f * i) * radius, (2.0f * i + 1.0f) * radius);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
}
m_world->SetGravity(b2Vec2(0.0f, 0.0f));
}
void CreateCircle()
{
float32 radius = 2.0f;
b2CircleShape shape;
shape.m_p.SetZero();
shape.m_radius = radius;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
fd.friction = 0.0f;
b2Vec2 p(RandomFloat(), 3.0f + RandomFloat());
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = p;
//bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'c':
CreateCircle();
break;
}
}
void Step(Settings* settings)
{
bool sleeping = true;
for (b2Body* b = m_world->GetBodyList(); b; b = b->GetNext())
{
if (b->GetType() != b2_dynamicBody)
{
continue;
}
if (b->IsAwake())
{
sleeping = false;
}
}
if (m_stepCount == 180)
{
m_stepCount += 0;
}
//if (sleeping)
//{
// CreateCircle();
//}
Test::Step(settings);
for (b2Body* b = m_world->GetBodyList(); b; b = b->GetNext())
{
if (b->GetType() != b2_dynamicBody)
{
continue;
}
b2Vec2 p = b->GetPosition();
if (p.x <= -10.0f || 10.0f <= p.x || p.y <= 0.0f || 20.0f <= p.y)
{
p.x += 0.0;
}
}
m_debugDraw.DrawString(5, m_textLine, "Press 'c' to create a circle.");
m_textLine += 15;
}
static Test* Create()
{
return new Confined;
}
};
#endif
@@ -0,0 +1,137 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef CONTINUOUS_TEST_H
#define CONTINUOUS_TEST_H
class ContinuousTest : public Test
{
public:
ContinuousTest()
{
{
b2BodyDef bd;
bd.position.Set(0.0f, 0.0f);
b2Body* body = m_world->CreateBody(&bd);
b2EdgeShape edge;
edge.Set(b2Vec2(-10.0f, 0.0f), b2Vec2(10.0f, 0.0f));
body->CreateFixture(&edge, 0.0f);
b2PolygonShape shape;
shape.SetAsBox(0.2f, 1.0f, b2Vec2(0.5f, 1.0f), 0.0f);
body->CreateFixture(&shape, 0.0f);
}
#if 1
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 20.0f);
//bd.angle = 0.1f;
b2PolygonShape shape;
shape.SetAsBox(2.0f, 0.1f);
m_body = m_world->CreateBody(&bd);
m_body->CreateFixture(&shape, 1.0f);
m_angularVelocity = RandomFloat(-50.0f, 50.0f);
//m_angularVelocity = 46.661274f;
m_body->SetLinearVelocity(b2Vec2(0.0f, -100.0f));
m_body->SetAngularVelocity(m_angularVelocity);
}
#else
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 2.0f);
b2Body* body = m_world->CreateBody(&bd);
b2CircleShape shape;
shape.m_p.SetZero();
shape.m_radius = 0.5f;
body->CreateFixture(&shape, 1.0f);
bd.bullet = true;
bd.position.Set(0.0f, 10.0f);
body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 1.0f);
body->SetLinearVelocity(b2Vec2(0.0f, -100.0f));
}
#endif
}
void Launch()
{
m_body->SetTransform(b2Vec2(0.0f, 20.0f), 0.0f);
m_angularVelocity = RandomFloat(-50.0f, 50.0f);
m_body->SetLinearVelocity(b2Vec2(0.0f, -100.0f));
m_body->SetAngularVelocity(m_angularVelocity);
}
void Step(Settings* settings)
{
if (m_stepCount == 12)
{
m_stepCount += 0;
}
Test::Step(settings);
extern int32 b2_gjkCalls, b2_gjkIters, b2_gjkMaxIters;
if (b2_gjkCalls > 0)
{
m_debugDraw.DrawString(5, m_textLine, "gjk calls = %d, ave gjk iters = %3.1f, max gjk iters = %d",
b2_gjkCalls, b2_gjkIters / float32(b2_gjkCalls), b2_gjkMaxIters);
m_textLine += 15;
}
extern int32 b2_toiCalls, b2_toiIters;
extern int32 b2_toiRootIters, b2_toiMaxRootIters;
if (b2_toiCalls > 0)
{
m_debugDraw.DrawString(5, m_textLine, "toi calls = %d, ave toi iters = %3.1f, max toi iters = %d",
b2_toiCalls, b2_toiIters / float32(b2_toiCalls), b2_toiMaxRootIters);
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "ave toi root iters = %3.1f, max toi root iters = %d",
b2_toiRootIters / float32(b2_toiCalls), b2_toiMaxRootIters);
m_textLine += 15;
}
if (m_stepCount % 60 == 0)
{
//Launch();
}
}
static Test* Create()
{
return new ContinuousTest;
}
b2Body* m_body;
float32 m_angularVelocity;
};
#endif
@@ -0,0 +1,135 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef DISTANCE_TEST_H
#define DISTANCE_TEST_H
class DistanceTest : public Test
{
public:
DistanceTest()
{
{
m_transformA.SetIdentity();
m_transformA.p.Set(0.0f, -0.2f);
m_polygonA.SetAsBox(10.0f, 0.2f);
}
{
m_positionB.Set(12.017401f, 0.13678508f);
m_angleB = -0.0109265f;
m_transformB.Set(m_positionB, m_angleB);
m_polygonB.SetAsBox(2.0f, 0.1f);
}
}
static Test* Create()
{
return new DistanceTest;
}
void Step(Settings* settings)
{
Test::Step(settings);
b2DistanceInput input;
input.proxyA.Set(&m_polygonA, 0);
input.proxyB.Set(&m_polygonB, 0);
input.transformA = m_transformA;
input.transformB = m_transformB;
input.useRadii = true;
b2SimplexCache cache;
cache.count = 0;
b2DistanceOutput output;
b2Distance(&output, &cache, &input);
m_debugDraw.DrawString(5, m_textLine, "distance = %g", output.distance);
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "iterations = %d", output.iterations);
m_textLine += 15;
{
b2Color color(0.9f, 0.9f, 0.9f);
b2Vec2 v[b2_maxPolygonVertices];
for (int32 i = 0; i < m_polygonA.m_vertexCount; ++i)
{
v[i] = b2Mul(m_transformA, m_polygonA.m_vertices[i]);
}
m_debugDraw.DrawPolygon(v, m_polygonA.m_vertexCount, color);
for (int32 i = 0; i < m_polygonB.m_vertexCount; ++i)
{
v[i] = b2Mul(m_transformB, m_polygonB.m_vertices[i]);
}
m_debugDraw.DrawPolygon(v, m_polygonB.m_vertexCount, color);
}
b2Vec2 x1 = output.pointA;
b2Vec2 x2 = output.pointB;
b2Color c1(1.0f, 0.0f, 0.0f);
m_debugDraw.DrawPoint(x1, 4.0f, c1);
b2Color c2(1.0f, 1.0f, 0.0f);
m_debugDraw.DrawPoint(x2, 4.0f, c2);
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'a':
m_positionB.x -= 0.1f;
break;
case 'd':
m_positionB.x += 0.1f;
break;
case 's':
m_positionB.y -= 0.1f;
break;
case 'w':
m_positionB.y += 0.1f;
break;
case 'q':
m_angleB += 0.1f * b2_pi;
break;
case 'e':
m_angleB -= 0.1f * b2_pi;
break;
}
m_transformB.Set(m_positionB, m_angleB);
}
b2Vec2 m_positionB;
float32 m_angleB;
b2Transform m_transformA;
b2Transform m_transformB;
b2PolygonShape m_polygonA;
b2PolygonShape m_polygonB;
};
#endif
@@ -0,0 +1,215 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef DOMINOS_H
#define DOMINOS_H
class Dominos : public Test
{
public:
Dominos()
{
b2Body* b1;
{
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
b2BodyDef bd;
b1 = m_world->CreateBody(&bd);
b1->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(6.0f, 0.25f);
b2BodyDef bd;
bd.position.Set(-1.5f, 10.0f);
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.1f, 1.0f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
fd.friction = 0.1f;
for (int i = 0; i < 10; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-6.0f + 1.0f * i, 11.25f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
}
{
b2PolygonShape shape;
shape.SetAsBox(7.0f, 0.25f, b2Vec2_zero, 0.3f);
b2BodyDef bd;
bd.position.Set(1.0f, 6.0f);
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&shape, 0.0f);
}
b2Body* b2;
{
b2PolygonShape shape;
shape.SetAsBox(0.25f, 1.5f);
b2BodyDef bd;
bd.position.Set(-7.0f, 4.0f);
b2 = m_world->CreateBody(&bd);
b2->CreateFixture(&shape, 0.0f);
}
b2Body* b3;
{
b2PolygonShape shape;
shape.SetAsBox(6.0f, 0.125f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-0.9f, 1.0f);
bd.angle = -0.15f;
b3 = m_world->CreateBody(&bd);
b3->CreateFixture(&shape, 10.0f);
}
b2RevoluteJointDef jd;
b2Vec2 anchor;
anchor.Set(-2.0f, 1.0f);
jd.Initialize(b1, b3, anchor);
jd.collideConnected = true;
m_world->CreateJoint(&jd);
b2Body* b4;
{
b2PolygonShape shape;
shape.SetAsBox(0.25f, 0.25f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-10.0f, 15.0f);
b4 = m_world->CreateBody(&bd);
b4->CreateFixture(&shape, 10.0f);
}
anchor.Set(-7.0f, 15.0f);
jd.Initialize(b2, b4, anchor);
m_world->CreateJoint(&jd);
b2Body* b5;
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(6.5f, 3.0f);
b5 = m_world->CreateBody(&bd);
b2PolygonShape shape;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 10.0f;
fd.friction = 0.1f;
shape.SetAsBox(1.0f, 0.1f, b2Vec2(0.0f, -0.9f), 0.0f);
b5->CreateFixture(&fd);
shape.SetAsBox(0.1f, 1.0f, b2Vec2(-0.9f, 0.0f), 0.0f);
b5->CreateFixture(&fd);
shape.SetAsBox(0.1f, 1.0f, b2Vec2(0.9f, 0.0f), 0.0f);
b5->CreateFixture(&fd);
}
anchor.Set(6.0f, 2.0f);
jd.Initialize(b1, b5, anchor);
m_world->CreateJoint(&jd);
b2Body* b6;
{
b2PolygonShape shape;
shape.SetAsBox(1.0f, 0.1f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(6.5f, 4.1f);
b6 = m_world->CreateBody(&bd);
b6->CreateFixture(&shape, 30.0f);
}
anchor.Set(7.5f, 4.0f);
jd.Initialize(b5, b6, anchor);
m_world->CreateJoint(&jd);
b2Body* b7;
{
b2PolygonShape shape;
shape.SetAsBox(0.1f, 1.0f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(7.4f, 1.0f);
b7 = m_world->CreateBody(&bd);
b7->CreateFixture(&shape, 10.0f);
}
b2DistanceJointDef djd;
djd.bodyA = b3;
djd.bodyB = b7;
djd.localAnchorA.Set(6.0f, 0.0f);
djd.localAnchorB.Set(0.0f, -1.0f);
b2Vec2 d = djd.bodyB->GetWorldPoint(djd.localAnchorB) - djd.bodyA->GetWorldPoint(djd.localAnchorA);
djd.length = d.Length();
m_world->CreateJoint(&djd);
{
float32 radius = 0.2f;
b2CircleShape shape;
shape.m_radius = radius;
for (int i = 0; i < 4; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(5.9f + 2.0f * radius * i, 2.4f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 10.0f);
}
}
}
static Test* Create()
{
return new Dominos;
}
};
#endif
@@ -0,0 +1,267 @@
/*
* Copyright (c) 2011 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef DUMP_SHELL_H
#define DUMP_SHELL_H
// This test holds worlds dumped using b2World::Dump.
class DumpShell : public Test
{
public:
DumpShell()
{
b2Vec2 g(0.000000000000000e+00f, 0.000000000000000e+00f);
m_world->SetGravity(g);
b2Body** bodies = (b2Body**)b2Alloc(3 * sizeof(b2Body*));
b2Joint** joints = (b2Joint**)b2Alloc(2 * sizeof(b2Joint*));
{
b2BodyDef bd;
bd.type = b2BodyType(2);
bd.position.Set(1.304347801208496e+01f, 2.500000000000000e+00f);
bd.angle = 0.000000000000000e+00f;
bd.linearVelocity.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
bd.angularVelocity = 0.000000000000000e+00f;
bd.linearDamping = 5.000000000000000e-01f;
bd.angularDamping = 5.000000000000000e-01f;
bd.allowSleep = bool(4);
bd.awake = bool(2);
bd.fixedRotation = bool(0);
bd.bullet = bool(0);
bd.active = bool(32);
bd.gravityScale = 1.000000000000000e+00f;
bodies[0] = m_world->CreateBody(&bd);
{
b2FixtureDef fd;
fd.friction = 1.000000000000000e+00f;
fd.restitution = 5.000000000000000e-01f;
fd.density = 1.000000000000000e+01f;
fd.isSensor = bool(0);
fd.filter.categoryBits = uint16(1);
fd.filter.maskBits = uint16(65535);
fd.filter.groupIndex = int16(0);
b2PolygonShape shape;
b2Vec2 vs[8];
vs[0].Set(-6.900000095367432e+00f, -3.000000119209290e-01f);
vs[1].Set(2.000000029802322e-01f, -3.000000119209290e-01f);
vs[2].Set(2.000000029802322e-01f, 2.000000029802322e-01f);
vs[3].Set(-6.900000095367432e+00f, 2.000000029802322e-01f);
shape.Set(vs, 4);
fd.shape = &shape;
bodies[0]->CreateFixture(&fd);
}
}
{
b2BodyDef bd;
bd.type = b2BodyType(2);
bd.position.Set(8.478260636329651e-01f, 2.500000000000000e+00f);
bd.angle = 0.000000000000000e+00f;
bd.linearVelocity.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
bd.angularVelocity = 0.000000000000000e+00f;
bd.linearDamping = 5.000000000000000e-01f;
bd.angularDamping = 5.000000000000000e-01f;
bd.allowSleep = bool(4);
bd.awake = bool(2);
bd.fixedRotation = bool(0);
bd.bullet = bool(0);
bd.active = bool(32);
bd.gravityScale = 1.000000000000000e+00f;
bodies[1] = m_world->CreateBody(&bd);
{
b2FixtureDef fd;
fd.friction = 1.000000000000000e+00f;
fd.restitution = 5.000000000000000e-01f;
fd.density = 1.000000000000000e+01f;
fd.isSensor = bool(0);
fd.filter.categoryBits = uint16(1);
fd.filter.maskBits = uint16(65535);
fd.filter.groupIndex = int16(0);
b2PolygonShape shape;
b2Vec2 vs[8];
vs[0].Set(-3.228000104427338e-01f, -2.957000136375427e-01f);
vs[1].Set(6.885900020599365e+00f, -3.641000092029572e-01f);
vs[2].Set(6.907599925994873e+00f, 3.271999955177307e-01f);
vs[3].Set(-3.228000104427338e-01f, 2.825999855995178e-01f);
shape.Set(vs, 4);
fd.shape = &shape;
bodies[1]->CreateFixture(&fd);
}
}
{
b2BodyDef bd;
bd.type = b2BodyType(0);
bd.position.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
bd.angle = 0.000000000000000e+00f;
bd.linearVelocity.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
bd.angularVelocity = 0.000000000000000e+00f;
bd.linearDamping = 0.000000000000000e+00f;
bd.angularDamping = 0.000000000000000e+00f;
bd.allowSleep = bool(4);
bd.awake = bool(2);
bd.fixedRotation = bool(0);
bd.bullet = bool(0);
bd.active = bool(32);
bd.gravityScale = 1.000000000000000e+00f;
bodies[2] = m_world->CreateBody(&bd);
{
b2FixtureDef fd;
fd.friction = 1.000000000000000e+01f;
fd.restitution = 0.000000000000000e+00f;
fd.density = 0.000000000000000e+00f;
fd.isSensor = bool(0);
fd.filter.categoryBits = uint16(1);
fd.filter.maskBits = uint16(65535);
fd.filter.groupIndex = int16(0);
b2EdgeShape shape;
shape.m_radius = 9.999999776482582e-03f;
shape.m_vertex0.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
shape.m_vertex1.Set(4.452173995971680e+01f, 1.669565200805664e+01f);
shape.m_vertex2.Set(4.452173995971680e+01f, 0.000000000000000e+00f);
shape.m_vertex3.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
shape.m_hasVertex0 = bool(0);
shape.m_hasVertex3 = bool(0);
fd.shape = &shape;
bodies[2]->CreateFixture(&fd);
}
{
b2FixtureDef fd;
fd.friction = 1.000000000000000e+01f;
fd.restitution = 0.000000000000000e+00f;
fd.density = 0.000000000000000e+00f;
fd.isSensor = bool(0);
fd.filter.categoryBits = uint16(1);
fd.filter.maskBits = uint16(65535);
fd.filter.groupIndex = int16(0);
b2EdgeShape shape;
shape.m_radius = 9.999999776482582e-03f;
shape.m_vertex0.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
shape.m_vertex1.Set(0.000000000000000e+00f, 1.669565200805664e+01f);
shape.m_vertex2.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
shape.m_vertex3.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
shape.m_hasVertex0 = bool(0);
shape.m_hasVertex3 = bool(0);
fd.shape = &shape;
bodies[2]->CreateFixture(&fd);
}
{
b2FixtureDef fd;
fd.friction = 1.000000000000000e+01f;
fd.restitution = 0.000000000000000e+00f;
fd.density = 0.000000000000000e+00f;
fd.isSensor = bool(0);
fd.filter.categoryBits = uint16(1);
fd.filter.maskBits = uint16(65535);
fd.filter.groupIndex = int16(0);
b2EdgeShape shape;
shape.m_radius = 9.999999776482582e-03f;
shape.m_vertex0.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
shape.m_vertex1.Set(0.000000000000000e+00f, 1.669565200805664e+01f);
shape.m_vertex2.Set(4.452173995971680e+01f, 1.669565200805664e+01f);
shape.m_vertex3.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
shape.m_hasVertex0 = bool(0);
shape.m_hasVertex3 = bool(0);
fd.shape = &shape;
bodies[2]->CreateFixture(&fd);
}
{
b2FixtureDef fd;
fd.friction = 1.000000000000000e+01f;
fd.restitution = 0.000000000000000e+00f;
fd.density = 0.000000000000000e+00f;
fd.isSensor = bool(0);
fd.filter.categoryBits = uint16(1);
fd.filter.maskBits = uint16(65535);
fd.filter.groupIndex = int16(0);
b2EdgeShape shape;
shape.m_radius = 9.999999776482582e-03f;
shape.m_vertex0.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
shape.m_vertex1.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
shape.m_vertex2.Set(4.452173995971680e+01f, 0.000000000000000e+00f);
shape.m_vertex3.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
shape.m_hasVertex0 = bool(0);
shape.m_hasVertex3 = bool(0);
fd.shape = &shape;
bodies[2]->CreateFixture(&fd);
}
}
{
b2PrismaticJointDef jd;
jd.bodyA = bodies[1];
jd.bodyB = bodies[0];
jd.collideConnected = bool(0);
jd.localAnchorA.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
jd.localAnchorB.Set(-1.219565200805664e+01f, 0.000000000000000e+00f);
jd.localAxisA.Set(-1.219565200805664e+01f, 0.000000000000000e+00f);
jd.referenceAngle = 0.000000000000000e+00f;
jd.enableLimit = bool(1);
jd.lowerTranslation = -2.000000000000000e+01f;
jd.upperTranslation = 0.000000000000000e+00f;
jd.enableMotor = bool(1);
jd.motorSpeed = 0.000000000000000e+00f;
jd.maxMotorForce = 1.000000000000000e+01f;
joints[0] = m_world->CreateJoint(&jd);
}
{
b2RevoluteJointDef jd;
jd.bodyA = bodies[1];
jd.bodyB = bodies[2];
jd.collideConnected = bool(0);
jd.localAnchorA.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
jd.localAnchorB.Set(8.478260636329651e-01f, 2.500000000000000e+00f);
jd.referenceAngle = 0.000000000000000e+00f;
jd.enableLimit = bool(0);
jd.lowerAngle = 0.000000000000000e+00f;
jd.upperAngle = 0.000000000000000e+00f;
jd.enableMotor = bool(0);
jd.motorSpeed = 0.000000000000000e+00f;
jd.maxMotorTorque = 0.000000000000000e+00f;
joints[1] = m_world->CreateJoint(&jd);
}
b2Free(joints);
b2Free(bodies);
joints = NULL;
bodies = NULL;
}
static Test* Create()
{
return new DumpShell;
}
};
#endif
@@ -0,0 +1,357 @@
/*
* Copyright (c) 2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef DYNAMIC_TREE_TEST_H
#define DYNAMIC_TREE_TEST_H
class DynamicTreeTest : public Test
{
public:
enum
{
e_actorCount = 128
};
DynamicTreeTest()
{
m_worldExtent = 15.0f;
m_proxyExtent = 0.5f;
srand(888);
for (int32 i = 0; i < e_actorCount; ++i)
{
Actor* actor = m_actors + i;
GetRandomAABB(&actor->aabb);
actor->proxyId = m_tree.CreateProxy(actor->aabb, actor);
}
m_stepCount = 0;
float32 h = m_worldExtent;
m_queryAABB.lowerBound.Set(-3.0f, -4.0f + h);
m_queryAABB.upperBound.Set(5.0f, 6.0f + h);
m_rayCastInput.p1.Set(-5.0, 5.0f + h);
m_rayCastInput.p2.Set(7.0f, -4.0f + h);
//m_rayCastInput.p1.Set(0.0f, 2.0f + h);
//m_rayCastInput.p2.Set(0.0f, -2.0f + h);
m_rayCastInput.maxFraction = 1.0f;
m_automated = false;
}
static Test* Create()
{
return new DynamicTreeTest;
}
void Step(Settings* settings)
{
B2_NOT_USED(settings);
m_rayActor = NULL;
for (int32 i = 0; i < e_actorCount; ++i)
{
m_actors[i].fraction = 1.0f;
m_actors[i].overlap = false;
}
if (m_automated == true)
{
int32 actionCount = b2Max(1, e_actorCount >> 2);
for (int32 i = 0; i < actionCount; ++i)
{
Action();
}
}
Query();
RayCast();
for (int32 i = 0; i < e_actorCount; ++i)
{
Actor* actor = m_actors + i;
if (actor->proxyId == b2_nullNode)
continue;
b2Color c(0.9f, 0.9f, 0.9f);
if (actor == m_rayActor && actor->overlap)
{
c.Set(0.9f, 0.6f, 0.6f);
}
else if (actor == m_rayActor)
{
c.Set(0.6f, 0.9f, 0.6f);
}
else if (actor->overlap)
{
c.Set(0.6f, 0.6f, 0.9f);
}
m_debugDraw.DrawAABB(&actor->aabb, c);
}
b2Color c(0.7f, 0.7f, 0.7f);
m_debugDraw.DrawAABB(&m_queryAABB, c);
m_debugDraw.DrawSegment(m_rayCastInput.p1, m_rayCastInput.p2, c);
b2Color c1(0.2f, 0.9f, 0.2f);
b2Color c2(0.9f, 0.2f, 0.2f);
m_debugDraw.DrawPoint(m_rayCastInput.p1, 6.0f, c1);
m_debugDraw.DrawPoint(m_rayCastInput.p2, 6.0f, c2);
if (m_rayActor)
{
b2Color cr(0.2f, 0.2f, 0.9f);
b2Vec2 p = m_rayCastInput.p1 + m_rayActor->fraction * (m_rayCastInput.p2 - m_rayCastInput.p1);
m_debugDraw.DrawPoint(p, 6.0f, cr);
}
{
int32 height = m_tree.GetHeight();
m_debugDraw.DrawString(5, m_textLine, "dynamic tree height = %d", height);
m_textLine += 15;
}
++m_stepCount;
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'a':
m_automated = !m_automated;
break;
case 'c':
CreateProxy();
break;
case 'd':
DestroyProxy();
break;
case 'm':
MoveProxy();
break;
}
}
bool QueryCallback(int32 proxyId)
{
Actor* actor = (Actor*)m_tree.GetUserData(proxyId);
actor->overlap = b2TestOverlap(m_queryAABB, actor->aabb);
return true;
}
float32 RayCastCallback(const b2RayCastInput& input, int32 proxyId)
{
Actor* actor = (Actor*)m_tree.GetUserData(proxyId);
b2RayCastOutput output;
bool hit = actor->aabb.RayCast(&output, input);
if (hit)
{
m_rayCastOutput = output;
m_rayActor = actor;
m_rayActor->fraction = output.fraction;
return output.fraction;
}
return input.maxFraction;
}
private:
struct Actor
{
b2AABB aabb;
float32 fraction;
bool overlap;
int32 proxyId;
};
void GetRandomAABB(b2AABB* aabb)
{
b2Vec2 w; w.Set(2.0f * m_proxyExtent, 2.0f * m_proxyExtent);
//aabb->lowerBound.x = -m_proxyExtent;
//aabb->lowerBound.y = -m_proxyExtent + m_worldExtent;
aabb->lowerBound.x = RandomFloat(-m_worldExtent, m_worldExtent);
aabb->lowerBound.y = RandomFloat(0.0f, 2.0f * m_worldExtent);
aabb->upperBound = aabb->lowerBound + w;
}
void MoveAABB(b2AABB* aabb)
{
b2Vec2 d;
d.x = RandomFloat(-0.5f, 0.5f);
d.y = RandomFloat(-0.5f, 0.5f);
//d.x = 2.0f;
//d.y = 0.0f;
aabb->lowerBound += d;
aabb->upperBound += d;
b2Vec2 c0 = 0.5f * (aabb->lowerBound + aabb->upperBound);
b2Vec2 min; min.Set(-m_worldExtent, 0.0f);
b2Vec2 max; max.Set(m_worldExtent, 2.0f * m_worldExtent);
b2Vec2 c = b2Clamp(c0, min, max);
aabb->lowerBound += c - c0;
aabb->upperBound += c - c0;
}
void CreateProxy()
{
for (int32 i = 0; i < e_actorCount; ++i)
{
int32 j = rand() % e_actorCount;
Actor* actor = m_actors + j;
if (actor->proxyId == b2_nullNode)
{
GetRandomAABB(&actor->aabb);
actor->proxyId = m_tree.CreateProxy(actor->aabb, actor);
return;
}
}
}
void DestroyProxy()
{
for (int32 i = 0; i < e_actorCount; ++i)
{
int32 j = rand() % e_actorCount;
Actor* actor = m_actors + j;
if (actor->proxyId != b2_nullNode)
{
m_tree.DestroyProxy(actor->proxyId);
actor->proxyId = b2_nullNode;
return;
}
}
}
void MoveProxy()
{
for (int32 i = 0; i < e_actorCount; ++i)
{
int32 j = rand() % e_actorCount;
Actor* actor = m_actors + j;
if (actor->proxyId == b2_nullNode)
{
continue;
}
b2AABB aabb0 = actor->aabb;
MoveAABB(&actor->aabb);
b2Vec2 displacement = actor->aabb.GetCenter() - aabb0.GetCenter();
m_tree.MoveProxy(actor->proxyId, actor->aabb, displacement);
return;
}
}
void Action()
{
int32 choice = rand() % 20;
switch (choice)
{
case 0:
CreateProxy();
break;
case 1:
DestroyProxy();
break;
default:
MoveProxy();
}
}
void Query()
{
m_tree.Query(this, m_queryAABB);
for (int32 i = 0; i < e_actorCount; ++i)
{
if (m_actors[i].proxyId == b2_nullNode)
{
continue;
}
bool overlap = b2TestOverlap(m_queryAABB, m_actors[i].aabb);
B2_NOT_USED(overlap);
b2Assert(overlap == m_actors[i].overlap);
}
}
void RayCast()
{
m_rayActor = NULL;
b2RayCastInput input = m_rayCastInput;
// Ray cast against the dynamic tree.
m_tree.RayCast(this, input);
// Brute force ray cast.
Actor* bruteActor = NULL;
b2RayCastOutput bruteOutput;
for (int32 i = 0; i < e_actorCount; ++i)
{
if (m_actors[i].proxyId == b2_nullNode)
{
continue;
}
b2RayCastOutput output;
bool hit = m_actors[i].aabb.RayCast(&output, input);
if (hit)
{
bruteActor = m_actors + i;
bruteOutput = output;
input.maxFraction = output.fraction;
}
}
if (bruteActor != NULL)
{
b2Assert(bruteOutput.fraction == m_rayCastOutput.fraction);
}
}
float32 m_worldExtent;
float32 m_proxyExtent;
b2DynamicTree m_tree;
b2AABB m_queryAABB;
b2RayCastInput m_rayCastInput;
b2RayCastOutput m_rayCastOutput;
Actor* m_rayActor;
Actor m_actors[e_actorCount];
int32 m_stepCount;
bool m_automated;
};
#endif
@@ -0,0 +1,249 @@
/*
* Copyright (c) 2006-2010 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef EDGE_SHAPES_H
#define EDGE_SHAPES_H
class EdgeShapesCallback : public b2RayCastCallback
{
public:
EdgeShapesCallback()
{
m_fixture = NULL;
}
float32 ReportFixture( b2Fixture* fixture, const b2Vec2& point,
const b2Vec2& normal, float32 fraction)
{
m_fixture = fixture;
m_point = point;
m_normal = normal;
return fraction;
}
b2Fixture* m_fixture;
b2Vec2 m_point;
b2Vec2 m_normal;
};
class EdgeShapes : public Test
{
public:
enum
{
e_maxBodies = 256
};
EdgeShapes()
{
// Ground body
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
float32 x1 = -20.0f;
float32 y1 = 2.0f * cosf(x1 / 10.0f * b2_pi);
for (int32 i = 0; i < 80; ++i)
{
float32 x2 = x1 + 0.5f;
float32 y2 = 2.0f * cosf(x2 / 10.0f * b2_pi);
b2EdgeShape shape;
shape.Set(b2Vec2(x1, y1), b2Vec2(x2, y2));
ground->CreateFixture(&shape, 0.0f);
x1 = x2;
y1 = y2;
}
}
{
b2Vec2 vertices[3];
vertices[0].Set(-0.5f, 0.0f);
vertices[1].Set(0.5f, 0.0f);
vertices[2].Set(0.0f, 1.5f);
m_polygons[0].Set(vertices, 3);
}
{
b2Vec2 vertices[3];
vertices[0].Set(-0.1f, 0.0f);
vertices[1].Set(0.1f, 0.0f);
vertices[2].Set(0.0f, 1.5f);
m_polygons[1].Set(vertices, 3);
}
{
float32 w = 1.0f;
float32 b = w / (2.0f + b2Sqrt(2.0f));
float32 s = b2Sqrt(2.0f) * b;
b2Vec2 vertices[8];
vertices[0].Set(0.5f * s, 0.0f);
vertices[1].Set(0.5f * w, b);
vertices[2].Set(0.5f * w, b + s);
vertices[3].Set(0.5f * s, w);
vertices[4].Set(-0.5f * s, w);
vertices[5].Set(-0.5f * w, b + s);
vertices[6].Set(-0.5f * w, b);
vertices[7].Set(-0.5f * s, 0.0f);
m_polygons[2].Set(vertices, 8);
}
{
m_polygons[3].SetAsBox(0.5f, 0.5f);
}
{
m_circle.m_radius = 0.5f;
}
m_bodyIndex = 0;
memset(m_bodies, 0, sizeof(m_bodies));
m_angle = 0.0f;
}
void Create(int32 index)
{
if (m_bodies[m_bodyIndex] != NULL)
{
m_world->DestroyBody(m_bodies[m_bodyIndex]);
m_bodies[m_bodyIndex] = NULL;
}
b2BodyDef bd;
float32 x = RandomFloat(-10.0f, 10.0f);
float32 y = RandomFloat(10.0f, 20.0f);
bd.position.Set(x, y);
bd.angle = RandomFloat(-b2_pi, b2_pi);
bd.type = b2_dynamicBody;
if (index == 4)
{
bd.angularDamping = 0.02f;
}
m_bodies[m_bodyIndex] = m_world->CreateBody(&bd);
if (index < 4)
{
b2FixtureDef fd;
fd.shape = m_polygons + index;
fd.friction = 0.3f;
fd.density = 20.0f;
m_bodies[m_bodyIndex]->CreateFixture(&fd);
}
else
{
b2FixtureDef fd;
fd.shape = &m_circle;
fd.friction = 0.3f;
fd.density = 20.0f;
m_bodies[m_bodyIndex]->CreateFixture(&fd);
}
m_bodyIndex = (m_bodyIndex + 1) % e_maxBodies;
}
void DestroyBody()
{
for (int32 i = 0; i < e_maxBodies; ++i)
{
if (m_bodies[i] != NULL)
{
m_world->DestroyBody(m_bodies[i]);
m_bodies[i] = NULL;
return;
}
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case '1':
case '2':
case '3':
case '4':
case '5':
Create(key - '1');
break;
case 'd':
DestroyBody();
break;
}
}
void Step(Settings* settings)
{
bool advanceRay = settings->pause == 0 || settings->singleStep;
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "Press 1-5 to drop stuff");
m_textLine += 15;
float32 L = 25.0f;
b2Vec2 point1(0.0f, 10.0f);
b2Vec2 d(L * cosf(m_angle), -L * b2Abs(sinf(m_angle)));
b2Vec2 point2 = point1 + d;
EdgeShapesCallback callback;
m_world->RayCast(&callback, point1, point2);
if (callback.m_fixture)
{
m_debugDraw.DrawPoint(callback.m_point, 5.0f, b2Color(0.4f, 0.9f, 0.4f));
m_debugDraw.DrawSegment(point1, callback.m_point, b2Color(0.8f, 0.8f, 0.8f));
b2Vec2 head = callback.m_point + 0.5f * callback.m_normal;
m_debugDraw.DrawSegment(callback.m_point, head, b2Color(0.9f, 0.9f, 0.4f));
}
else
{
m_debugDraw.DrawSegment(point1, point2, b2Color(0.8f, 0.8f, 0.8f));
}
if (advanceRay)
{
m_angle += 0.25f * b2_pi / 180.0f;
}
}
static Test* Create()
{
return new EdgeShapes;
}
int32 m_bodyIndex;
b2Body* m_bodies[e_maxBodies];
b2PolygonShape m_polygons[4];
b2CircleShape m_circle;
float32 m_angle;
};
#endif
@@ -0,0 +1,109 @@
/*
* Copyright (c) 2006-2010 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef EDGE_TEST_H
#define EDGE_TEST_H
class EdgeTest : public Test
{
public:
EdgeTest()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2Vec2 v1(-10.0f, 0.0f), v2(-7.0f, -2.0f), v3(-4.0f, 0.0f);
b2Vec2 v4(0.0f, 0.0f), v5(4.0f, 0.0f), v6(7.0f, 2.0f), v7(10.0f, 0.0f);
b2EdgeShape shape;
shape.Set(v1, v2);
shape.m_hasVertex3 = true;
shape.m_vertex3 = v3;
ground->CreateFixture(&shape, 0.0f);
shape.Set(v2, v3);
shape.m_hasVertex0 = true;
shape.m_hasVertex3 = true;
shape.m_vertex0 = v1;
shape.m_vertex3 = v4;
ground->CreateFixture(&shape, 0.0f);
shape.Set(v3, v4);
shape.m_hasVertex0 = true;
shape.m_hasVertex3 = true;
shape.m_vertex0 = v2;
shape.m_vertex3 = v5;
ground->CreateFixture(&shape, 0.0f);
shape.Set(v4, v5);
shape.m_hasVertex0 = true;
shape.m_hasVertex3 = true;
shape.m_vertex0 = v3;
shape.m_vertex3 = v6;
ground->CreateFixture(&shape, 0.0f);
shape.Set(v5, v6);
shape.m_hasVertex0 = true;
shape.m_hasVertex3 = true;
shape.m_vertex0 = v4;
shape.m_vertex3 = v7;
ground->CreateFixture(&shape, 0.0f);
shape.Set(v6, v7);
shape.m_hasVertex0 = true;
shape.m_vertex0 = v5;
ground->CreateFixture(&shape, 0.0f);
}
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-0.5f, 0.6f);
bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
b2CircleShape shape;
shape.m_radius = 0.5f;
body->CreateFixture(&shape, 1.0f);
}
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(1.0f, 0.6f);
bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.5f);
body->CreateFixture(&shape, 1.0f);
}
}
static Test* Create()
{
return new EdgeTest;
}
};
#endif
@@ -0,0 +1,187 @@
/*
* Copyright (c) 2007-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef GEARS_H
#define GEARS_H
class Gears : public Test
{
public:
Gears()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(50.0f, 0.0f), b2Vec2(-50.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
// Gears co
{
b2CircleShape circle1;
circle1.m_radius = 1.0f;
b2PolygonShape box;
box.SetAsBox(0.5f, 5.0f);
b2CircleShape circle2;
circle2.m_radius = 2.0f;
b2BodyDef bd1;
bd1.type = b2_staticBody;
bd1.position.Set(10.0f, 9.0f);
b2Body* body1 = m_world->CreateBody(&bd1);
body1->CreateFixture(&circle1, 0.0f);
b2BodyDef bd2;
bd2.type = b2_dynamicBody;
bd2.position.Set(10.0f, 8.0f);
b2Body* body2 = m_world->CreateBody(&bd2);
body2->CreateFixture(&box, 5.0f);
b2BodyDef bd3;
bd3.type = b2_dynamicBody;
bd3.position.Set(10.0f, 6.0f);
b2Body* body3 = m_world->CreateBody(&bd3);
body3->CreateFixture(&circle2, 5.0f);
b2RevoluteJointDef jd1;
jd1.Initialize(body2, body1, bd1.position);
b2Joint* joint1 = m_world->CreateJoint(&jd1);
b2RevoluteJointDef jd2;
jd2.Initialize(body2, body3, bd3.position);
b2Joint* joint2 = m_world->CreateJoint(&jd2);
b2GearJointDef jd4;
jd4.bodyA = body1;
jd4.bodyB = body3;
jd4.joint1 = joint1;
jd4.joint2 = joint2;
jd4.ratio = circle2.m_radius / circle1.m_radius;
m_world->CreateJoint(&jd4);
}
{
b2CircleShape circle1;
circle1.m_radius = 1.0f;
b2CircleShape circle2;
circle2.m_radius = 2.0f;
b2PolygonShape box;
box.SetAsBox(0.5f, 5.0f);
b2BodyDef bd1;
bd1.type = b2_dynamicBody;
bd1.position.Set(-3.0f, 12.0f);
b2Body* body1 = m_world->CreateBody(&bd1);
body1->CreateFixture(&circle1, 5.0f);
b2RevoluteJointDef jd1;
jd1.bodyA = ground;
jd1.bodyB = body1;
jd1.localAnchorA = ground->GetLocalPoint(bd1.position);
jd1.localAnchorB = body1->GetLocalPoint(bd1.position);
jd1.referenceAngle = body1->GetAngle() - ground->GetAngle();
m_joint1 = (b2RevoluteJoint*)m_world->CreateJoint(&jd1);
b2BodyDef bd2;
bd2.type = b2_dynamicBody;
bd2.position.Set(0.0f, 12.0f);
b2Body* body2 = m_world->CreateBody(&bd2);
body2->CreateFixture(&circle2, 5.0f);
b2RevoluteJointDef jd2;
jd2.Initialize(ground, body2, bd2.position);
m_joint2 = (b2RevoluteJoint*)m_world->CreateJoint(&jd2);
b2BodyDef bd3;
bd3.type = b2_dynamicBody;
bd3.position.Set(2.5f, 12.0f);
b2Body* body3 = m_world->CreateBody(&bd3);
body3->CreateFixture(&box, 5.0f);
b2PrismaticJointDef jd3;
jd3.Initialize(ground, body3, bd3.position, b2Vec2(0.0f, 1.0f));
jd3.lowerTranslation = -5.0f;
jd3.upperTranslation = 5.0f;
jd3.enableLimit = true;
m_joint3 = (b2PrismaticJoint*)m_world->CreateJoint(&jd3);
b2GearJointDef jd4;
jd4.bodyA = body1;
jd4.bodyB = body2;
jd4.joint1 = m_joint1;
jd4.joint2 = m_joint2;
jd4.ratio = circle2.m_radius / circle1.m_radius;
m_joint4 = (b2GearJoint*)m_world->CreateJoint(&jd4);
b2GearJointDef jd5;
jd5.bodyA = body2;
jd5.bodyB = body3;
jd5.joint1 = m_joint2;
jd5.joint2 = m_joint3;
jd5.ratio = -1.0f / circle2.m_radius;
m_joint5 = (b2GearJoint*)m_world->CreateJoint(&jd5);
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 0:
break;
}
}
void Step(Settings* settings)
{
Test::Step(settings);
float32 ratio, value;
ratio = m_joint4->GetRatio();
value = m_joint1->GetJointAngle() + ratio * m_joint2->GetJointAngle();
m_debugDraw.DrawString(5, m_textLine, "theta1 + %4.2f * theta2 = %4.2f", (float) ratio, (float) value);
m_textLine += 15;
ratio = m_joint5->GetRatio();
value = m_joint2->GetJointAngle() + ratio * m_joint3->GetJointTranslation();
m_debugDraw.DrawString(5, m_textLine, "theta2 + %4.2f * delta = %4.2f", (float) ratio, (float) value);
m_textLine += 15;
}
static Test* Create()
{
return new Gears;
}
b2RevoluteJoint* m_joint1;
b2RevoluteJoint* m_joint2;
b2PrismaticJoint* m_joint3;
b2GearJoint* m_joint4;
b2GearJoint* m_joint5;
};
#endif
@@ -0,0 +1,120 @@
/*
* Copyright (c) 2008-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef ONE_SIDED_PLATFORM_H
#define ONE_SIDED_PLATFORM_H
class OneSidedPlatform : public Test
{
public:
enum State
{
e_unknown,
e_above,
e_below
};
OneSidedPlatform()
{
// Ground
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-20.0f, 0.0f), b2Vec2(20.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
// Platform
{
b2BodyDef bd;
bd.position.Set(0.0f, 10.0f);
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(3.0f, 0.5f);
m_platform = body->CreateFixture(&shape, 0.0f);
m_bottom = 10.0f - 0.5f;
m_top = 10.0f + 0.5f;
}
// Actor
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 12.0f);
b2Body* body = m_world->CreateBody(&bd);
m_radius = 0.5f;
b2CircleShape shape;
shape.m_radius = m_radius;
m_character = body->CreateFixture(&shape, 20.0f);
body->SetLinearVelocity(b2Vec2(0.0f, -50.0f));
m_state = e_unknown;
}
}
void PreSolve(b2Contact* contact, const b2Manifold* oldManifold)
{
Test::PreSolve(contact, oldManifold);
b2Fixture* fixtureA = contact->GetFixtureA();
b2Fixture* fixtureB = contact->GetFixtureB();
if (fixtureA != m_platform && fixtureA != m_character)
{
return;
}
if (fixtureB != m_platform && fixtureB != m_character)
{
return;
}
b2Vec2 position = m_character->GetBody()->GetPosition();
if (position.y < m_top + m_radius - 3.0f * b2_linearSlop)
{
contact->SetEnabled(false);
}
}
void Step(Settings* settings)
{
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "Press: (c) create a shape, (d) destroy a shape.");
m_textLine += 15;
}
static Test* Create()
{
return new OneSidedPlatform;
}
float32 m_radius, m_top, m_bottom;
State m_state;
b2Fixture* m_platform;
b2Fixture* m_character;
};
#endif
@@ -0,0 +1,169 @@
/*
* Copyright (c) 2006-2010 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef PINBALL_H
#define PINBALL_H
/// This tests bullet collision and provides an example of a gameplay scenario.
/// This also uses a loop shape.
class Pinball : public Test
{
public:
Pinball()
{
// Ground body
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2Vec2 vs[5];
vs[0].Set(0.0f, -2.0f);
vs[1].Set(8.0f, 6.0f);
vs[2].Set(8.0f, 20.0f);
vs[3].Set(-8.0f, 20.0f);
vs[4].Set(-8.0f, 6.0f);
b2ChainShape loop;
loop.CreateLoop(vs, 5);
b2FixtureDef fd;
fd.shape = &loop;
fd.density = 0.0f;
ground->CreateFixture(&fd);
}
// Flippers
{
b2Vec2 p1(-2.0f, 0.0f), p2(2.0f, 0.0f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = p1;
b2Body* leftFlipper = m_world->CreateBody(&bd);
bd.position = p2;
b2Body* rightFlipper = m_world->CreateBody(&bd);
b2PolygonShape box;
box.SetAsBox(1.75f, 0.1f);
b2FixtureDef fd;
fd.shape = &box;
fd.density = 1.0f;
leftFlipper->CreateFixture(&fd);
rightFlipper->CreateFixture(&fd);
b2RevoluteJointDef jd;
jd.bodyA = ground;
jd.localAnchorB.SetZero();
jd.enableMotor = true;
jd.maxMotorTorque = 1000.0f;
jd.enableLimit = true;
jd.motorSpeed = 0.0f;
jd.localAnchorA = p1;
jd.bodyB = leftFlipper;
jd.lowerAngle = -30.0f * b2_pi / 180.0f;
jd.upperAngle = 5.0f * b2_pi / 180.0f;
m_leftJoint = (b2RevoluteJoint*)m_world->CreateJoint(&jd);
jd.motorSpeed = 0.0f;
jd.localAnchorA = p2;
jd.bodyB = rightFlipper;
jd.lowerAngle = -5.0f * b2_pi / 180.0f;
jd.upperAngle = 30.0f * b2_pi / 180.0f;
m_rightJoint = (b2RevoluteJoint*)m_world->CreateJoint(&jd);
}
// Circle character
{
b2BodyDef bd;
bd.position.Set(1.0f, 15.0f);
bd.type = b2_dynamicBody;
bd.bullet = true;
m_ball = m_world->CreateBody(&bd);
b2CircleShape shape;
shape.m_radius = 0.2f;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
m_ball->CreateFixture(&fd);
}
m_button = false;
}
void Step()
{
if (m_button)
{
m_leftJoint->SetMotorSpeed(20.0f);
m_rightJoint->SetMotorSpeed(-20.0f);
}
else
{
m_leftJoint->SetMotorSpeed(-10.0f);
m_rightJoint->SetMotorSpeed(10.0f);
}
// Test::Step(settings);
//
// m_debugDraw.DrawString(5, m_textLine, "Press 'a' to control the flippers");
// m_textLine += 15;
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'a':
case 'A':
m_button = true;
break;
}
}
void KeyboardUp(unsigned char key)
{
switch (key)
{
case 'a':
case 'A':
m_button = false;
break;
}
}
static Test* Create()
{
return new Pinball;
}
b2RevoluteJoint* m_leftJoint;
b2RevoluteJoint* m_rightJoint;
b2Body* m_ball;
bool m_button;
};
#endif
@@ -0,0 +1,122 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef POLYCOLLISION_H
#define POLYCOLLISION_H
class PolyCollision : public Test
{
public:
PolyCollision()
{
{
m_polygonA.SetAsBox(0.2f, 0.4f);
m_transformA.Set(b2Vec2(0.0f, 0.0f), 0.0f);
}
{
m_polygonB.SetAsBox(0.5f, 0.5f);
m_positionB.Set(19.345284f, 1.5632932f);
m_angleB = 1.9160721f;
m_transformB.Set(m_positionB, m_angleB);
}
}
static Test* Create()
{
return new PolyCollision;
}
void Step(Settings* settings)
{
B2_NOT_USED(settings);
b2Manifold manifold;
b2CollidePolygons(&manifold, &m_polygonA, m_transformA, &m_polygonB, m_transformB);
b2WorldManifold worldManifold;
worldManifold.Initialize(&manifold, m_transformA, m_polygonA.m_radius, m_transformB, m_polygonB.m_radius);
m_debugDraw.DrawString(5, m_textLine, "point count = %d", manifold.pointCount);
m_textLine += 15;
{
b2Color color(0.9f, 0.9f, 0.9f);
b2Vec2 v[b2_maxPolygonVertices];
for (int32 i = 0; i < m_polygonA.m_vertexCount; ++i)
{
v[i] = b2Mul(m_transformA, m_polygonA.m_vertices[i]);
}
m_debugDraw.DrawPolygon(v, m_polygonA.m_vertexCount, color);
for (int32 i = 0; i < m_polygonB.m_vertexCount; ++i)
{
v[i] = b2Mul(m_transformB, m_polygonB.m_vertices[i]);
}
m_debugDraw.DrawPolygon(v, m_polygonB.m_vertexCount, color);
}
for (int32 i = 0; i < manifold.pointCount; ++i)
{
m_debugDraw.DrawPoint(worldManifold.points[i], 4.0f, b2Color(0.9f, 0.3f, 0.3f));
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'a':
m_positionB.x -= 0.1f;
break;
case 'd':
m_positionB.x += 0.1f;
break;
case 's':
m_positionB.y -= 0.1f;
break;
case 'w':
m_positionB.y += 0.1f;
break;
case 'q':
m_angleB += 0.1f * b2_pi;
break;
case 'e':
m_angleB -= 0.1f * b2_pi;
break;
}
m_transformB.Set(m_positionB, m_angleB);
}
b2PolygonShape m_polygonA;
b2PolygonShape m_polygonB;
b2Transform m_transformA;
b2Transform m_transformB;
b2Vec2 m_positionB;
float32 m_angleB;
};
#endif
@@ -0,0 +1,295 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef POLY_SHAPES_H
#define POLY_SHAPES_H
/// This tests stacking. It also shows how to use b2World::Query
/// and b2TestOverlap.
const int32 k_maxBodies = 256;
/// This callback is called by b2World::QueryAABB. We find all the fixtures
/// that overlap an AABB. Of those, we use b2TestOverlap to determine which fixtures
/// overlap a circle. Up to 4 overlapped fixtures will be highlighted with a yellow border.
class PolyShapesCallback : public b2QueryCallback
{
public:
enum
{
e_maxCount = 4
};
PolyShapesCallback()
{
m_count = 0;
}
void DrawFixture(b2Fixture* fixture)
{
b2Color color(0.95f, 0.95f, 0.6f);
const b2Transform& xf = fixture->GetBody()->GetTransform();
switch (fixture->GetType())
{
case b2Shape::e_circle:
{
b2CircleShape* circle = (b2CircleShape*)fixture->GetShape();
b2Vec2 center = b2Mul(xf, circle->m_p);
float32 radius = circle->m_radius;
m_debugDraw->DrawCircle(center, radius, color);
}
break;
case b2Shape::e_polygon:
{
b2PolygonShape* poly = (b2PolygonShape*)fixture->GetShape();
int32 vertexCount = poly->m_vertexCount;
b2Assert(vertexCount <= b2_maxPolygonVertices);
b2Vec2 vertices[b2_maxPolygonVertices];
for (int32 i = 0; i < vertexCount; ++i)
{
vertices[i] = b2Mul(xf, poly->m_vertices[i]);
}
m_debugDraw->DrawPolygon(vertices, vertexCount, color);
}
break;
default:
break;
}
}
/// Called for each fixture found in the query AABB.
/// @return false to terminate the query.
bool ReportFixture(b2Fixture* fixture)
{
if (m_count == e_maxCount)
{
return false;
}
b2Body* body = fixture->GetBody();
b2Shape* shape = fixture->GetShape();
bool overlap = b2TestOverlap(shape, 0, &m_circle, 0, body->GetTransform(), m_transform);
if (overlap)
{
DrawFixture(fixture);
++m_count;
}
return true;
}
b2CircleShape m_circle;
b2Transform m_transform;
b2Draw* m_debugDraw;
int32 m_count;
};
class PolyShapes : public Test
{
public:
PolyShapes()
{
// Ground body
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2Vec2 vertices[3];
vertices[0].Set(-0.5f, 0.0f);
vertices[1].Set(0.5f, 0.0f);
vertices[2].Set(0.0f, 1.5f);
m_polygons[0].Set(vertices, 3);
}
{
b2Vec2 vertices[3];
vertices[0].Set(-0.1f, 0.0f);
vertices[1].Set(0.1f, 0.0f);
vertices[2].Set(0.0f, 1.5f);
m_polygons[1].Set(vertices, 3);
}
{
float32 w = 1.0f;
float32 b = w / (2.0f + b2Sqrt(2.0f));
float32 s = b2Sqrt(2.0f) * b;
b2Vec2 vertices[8];
vertices[0].Set(0.5f * s, 0.0f);
vertices[1].Set(0.5f * w, b);
vertices[2].Set(0.5f * w, b + s);
vertices[3].Set(0.5f * s, w);
vertices[4].Set(-0.5f * s, w);
vertices[5].Set(-0.5f * w, b + s);
vertices[6].Set(-0.5f * w, b);
vertices[7].Set(-0.5f * s, 0.0f);
m_polygons[2].Set(vertices, 8);
}
{
m_polygons[3].SetAsBox(0.5f, 0.5f);
}
{
m_circle.m_radius = 0.5f;
}
m_bodyIndex = 0;
memset(m_bodies, 0, sizeof(m_bodies));
}
void Create(int32 index)
{
if (m_bodies[m_bodyIndex] != NULL)
{
m_world->DestroyBody(m_bodies[m_bodyIndex]);
m_bodies[m_bodyIndex] = NULL;
}
b2BodyDef bd;
bd.type = b2_dynamicBody;
float32 x = RandomFloat(-2.0f, 2.0f);
bd.position.Set(x, 10.0f);
bd.angle = RandomFloat(-b2_pi, b2_pi);
if (index == 4)
{
bd.angularDamping = 0.02f;
}
m_bodies[m_bodyIndex] = m_world->CreateBody(&bd);
if (index < 4)
{
b2FixtureDef fd;
fd.shape = m_polygons + index;
fd.density = 1.0f;
fd.friction = 0.3f;
m_bodies[m_bodyIndex]->CreateFixture(&fd);
}
else
{
b2FixtureDef fd;
fd.shape = &m_circle;
fd.density = 1.0f;
fd.friction = 0.3f;
m_bodies[m_bodyIndex]->CreateFixture(&fd);
}
m_bodyIndex = (m_bodyIndex + 1) % k_maxBodies;
}
void DestroyBody()
{
for (int32 i = 0; i < k_maxBodies; ++i)
{
if (m_bodies[i] != NULL)
{
m_world->DestroyBody(m_bodies[i]);
m_bodies[i] = NULL;
return;
}
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case '1':
case '2':
case '3':
case '4':
case '5':
Create(key - '1');
break;
case 'a':
for (int32 i = 0; i < k_maxBodies; i += 2)
{
if (m_bodies[i])
{
bool active = m_bodies[i]->IsActive();
m_bodies[i]->SetActive(!active);
}
}
break;
case 'd':
DestroyBody();
break;
}
}
void Step(Settings* settings)
{
Test::Step(settings);
PolyShapesCallback callback;
callback.m_circle.m_radius = 2.0f;
callback.m_circle.m_p.Set(0.0f, 1.1f);
callback.m_transform.SetIdentity();
callback.m_debugDraw = &m_debugDraw;
b2AABB aabb;
callback.m_circle.ComputeAABB(&aabb, callback.m_transform, 0);
m_world->QueryAABB(&callback, aabb);
b2Color color(0.4f, 0.7f, 0.8f);
m_debugDraw.DrawCircle(callback.m_circle.m_p, callback.m_circle.m_radius, color);
m_debugDraw.DrawString(5, m_textLine, "Press 1-5 to drop stuff");
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "Press 'a' to (de)activate some bodies");
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "Press 'd' to destroy a body");
m_textLine += 15;
}
static Test* Create()
{
return new PolyShapes;
}
int32 m_bodyIndex;
b2Body* m_bodies[k_maxBodies];
b2PolygonShape m_polygons[4];
b2CircleShape m_circle;
};
#endif
@@ -0,0 +1,107 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef PRISMATIC_H
#define PRISMATIC_H
// The motor in this test gets smoother with higher velocity iterations.
class Prismatic : public Test
{
public:
Prismatic()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(2.0f, 0.5f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-10.0f, 10.0f);
bd.angle = 0.5f * b2_pi;
bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 5.0f);
b2PrismaticJointDef pjd;
// Bouncy limit
b2Vec2 axis(2.0f, 1.0f);
axis.Normalize();
pjd.Initialize(ground, body, b2Vec2(0.0f, 0.0f), axis);
// Non-bouncy limit
//pjd.Initialize(ground, body, b2Vec2(-10.0f, 10.0f), b2Vec2(1.0f, 0.0f));
pjd.motorSpeed = 10.0f;
pjd.maxMotorForce = 10000.0f;
pjd.enableMotor = true;
pjd.lowerTranslation = 0.0f;
pjd.upperTranslation = 20.0f;
pjd.enableLimit = true;
m_joint = (b2PrismaticJoint*)m_world->CreateJoint(&pjd);
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'l':
m_joint->EnableLimit(!m_joint->IsLimitEnabled());
break;
case 'm':
m_joint->EnableMotor(!m_joint->IsMotorEnabled());
break;
case 's':
m_joint->SetMotorSpeed(-m_joint->GetMotorSpeed());
break;
}
}
void Step(Settings* settings)
{
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "Keys: (l) limits, (m) motors, (s) speed");
m_textLine += 15;
float32 force = m_joint->GetMotorForce(settings->hz);
m_debugDraw.DrawString(5, m_textLine, "Motor Force = %4.0f", (float) force);
m_textLine += 15;
}
static Test* Create()
{
return new Prismatic;
}
b2PrismaticJoint* m_joint;
};
#endif
@@ -0,0 +1,106 @@
/*
* Copyright (c) 2007-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef PULLEYS_H
#define PULLEYS_H
class Pulleys : public Test
{
public:
Pulleys()
{
float32 y = 16.0f;
float32 L = 12.0f;
float32 a = 1.0f;
float32 b = 2.0f;
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape edge;
edge.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
//ground->CreateFixture(&shape, 0.0f);
b2CircleShape circle;
circle.m_radius = 2.0f;
circle.m_p.Set(-10.0f, y + b + L);
ground->CreateFixture(&circle, 0.0f);
circle.m_p.Set(10.0f, y + b + L);
ground->CreateFixture(&circle, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(a, b);
b2BodyDef bd;
bd.type = b2_dynamicBody;
//bd.fixedRotation = true;
bd.position.Set(-10.0f, y);
b2Body* body1 = m_world->CreateBody(&bd);
body1->CreateFixture(&shape, 5.0f);
bd.position.Set(10.0f, y);
b2Body* body2 = m_world->CreateBody(&bd);
body2->CreateFixture(&shape, 5.0f);
b2PulleyJointDef pulleyDef;
b2Vec2 anchor1(-10.0f, y + b);
b2Vec2 anchor2(10.0f, y + b);
b2Vec2 groundAnchor1(-10.0f, y + b + L);
b2Vec2 groundAnchor2(10.0f, y + b + L);
pulleyDef.Initialize(body1, body2, groundAnchor1, groundAnchor2, anchor1, anchor2, 1.5f);
m_joint1 = (b2PulleyJoint*)m_world->CreateJoint(&pulleyDef);
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 0:
break;
}
}
void Step(Settings* settings)
{
Test::Step(settings);
float32 ratio = m_joint1->GetRatio();
float32 L = m_joint1->GetLengthA() + ratio * m_joint1->GetLengthB();
m_debugDraw.DrawString(5, m_textLine, "L1 + %4.2f * L2 = %4.2f", (float) ratio, (float) L);
m_textLine += 15;
}
static Test* Create()
{
return new Pulleys;
}
b2PulleyJoint* m_joint1;
};
#endif
@@ -0,0 +1,89 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef PYRAMID_H
#define PYRAMID_H
class Pyramid : public Test
{
public:
enum
{
e_count = 20
};
Pyramid()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
float32 a = 0.5f;
b2PolygonShape shape;
shape.SetAsBox(a, a);
b2Vec2 x(-7.0f, 0.75f);
b2Vec2 y;
b2Vec2 deltaX(0.5625f, 1.25f);
b2Vec2 deltaY(1.125f, 0.0f);
for (int32 i = 0; i < e_count; ++i)
{
y = x;
for (int32 j = i; j < e_count; ++j)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = y;
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 5.0f);
y += deltaY;
}
x += deltaX;
}
}
}
void Step(Settings* settings)
{
Test::Step(settings);
//b2DynamicTree* tree = &m_world->m_contactManager.m_broadPhase.m_tree;
//if (m_stepCount == 400)
//{
// tree->RebuildBottomUp();
//}
}
static Test* Create()
{
return new Pyramid;
}
};
#endif
@@ -0,0 +1,440 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef RAY_CAST_H
#define RAY_CAST_H
// This test demonstrates how to use the world ray-cast feature.
// NOTE: we are intentionally filtering one of the polygons, therefore
// the ray will always miss one type of polygon.
// This callback finds the closest hit. Polygon 0 is filtered.
class RayCastClosestCallback : public b2RayCastCallback
{
public:
RayCastClosestCallback()
{
m_hit = false;
}
float32 ReportFixture( b2Fixture* fixture, const b2Vec2& point,
const b2Vec2& normal, float32 fraction)
{
b2Body* body = fixture->GetBody();
void* userData = body->GetUserData();
if (userData)
{
int32 index = *(int32*)userData;
if (index == 0)
{
// filter
return -1.0f;
}
}
m_hit = true;
m_point = point;
m_normal = normal;
return fraction;
}
bool m_hit;
b2Vec2 m_point;
b2Vec2 m_normal;
};
// This callback finds any hit. Polygon 0 is filtered.
class RayCastAnyCallback : public b2RayCastCallback
{
public:
RayCastAnyCallback()
{
m_hit = false;
}
float32 ReportFixture( b2Fixture* fixture, const b2Vec2& point,
const b2Vec2& normal, float32 fraction)
{
b2Body* body = fixture->GetBody();
void* userData = body->GetUserData();
if (userData)
{
int32 index = *(int32*)userData;
if (index == 0)
{
// filter
return -1.0f;
}
}
m_hit = true;
m_point = point;
m_normal = normal;
return 0.0f;
}
bool m_hit;
b2Vec2 m_point;
b2Vec2 m_normal;
};
// This ray cast collects multiple hits along the ray. Polygon 0 is filtered.
class RayCastMultipleCallback : public b2RayCastCallback
{
public:
enum
{
e_maxCount = 3
};
RayCastMultipleCallback()
{
m_count = 0;
}
float32 ReportFixture( b2Fixture* fixture, const b2Vec2& point,
const b2Vec2& normal, float32 fraction)
{
b2Body* body = fixture->GetBody();
void* userData = body->GetUserData();
if (userData)
{
int32 index = *(int32*)userData;
if (index == 0)
{
// filter
return -1.0f;
}
}
b2Assert(m_count < e_maxCount);
m_points[m_count] = point;
m_normals[m_count] = normal;
++m_count;
if (m_count == e_maxCount)
{
return 0.0f;
}
return 1.0f;
}
b2Vec2 m_points[e_maxCount];
b2Vec2 m_normals[e_maxCount];
int32 m_count;
};
class RayCast : public Test
{
public:
enum
{
e_maxBodies = 256
};
enum Mode
{
e_closest,
e_any,
e_multiple
};
RayCast()
{
// Ground body
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2Vec2 vertices[3];
vertices[0].Set(-0.5f, 0.0f);
vertices[1].Set(0.5f, 0.0f);
vertices[2].Set(0.0f, 1.5f);
m_polygons[0].Set(vertices, 3);
}
{
b2Vec2 vertices[3];
vertices[0].Set(-0.1f, 0.0f);
vertices[1].Set(0.1f, 0.0f);
vertices[2].Set(0.0f, 1.5f);
m_polygons[1].Set(vertices, 3);
}
{
float32 w = 1.0f;
float32 b = w / (2.0f + b2Sqrt(2.0f));
float32 s = b2Sqrt(2.0f) * b;
b2Vec2 vertices[8];
vertices[0].Set(0.5f * s, 0.0f);
vertices[1].Set(0.5f * w, b);
vertices[2].Set(0.5f * w, b + s);
vertices[3].Set(0.5f * s, w);
vertices[4].Set(-0.5f * s, w);
vertices[5].Set(-0.5f * w, b + s);
vertices[6].Set(-0.5f * w, b);
vertices[7].Set(-0.5f * s, 0.0f);
m_polygons[2].Set(vertices, 8);
}
{
m_polygons[3].SetAsBox(0.5f, 0.5f);
}
{
m_circle.m_radius = 0.5f;
}
m_bodyIndex = 0;
memset(m_bodies, 0, sizeof(m_bodies));
m_angle = 0.0f;
m_mode = e_closest;
}
void Create(int32 index)
{
if (m_bodies[m_bodyIndex] != NULL)
{
m_world->DestroyBody(m_bodies[m_bodyIndex]);
m_bodies[m_bodyIndex] = NULL;
}
b2BodyDef bd;
float32 x = RandomFloat(-10.0f, 10.0f);
float32 y = RandomFloat(0.0f, 20.0f);
bd.position.Set(x, y);
bd.angle = RandomFloat(-b2_pi, b2_pi);
m_userData[m_bodyIndex] = index;
bd.userData = m_userData + m_bodyIndex;
if (index == 4)
{
bd.angularDamping = 0.02f;
}
m_bodies[m_bodyIndex] = m_world->CreateBody(&bd);
if (index < 4)
{
b2FixtureDef fd;
fd.shape = m_polygons + index;
fd.friction = 0.3f;
m_bodies[m_bodyIndex]->CreateFixture(&fd);
}
else
{
b2FixtureDef fd;
fd.shape = &m_circle;
fd.friction = 0.3f;
m_bodies[m_bodyIndex]->CreateFixture(&fd);
}
m_bodyIndex = (m_bodyIndex + 1) % e_maxBodies;
}
void DestroyBody()
{
for (int32 i = 0; i < e_maxBodies; ++i)
{
if (m_bodies[i] != NULL)
{
m_world->DestroyBody(m_bodies[i]);
m_bodies[i] = NULL;
return;
}
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case '1':
case '2':
case '3':
case '4':
case '5':
Create(key - '1');
break;
case 'd':
DestroyBody();
break;
case 'm':
if (m_mode == e_closest)
{
m_mode = e_any;
}
else if (m_mode == e_any)
{
m_mode = e_multiple;
}
else if (m_mode == e_multiple)
{
m_mode = e_closest;
}
}
}
void Step(Settings* settings)
{
bool advanceRay = settings->pause == 0 || settings->singleStep;
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "Press 1-5 to drop stuff, m to change the mode");
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "Mode = %d", m_mode);
m_textLine += 15;
float32 L = 11.0f;
b2Vec2 point1(0.0f, 10.0f);
b2Vec2 d(L * cosf(m_angle), L * sinf(m_angle));
b2Vec2 point2 = point1 + d;
if (m_mode == e_closest)
{
RayCastClosestCallback callback;
m_world->RayCast(&callback, point1, point2);
if (callback.m_hit)
{
m_debugDraw.DrawPoint(callback.m_point, 5.0f, b2Color(0.4f, 0.9f, 0.4f));
m_debugDraw.DrawSegment(point1, callback.m_point, b2Color(0.8f, 0.8f, 0.8f));
b2Vec2 head = callback.m_point + 0.5f * callback.m_normal;
m_debugDraw.DrawSegment(callback.m_point, head, b2Color(0.9f, 0.9f, 0.4f));
}
else
{
m_debugDraw.DrawSegment(point1, point2, b2Color(0.8f, 0.8f, 0.8f));
}
}
else if (m_mode == e_any)
{
RayCastAnyCallback callback;
m_world->RayCast(&callback, point1, point2);
if (callback.m_hit)
{
m_debugDraw.DrawPoint(callback.m_point, 5.0f, b2Color(0.4f, 0.9f, 0.4f));
m_debugDraw.DrawSegment(point1, callback.m_point, b2Color(0.8f, 0.8f, 0.8f));
b2Vec2 head = callback.m_point + 0.5f * callback.m_normal;
m_debugDraw.DrawSegment(callback.m_point, head, b2Color(0.9f, 0.9f, 0.4f));
}
else
{
m_debugDraw.DrawSegment(point1, point2, b2Color(0.8f, 0.8f, 0.8f));
}
}
else if (m_mode == e_multiple)
{
RayCastMultipleCallback callback;
m_world->RayCast(&callback, point1, point2);
m_debugDraw.DrawSegment(point1, point2, b2Color(0.8f, 0.8f, 0.8f));
for (int32 i = 0; i < callback.m_count; ++i)
{
b2Vec2 p = callback.m_points[i];
b2Vec2 n = callback.m_normals[i];
m_debugDraw.DrawPoint(p, 5.0f, b2Color(0.4f, 0.9f, 0.4f));
m_debugDraw.DrawSegment(point1, p, b2Color(0.8f, 0.8f, 0.8f));
b2Vec2 head = p + 0.5f * n;
m_debugDraw.DrawSegment(p, head, b2Color(0.9f, 0.9f, 0.4f));
}
}
if (advanceRay)
{
m_angle += 0.25f * b2_pi / 180.0f;
}
#if 0
// This case was failing.
{
b2Vec2 vertices[4];
//vertices[0].Set(-22.875f, -3.0f);
//vertices[1].Set(22.875f, -3.0f);
//vertices[2].Set(22.875f, 3.0f);
//vertices[3].Set(-22.875f, 3.0f);
b2PolygonShape shape;
//shape.Set(vertices, 4);
shape.SetAsBox(22.875f, 3.0f);
b2RayCastInput input;
input.p1.Set(10.2725f,1.71372f);
input.p2.Set(10.2353f,2.21807f);
//input.maxFraction = 0.567623f;
input.maxFraction = 0.56762173f;
b2Transform xf;
xf.SetIdentity();
xf.position.Set(23.0f, 5.0f);
b2RayCastOutput output;
bool hit;
hit = shape.RayCast(&output, input, xf);
hit = false;
b2Color color(1.0f, 1.0f, 1.0f);
b2Vec2 vs[4];
for (int32 i = 0; i < 4; ++i)
{
vs[i] = b2Mul(xf, shape.m_vertices[i]);
}
m_debugDraw.DrawPolygon(vs, 4, color);
m_debugDraw.DrawSegment(input.p1, input.p2, color);
}
#endif
}
static Test* Create()
{
return new RayCast;
}
int32 m_bodyIndex;
b2Body* m_bodies[e_maxBodies];
int32 m_userData[e_maxBodies];
b2PolygonShape m_polygons[4];
b2CircleShape m_circle;
float32 m_angle;
Mode m_mode;
};
#endif
@@ -0,0 +1,166 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef REVOLUTE_H
#define REVOLUTE_H
class Revolute : public Test
{
public:
Revolute()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
b2FixtureDef fd;
fd.shape = &shape;
//fd.filter.categoryBits = 2;
ground->CreateFixture(&fd);
}
{
b2CircleShape shape;
shape.m_radius = 0.5f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
b2RevoluteJointDef rjd;
bd.position.Set(-10.0f, 20.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 5.0f);
float32 w = 100.0f;
body->SetAngularVelocity(w);
body->SetLinearVelocity(b2Vec2(-8.0f * w, 0.0f));
rjd.Initialize(ground, body, b2Vec2(-10.0f, 12.0f));
rjd.motorSpeed = 1.0f * b2_pi;
rjd.maxMotorTorque = 10000.0f;
rjd.enableMotor = false;
rjd.lowerAngle = -0.25f * b2_pi;
rjd.upperAngle = 0.5f * b2_pi;
rjd.enableLimit = true;
rjd.collideConnected = true;
m_joint = (b2RevoluteJoint*)m_world->CreateJoint(&rjd);
}
{
b2CircleShape circle_shape;
circle_shape.m_radius = 3.0f;
b2BodyDef circle_bd;
circle_bd.type = b2_dynamicBody;
circle_bd.position.Set(5.0f, 30.0f);
b2FixtureDef fd;
fd.density = 5.0f;
fd.filter.maskBits = 1;
fd.shape = &circle_shape;
m_ball = m_world->CreateBody(&circle_bd);
m_ball->CreateFixture(&fd);
b2PolygonShape polygon_shape;
polygon_shape.SetAsBox(10.0f, 0.2f, b2Vec2 (-10.0f, 0.0f), 0.0f);
b2BodyDef polygon_bd;
polygon_bd.position.Set(20.0f, 10.0f);
polygon_bd.type = b2_dynamicBody;
polygon_bd.bullet = true;
b2Body* polygon_body = m_world->CreateBody(&polygon_bd);
polygon_body->CreateFixture(&polygon_shape, 2.0f);
b2RevoluteJointDef rjd;
rjd.Initialize(ground, polygon_body, b2Vec2(20.0f, 10.0f));
rjd.lowerAngle = -0.25f * b2_pi;
rjd.upperAngle = 0.0f * b2_pi;
rjd.enableLimit = true;
m_world->CreateJoint(&rjd);
}
// Tests mass computation of a small object far from the origin
{
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
b2Body* body = m_world->CreateBody(&bodyDef);
b2PolygonShape polyShape;
b2Vec2 verts[3];
verts[0].Set( 17.63f, 36.31f );
verts[1].Set( 17.52f, 36.69f );
verts[2].Set( 17.19f, 36.36f );
polyShape.Set(verts, 3);
b2FixtureDef polyFixtureDef;
polyFixtureDef.shape = &polyShape;
polyFixtureDef.density = 1;
body->CreateFixture(&polyFixtureDef); //assertion hits inside here
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'l':
m_joint->EnableLimit(!m_joint->IsLimitEnabled());
break;
case 'm':
m_joint->EnableMotor(!m_joint->IsMotorEnabled());
break;
}
}
void Step(Settings* settings)
{
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "Keys: (l) limits, (m) motor");
m_textLine += 15;
//if (m_stepCount == 360)
//{
// m_ball->SetTransform(b2Vec2(0.0f, 0.5f), 0.0f);
//}
//float32 torque1 = m_joint1->GetMotorTorque();
//m_debugDraw.DrawString(5, m_textLine, "Motor Torque = %4.0f, %4.0f : Motor Force = %4.0f", (float) torque1, (float) torque2, (float) force3);
//m_textLine += 15;
}
static Test* Create()
{
return new Revolute;
}
b2Body* m_ball;
b2RevoluteJoint* m_joint;
};
#endif
@@ -0,0 +1,101 @@
/*
* Copyright (c) 2011 Erin Catto http://box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef ROPE_H
#define ROPE_H
///
class Rope : public Test
{
public:
Rope()
{
const int32 N = 40;
b2Vec2 vertices[N];
float32 masses[N];
for (int32 i = 0; i < N; ++i)
{
vertices[i].Set(0.0f, 20.0f - 0.25f * i);
masses[i] = 1.0f;
}
masses[0] = 0.0f;
masses[1] = 0.0f;
b2RopeDef def;
def.vertices = vertices;
def.count = N;
def.gravity.Set(0.0f, -10.0f);
def.masses = masses;
def.damping = 0.1f;
def.k2 = 1.0f;
def.k3 = 0.5f;
m_rope.Initialize(&def);
m_angle = 0.0f;
m_rope.SetAngle(m_angle);
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'q':
m_angle = b2Max(-b2_pi, m_angle - 0.05f * b2_pi);
m_rope.SetAngle(m_angle);
break;
case 'e':
m_angle = b2Min(b2_pi, m_angle + 0.05f * b2_pi);
m_rope.SetAngle(m_angle);
break;
}
}
void Step(Settings* settings)
{
float32 dt = settings->hz > 0.0f ? 1.0f / settings->hz : 0.0f;
if (settings->pause == 1 && settings->singleStep == 0)
{
dt = 0.0f;
}
m_rope.Step(dt, 1);
Test::Step(settings);
m_rope.Draw(&m_debugDraw);
m_debugDraw.DrawString(5, m_textLine, "Press (q,e) to adjust target angle");
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "Target angle = %g degrees", m_angle * 180.0f / b2_pi);
m_textLine += 15;
}
static Test* Create()
{
return new Rope;
}
b2Rope m_rope;
float32 m_angle;
};
#endif
@@ -0,0 +1,145 @@
/*
* Copyright (c) 2006-2010 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef ROPE_JOINT_H
#define ROPE_JOINT_H
/// This test shows how a rope joint can be used to stabilize a chain of
/// bodies with a heavy payload. Notice that the rope joint just prevents
/// excessive stretching and has no other effect.
/// By disabling the rope joint you can see that the Box2D solver has trouble
/// supporting heavy bodies with light bodies. Try playing around with the
/// densities, time step, and iterations to see how they affect stability.
/// This test also shows how to use contact filtering. Filtering is configured
/// so that the payload does not collide with the chain.
class RopeJoint : public Test
{
public:
RopeJoint()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
fd.friction = 0.2f;
fd.filter.categoryBits = 0x0001;
fd.filter.maskBits = 0xFFFF & ~0x0002;
b2RevoluteJointDef jd;
jd.collideConnected = false;
const int32 N = 10;
const float32 y = 15.0f;
m_ropeDef.localAnchorA.Set(0.0f, y);
b2Body* prevBody = ground;
for (int32 i = 0; i < N; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.5f + 1.0f * i, y);
if (i == N - 1)
{
shape.SetAsBox(1.5f, 1.5f);
fd.density = 100.0f;
fd.filter.categoryBits = 0x0002;
bd.position.Set(1.0f * i, y);
bd.angularDamping = 0.4f;
}
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
b2Vec2 anchor(float32(i), y);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
prevBody = body;
}
m_ropeDef.localAnchorB.SetZero();
float32 extraLength = 0.01f;
m_ropeDef.maxLength = N - 1.0f + extraLength;
m_ropeDef.bodyB = prevBody;
}
{
m_ropeDef.bodyA = ground;
m_rope = m_world->CreateJoint(&m_ropeDef);
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'j':
if (m_rope)
{
m_world->DestroyJoint(m_rope);
m_rope = NULL;
}
else
{
m_rope = m_world->CreateJoint(&m_ropeDef);
}
break;
}
}
void Step(Settings* settings)
{
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "Press (j) to toggle the rope joint.");
m_textLine += 15;
if (m_rope)
{
m_debugDraw.DrawString(5, m_textLine, "Rope ON");
}
else
{
m_debugDraw.DrawString(5, m_textLine, "Rope OFF");
}
m_textLine += 15;
}
static Test* Create()
{
return new RopeJoint;
}
b2RopeJointDef m_ropeDef;
b2Joint* m_rope;
};
#endif
@@ -0,0 +1,181 @@
/*
* Copyright (c) 2008-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef SENSOR_TEST_H
#define SENSOR_TEST_H
// This is used to test sensor shapes.
class SensorTest : public Test
{
public:
enum
{
e_count = 7
};
SensorTest()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
{
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
#if 0
{
b2FixtureDef sd;
sd.SetAsBox(10.0f, 2.0f, b2Vec2(0.0f, 20.0f), 0.0f);
sd.isSensor = true;
m_sensor = ground->CreateFixture(&sd);
}
#else
{
b2CircleShape shape;
shape.m_radius = 5.0f;
shape.m_p.Set(0.0f, 10.0f);
b2FixtureDef fd;
fd.shape = &shape;
fd.isSensor = true;
m_sensor = ground->CreateFixture(&fd);
}
#endif
}
{
b2CircleShape shape;
shape.m_radius = 1.0f;
for (int32 i = 0; i < e_count; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-10.0f + 3.0f * i, 20.0f);
bd.userData = m_touching + i;
m_touching[i] = false;
m_bodies[i] = m_world->CreateBody(&bd);
m_bodies[i]->CreateFixture(&shape, 1.0f);
}
}
}
// Implement contact listener.
void BeginContact(b2Contact* contact)
{
b2Fixture* fixtureA = contact->GetFixtureA();
b2Fixture* fixtureB = contact->GetFixtureB();
if (fixtureA == m_sensor)
{
void* userData = fixtureB->GetBody()->GetUserData();
if (userData)
{
bool* touching = (bool*)userData;
*touching = true;
}
}
if (fixtureB == m_sensor)
{
void* userData = fixtureA->GetBody()->GetUserData();
if (userData)
{
bool* touching = (bool*)userData;
*touching = true;
}
}
}
// Implement contact listener.
void EndContact(b2Contact* contact)
{
b2Fixture* fixtureA = contact->GetFixtureA();
b2Fixture* fixtureB = contact->GetFixtureB();
if (fixtureA == m_sensor)
{
void* userData = fixtureB->GetBody()->GetUserData();
if (userData)
{
bool* touching = (bool*)userData;
*touching = false;
}
}
if (fixtureB == m_sensor)
{
void* userData = fixtureA->GetBody()->GetUserData();
if (userData)
{
bool* touching = (bool*)userData;
*touching = false;
}
}
}
void Step(Settings* settings)
{
Test::Step(settings);
// Traverse the contact results. Apply a force on shapes
// that overlap the sensor.
for (int32 i = 0; i < e_count; ++i)
{
if (m_touching[i] == false)
{
continue;
}
b2Body* body = m_bodies[i];
b2Body* ground = m_sensor->GetBody();
b2CircleShape* circle = (b2CircleShape*)m_sensor->GetShape();
b2Vec2 center = ground->GetWorldPoint(circle->m_p);
b2Vec2 position = body->GetPosition();
b2Vec2 d = center - position;
if (d.LengthSquared() < FLT_EPSILON * FLT_EPSILON)
{
continue;
}
d.Normalize();
b2Vec2 F = 100.0f * d;
body->ApplyForce(F, position);
}
}
static Test* Create()
{
return new SensorTest;
}
b2Fixture* m_sensor;
b2Body* m_bodies[e_count];
bool m_touching[e_count];
};
#endif
@@ -0,0 +1,105 @@
/*
* Copyright (c) 2008-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef SHAPE_EDITING_H
#define SHAPE_EDITING_H
class ShapeEditing : public Test
{
public:
ShapeEditing()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 10.0f);
m_body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(4.0f, 4.0f, b2Vec2(0.0f, 0.0f), 0.0f);
m_fixture1 = m_body->CreateFixture(&shape, 10.0f);
m_fixture2 = NULL;
m_sensor = false;
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'c':
if (m_fixture2 == NULL)
{
b2CircleShape shape;
shape.m_radius = 3.0f;
shape.m_p.Set(0.5f, -4.0f);
m_fixture2 = m_body->CreateFixture(&shape, 10.0f);
m_body->SetAwake(true);
}
break;
case 'd':
if (m_fixture2 != NULL)
{
m_body->DestroyFixture(m_fixture2);
m_fixture2 = NULL;
m_body->SetAwake(true);
}
break;
case 's':
if (m_fixture2 != NULL)
{
m_sensor = !m_sensor;
m_fixture2->SetSensor(m_sensor);
}
break;
}
}
void Step(Settings* settings)
{
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "Press: (c) create a shape, (d) destroy a shape.");
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "sensor = %d", m_sensor);
m_textLine += 15;
}
static Test* Create()
{
return new ShapeEditing;
}
b2Body* m_body;
b2Fixture* m_fixture1;
b2Fixture* m_fixture2;
bool m_sensor;
};
#endif
@@ -0,0 +1,156 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef SLIDER_CRANK_H
#define SLIDER_CRANK_H
// A motor driven slider crank with joint friction.
class SliderCrank : public Test
{
public:
SliderCrank()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2Body* prevBody = ground;
// Define crank.
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 2.0f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 7.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 2.0f);
b2RevoluteJointDef rjd;
rjd.Initialize(prevBody, body, b2Vec2(0.0f, 5.0f));
rjd.motorSpeed = 1.0f * b2_pi;
rjd.maxMotorTorque = 10000.0f;
rjd.enableMotor = true;
m_joint1 = (b2RevoluteJoint*)m_world->CreateJoint(&rjd);
prevBody = body;
}
// Define follower.
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 4.0f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 13.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 2.0f);
b2RevoluteJointDef rjd;
rjd.Initialize(prevBody, body, b2Vec2(0.0f, 9.0f));
rjd.enableMotor = false;
m_world->CreateJoint(&rjd);
prevBody = body;
}
// Define piston
{
b2PolygonShape shape;
shape.SetAsBox(1.5f, 1.5f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.fixedRotation = true;
bd.position.Set(0.0f, 17.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 2.0f);
b2RevoluteJointDef rjd;
rjd.Initialize(prevBody, body, b2Vec2(0.0f, 17.0f));
m_world->CreateJoint(&rjd);
b2PrismaticJointDef pjd;
pjd.Initialize(ground, body, b2Vec2(0.0f, 17.0f), b2Vec2(0.0f, 1.0f));
pjd.maxMotorForce = 1000.0f;
pjd.enableMotor = true;
m_joint2 = (b2PrismaticJoint*)m_world->CreateJoint(&pjd);
}
// Create a payload
{
b2PolygonShape shape;
shape.SetAsBox(1.5f, 1.5f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 23.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 2.0f);
}
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'f':
m_joint2->EnableMotor(!m_joint2->IsMotorEnabled());
m_joint2->GetBodyB()->SetAwake(true);
break;
case 'm':
m_joint1->EnableMotor(!m_joint1->IsMotorEnabled());
m_joint1->GetBodyB()->SetAwake(true);
break;
}
}
void Step(Settings* settings)
{
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "Keys: (f) toggle friction, (m) toggle motor");
m_textLine += 15;
float32 torque = m_joint1->GetMotorTorque(settings->hz);
m_debugDraw.DrawString(5, m_textLine, "Motor Torque = %5.0f", (float) torque);
m_textLine += 15;
}
static Test* Create()
{
return new SliderCrank;
}
b2RevoluteJoint* m_joint1;
b2PrismaticJoint* m_joint2;
};
#endif
@@ -0,0 +1,86 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef SPHERE_STACK_H
#define SPHERE_STACK_H
class SphereStack : public Test
{
public:
enum
{
e_count = 10
};
SphereStack()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2CircleShape shape;
shape.m_radius = 1.0f;
for (int32 i = 0; i < e_count; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0, 4.0f + 3.0f * i);
m_bodies[i] = m_world->CreateBody(&bd);
m_bodies[i]->CreateFixture(&shape, 1.0f);
m_bodies[i]->SetLinearVelocity(b2Vec2(0.0f, -50.0f));
}
}
}
void Step(Settings* settings)
{
Test::Step(settings);
//for (int32 i = 0; i < e_count; ++i)
//{
// printf("%g ", m_bodies[i]->GetWorldCenter().y);
//}
//for (int32 i = 0; i < e_count; ++i)
//{
// printf("%g ", m_bodies[i]->GetLinearVelocity().y);
//}
//printf("\n");
}
static Test* Create()
{
return new SphereStack;
}
b2Body* m_bodies[e_count];
};
#endif
@@ -0,0 +1,125 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#include "../Framework/Test.h"
#include "../Framework/Render.h"
#ifdef __APPLE__
#include <GLUT/glut.h>
#else
#include "freeglut/freeglut.h"
#endif
#include <cstring>
using namespace std;
#include "AddPair.h"
#include "ApplyForce.h"
#include "BodyTypes.h"
#include "Breakable.h"
#include "Bridge.h"
#include "BulletTest.h"
#include "Cantilever.h"
#include "Car.h"
#include "ContinuousTest.h"
#include "Chain.h"
#include "CharacterCollision.h"
#include "CollisionFiltering.h"
#include "CollisionProcessing.h"
#include "CompoundShapes.h"
#include "Confined.h"
#include "DistanceTest.h"
#include "Dominos.h"
#include "DumpShell.h"
#include "DynamicTreeTest.h"
#include "EdgeShapes.h"
#include "EdgeTest.h"
#include "Gears.h"
#include "OneSidedPlatform.h"
#include "Pinball.h"
#include "PolyCollision.h"
#include "PolyShapes.h"
#include "Prismatic.h"
#include "Pulleys.h"
#include "Pyramid.h"
#include "RayCast.h"
#include "Revolute.h"
//#include "Rope.h"
#include "RopeJoint.h"
#include "SensorTest.h"
#include "ShapeEditing.h"
#include "SliderCrank.h"
#include "SphereStack.h"
#include "TheoJansen.h"
#include "Tiles.h"
#include "TimeOfImpact.h"
#include "Tumbler.h"
#include "VaryingFriction.h"
#include "VaryingRestitution.h"
#include "VerticalStack.h"
#include "Web.h"
TestEntry g_testEntries[] =
{
{"Tumbler", Tumbler::Create},
{"Tiles", Tiles::Create},
{"Dump Shell", DumpShell::Create},
{"Gears", Gears::Create},
{"Cantilever", Cantilever::Create},
{"Varying Restitution", VaryingRestitution::Create},
{"Character Collision", CharacterCollision::Create},
{"Edge Test", EdgeTest::Create},
{"Body Types", BodyTypes::Create},
{"Shape Editing", ShapeEditing::Create},
{"Car", Car::Create},
{"Apply Force", ApplyForce::Create},
{"Prismatic", Prismatic::Create},
{"Vertical Stack", VerticalStack::Create},
{"SphereStack", SphereStack::Create},
{"Revolute", Revolute::Create},
{"Pulleys", Pulleys::Create},
{"Polygon Shapes", PolyShapes::Create},
//{"Rope", Rope::Create},
{"Web", Web::Create},
{"RopeJoint", RopeJoint::Create},
{"One-Sided Platform", OneSidedPlatform::Create},
{"Pinball", Pinball::Create},
{"Bullet Test", BulletTest::Create},
{"Continuous Test", ContinuousTest::Create},
{"Time of Impact", TimeOfImpact::Create},
{"Ray-Cast", RayCast::Create},
{"Confined", Confined::Create},
{"Pyramid", Pyramid::Create},
{"Theo Jansen's Walker", TheoJansen::Create},
{"Edge Shapes", EdgeShapes::Create},
{"PolyCollision", PolyCollision::Create},
{"Bridge", Bridge::Create},
{"Breakable", Breakable::Create},
{"Chain", Chain::Create},
{"Collision Filtering", CollisionFiltering::Create},
{"Collision Processing", CollisionProcessing::Create},
{"Compound Shapes", CompoundShapes::Create},
{"Distance Test", DistanceTest::Create},
{"Dominos", Dominos::Create},
{"Dynamic Tree", DynamicTreeTest::Create},
{"Sensor Test", SensorTest::Create},
{"Slider Crank", SliderCrank::Create},
{"Varying Friction", VaryingFriction::Create},
{"Add Pair Stress Test", AddPair::Create},
{NULL, NULL}
};
@@ -0,0 +1,256 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
// Inspired by a contribution by roman_m
// Dimensions scooped from APE (http://www.cove.org/ape/index.htm)
#ifndef THEO_JANSEN_H
#define THEO_JANSEN_H
class TheoJansen : public Test
{
public:
void CreateLeg(float32 s, const b2Vec2& wheelAnchor)
{
b2Vec2 p1(5.4f * s, -6.1f);
b2Vec2 p2(7.2f * s, -1.2f);
b2Vec2 p3(4.3f * s, -1.9f);
b2Vec2 p4(3.1f * s, 0.8f);
b2Vec2 p5(6.0f * s, 1.5f);
b2Vec2 p6(2.5f * s, 3.7f);
b2FixtureDef fd1, fd2;
fd1.filter.groupIndex = -1;
fd2.filter.groupIndex = -1;
fd1.density = 1.0f;
fd2.density = 1.0f;
b2PolygonShape poly1, poly2;
if (s > 0.0f)
{
b2Vec2 vertices[3];
vertices[0] = p1;
vertices[1] = p2;
vertices[2] = p3;
poly1.Set(vertices, 3);
vertices[0] = b2Vec2_zero;
vertices[1] = p5 - p4;
vertices[2] = p6 - p4;
poly2.Set(vertices, 3);
}
else
{
b2Vec2 vertices[3];
vertices[0] = p1;
vertices[1] = p3;
vertices[2] = p2;
poly1.Set(vertices, 3);
vertices[0] = b2Vec2_zero;
vertices[1] = p6 - p4;
vertices[2] = p5 - p4;
poly2.Set(vertices, 3);
}
fd1.shape = &poly1;
fd2.shape = &poly2;
b2BodyDef bd1, bd2;
bd1.type = b2_dynamicBody;
bd2.type = b2_dynamicBody;
bd1.position = m_offset;
bd2.position = p4 + m_offset;
bd1.angularDamping = 10.0f;
bd2.angularDamping = 10.0f;
b2Body* body1 = m_world->CreateBody(&bd1);
b2Body* body2 = m_world->CreateBody(&bd2);
body1->CreateFixture(&fd1);
body2->CreateFixture(&fd2);
b2DistanceJointDef djd;
// Using a soft distance constraint can reduce some jitter.
// It also makes the structure seem a bit more fluid by
// acting like a suspension system.
djd.dampingRatio = 0.5f;
djd.frequencyHz = 10.0f;
djd.Initialize(body1, body2, p2 + m_offset, p5 + m_offset);
m_world->CreateJoint(&djd);
djd.Initialize(body1, body2, p3 + m_offset, p4 + m_offset);
m_world->CreateJoint(&djd);
djd.Initialize(body1, m_wheel, p3 + m_offset, wheelAnchor + m_offset);
m_world->CreateJoint(&djd);
djd.Initialize(body2, m_wheel, p6 + m_offset, wheelAnchor + m_offset);
m_world->CreateJoint(&djd);
b2RevoluteJointDef rjd;
rjd.Initialize(body2, m_chassis, p4 + m_offset);
m_world->CreateJoint(&rjd);
}
TheoJansen()
{
m_offset.Set(0.0f, 8.0f);
m_motorSpeed = 2.0f;
m_motorOn = true;
b2Vec2 pivot(0.0f, 0.8f);
// Ground
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-50.0f, 0.0f), b2Vec2(50.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
shape.Set(b2Vec2(-50.0f, 0.0f), b2Vec2(-50.0f, 10.0f));
ground->CreateFixture(&shape, 0.0f);
shape.Set(b2Vec2(50.0f, 0.0f), b2Vec2(50.0f, 10.0f));
ground->CreateFixture(&shape, 0.0f);
}
// Balls
for (int32 i = 0; i < 40; ++i)
{
b2CircleShape shape;
shape.m_radius = 0.25f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-40.0f + 2.0f * i, 0.5f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 1.0f);
}
// Chassis
{
b2PolygonShape shape;
shape.SetAsBox(2.5f, 1.0f);
b2FixtureDef sd;
sd.density = 1.0f;
sd.shape = &shape;
sd.filter.groupIndex = -1;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = pivot + m_offset;
m_chassis = m_world->CreateBody(&bd);
m_chassis->CreateFixture(&sd);
}
{
b2CircleShape shape;
shape.m_radius = 1.6f;
b2FixtureDef sd;
sd.density = 1.0f;
sd.shape = &shape;
sd.filter.groupIndex = -1;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = pivot + m_offset;
m_wheel = m_world->CreateBody(&bd);
m_wheel->CreateFixture(&sd);
}
{
b2RevoluteJointDef jd;
jd.Initialize(m_wheel, m_chassis, pivot + m_offset);
jd.collideConnected = false;
jd.motorSpeed = m_motorSpeed;
jd.maxMotorTorque = 400.0f;
jd.enableMotor = m_motorOn;
m_motorJoint = (b2RevoluteJoint*)m_world->CreateJoint(&jd);
}
b2Vec2 wheelAnchor;
wheelAnchor = pivot + b2Vec2(0.0f, -0.8f);
CreateLeg(-1.0f, wheelAnchor);
CreateLeg(1.0f, wheelAnchor);
m_wheel->SetTransform(m_wheel->GetPosition(), 120.0f * b2_pi / 180.0f);
CreateLeg(-1.0f, wheelAnchor);
CreateLeg(1.0f, wheelAnchor);
m_wheel->SetTransform(m_wheel->GetPosition(), -120.0f * b2_pi / 180.0f);
CreateLeg(-1.0f, wheelAnchor);
CreateLeg(1.0f, wheelAnchor);
}
void Step(Settings* settings)
{
m_debugDraw.DrawString(5, m_textLine, "Keys: left = a, brake = s, right = d, toggle motor = m");
m_textLine += 15;
Test::Step(settings);
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'a':
m_motorJoint->SetMotorSpeed(-m_motorSpeed);
break;
case 's':
m_motorJoint->SetMotorSpeed(0.0f);
break;
case 'd':
m_motorJoint->SetMotorSpeed(m_motorSpeed);
break;
case 'm':
m_motorJoint->EnableMotor(!m_motorJoint->IsMotorEnabled());
break;
}
}
static Test* Create()
{
return new TheoJansen;
}
b2Vec2 m_offset;
b2Body* m_chassis;
b2Body* m_wheel;
b2RevoluteJoint* m_motorJoint;
bool m_motorOn;
float32 m_motorSpeed;
};
#endif // THEO_JANSEN_H
@@ -0,0 +1,156 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef TILES_H
#define TILES_H
/// This stress tests the dynamic tree broad-phase. This also shows that tile
/// based collision is _not_ smooth due to Box2D not knowing about adjacency.
class Tiles : public Test
{
public:
enum
{
e_count = 20
};
Tiles()
{
m_fixtureCount = 0;
b2Timer timer;
{
float32 a = 0.5f;
b2BodyDef bd;
bd.position.y = -a;
b2Body* ground = m_world->CreateBody(&bd);
#if 1
int32 N = 200;
int32 M = 10;
b2Vec2 position;
position.y = 0.0f;
for (int32 j = 0; j < M; ++j)
{
position.x = -N * a;
for (int32 i = 0; i < N; ++i)
{
b2PolygonShape shape;
shape.SetAsBox(a, a, position, 0.0f);
ground->CreateFixture(&shape, 0.0f);
++m_fixtureCount;
position.x += 2.0f * a;
}
position.y -= 2.0f * a;
}
#else
int32 N = 200;
int32 M = 10;
b2Vec2 position;
position.x = -N * a;
for (int32 i = 0; i < N; ++i)
{
position.y = 0.0f;
for (int32 j = 0; j < M; ++j)
{
b2PolygonShape shape;
shape.SetAsBox(a, a, position, 0.0f);
ground->CreateFixture(&shape, 0.0f);
position.y -= 2.0f * a;
}
position.x += 2.0f * a;
}
#endif
}
{
float32 a = 0.5f;
b2PolygonShape shape;
shape.SetAsBox(a, a);
b2Vec2 x(-7.0f, 0.75f);
b2Vec2 y;
b2Vec2 deltaX(0.5625f, 1.25f);
b2Vec2 deltaY(1.125f, 0.0f);
for (int32 i = 0; i < e_count; ++i)
{
y = x;
for (int32 j = i; j < e_count; ++j)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = y;
//if (i == 0 && j == 0)
//{
// bd.allowSleep = false;
//}
//else
//{
// bd.allowSleep = true;
//}
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 5.0f);
++m_fixtureCount;
y += deltaY;
}
x += deltaX;
}
}
m_createTime = timer.GetMilliseconds();
}
void Step(Settings* settings)
{
const b2ContactManager& cm = m_world->GetContactManager();
int32 height = cm.m_broadPhase.GetTreeHeight();
int32 leafCount = cm.m_broadPhase.GetProxyCount();
int32 minimumNodeCount = 2 * leafCount - 1;
float32 minimumHeight = ceilf(logf(float32(minimumNodeCount)) / logf(2.0f));
m_debugDraw.DrawString(5, m_textLine, "dynamic tree height = %d, min = %d", height, int32(minimumHeight));
m_textLine += 15;
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "create time = %6.2f ms, fixture count = %d",
m_createTime, m_fixtureCount);
m_textLine += 15;
//b2DynamicTree* tree = &m_world->m_contactManager.m_broadPhase.m_tree;
//if (m_stepCount == 400)
//{
// tree->RebuildBottomUp();
//}
}
static Test* Create()
{
return new Tiles;
}
int32 m_fixtureCount;
float32 m_createTime;
};
#endif
@@ -0,0 +1,131 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef TIME_OF_IMPACT_H
#define TIME_OF_IMPACT_H
class TimeOfImpact : public Test
{
public:
TimeOfImpact()
{
m_shapeA.SetAsBox(25.0f, 5.0f);
m_shapeB.SetAsBox(2.5f, 2.5f);
}
static Test* Create()
{
return new TimeOfImpact;
}
void Step(Settings* settings)
{
Test::Step(settings);
b2Sweep sweepA;
sweepA.c0.Set(24.0f, -60.0f);
sweepA.a0 = 2.95f;
sweepA.c = sweepA.c0;
sweepA.a = sweepA.a0;
sweepA.localCenter.SetZero();
b2Sweep sweepB;
sweepB.c0.Set(53.474274f, -50.252514f);
sweepB.a0 = 513.36676f; // - 162.0f * b2_pi;
sweepB.c.Set(54.595478f, -51.083473f);
sweepB.a = 513.62781f; // - 162.0f * b2_pi;
sweepB.localCenter.SetZero();
//sweepB.a0 -= 300.0f * b2_pi;
//sweepB.a -= 300.0f * b2_pi;
b2TOIInput input;
input.proxyA.Set(&m_shapeA, 0);
input.proxyB.Set(&m_shapeB, 0);
input.sweepA = sweepA;
input.sweepB = sweepB;
input.tMax = 1.0f;
b2TOIOutput output;
b2TimeOfImpact(&output, &input);
m_debugDraw.DrawString(5, m_textLine, "toi = %g", output.t);
m_textLine += 15;
extern int32 b2_toiMaxIters, b2_toiMaxRootIters;
m_debugDraw.DrawString(5, m_textLine, "max toi iters = %d, max root iters = %d", b2_toiMaxIters, b2_toiMaxRootIters);
m_textLine += 15;
b2Vec2 vertices[b2_maxPolygonVertices];
b2Transform transformA;
sweepA.GetTransform(&transformA, 0.0f);
for (int32 i = 0; i < m_shapeA.m_vertexCount; ++i)
{
vertices[i] = b2Mul(transformA, m_shapeA.m_vertices[i]);
}
m_debugDraw.DrawPolygon(vertices, m_shapeA.m_vertexCount, b2Color(0.9f, 0.9f, 0.9f));
b2Transform transformB;
sweepB.GetTransform(&transformB, 0.0f);
b2Vec2 localPoint(2.0f, -0.1f);
b2Vec2 rB = b2Mul(transformB, localPoint) - sweepB.c0;
float32 wB = sweepB.a - sweepB.a0;
b2Vec2 vB = sweepB.c - sweepB.c0;
b2Vec2 v = vB + b2Cross(wB, rB);
for (int32 i = 0; i < m_shapeB.m_vertexCount; ++i)
{
vertices[i] = b2Mul(transformB, m_shapeB.m_vertices[i]);
}
m_debugDraw.DrawPolygon(vertices, m_shapeB.m_vertexCount, b2Color(0.5f, 0.9f, 0.5f));
sweepB.GetTransform(&transformB, output.t);
for (int32 i = 0; i < m_shapeB.m_vertexCount; ++i)
{
vertices[i] = b2Mul(transformB, m_shapeB.m_vertices[i]);
}
m_debugDraw.DrawPolygon(vertices, m_shapeB.m_vertexCount, b2Color(0.5f, 0.7f, 0.9f));
sweepB.GetTransform(&transformB, 1.0f);
for (int32 i = 0; i < m_shapeB.m_vertexCount; ++i)
{
vertices[i] = b2Mul(transformB, m_shapeB.m_vertices[i]);
}
m_debugDraw.DrawPolygon(vertices, m_shapeB.m_vertexCount, b2Color(0.9f, 0.5f, 0.5f));
#if 0
for (float32 t = 0.0f; t < 1.0f; t += 0.1f)
{
sweepB.GetTransform(&transformB, t);
for (int32 i = 0; i < m_shapeB.m_vertexCount; ++i)
{
vertices[i] = b2Mul(transformB, m_shapeB.m_vertices[i]);
}
m_debugDraw.DrawPolygon(vertices, m_shapeB.m_vertexCount, b2Color(0.9f, 0.5f, 0.5f));
}
#endif
}
b2PolygonShape m_shapeA;
b2PolygonShape m_shapeB;
};
#endif
@@ -0,0 +1,99 @@
/*
* Copyright (c) 2011 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef TUMBLER_H
#define TUMBLER_H
class Tumbler : public Test
{
public:
enum
{
e_count = 800
};
Tumbler()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
}
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.allowSleep = false;
bd.position.Set(0.0f, 10.0f);
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.5f, 10.0f, b2Vec2( 10.0f, 0.0f), 0.0);
body->CreateFixture(&shape, 5.0f);
shape.SetAsBox(0.5f, 10.0f, b2Vec2(-10.0f, 0.0f), 0.0);
body->CreateFixture(&shape, 5.0f);
shape.SetAsBox(10.0f, 0.5f, b2Vec2(0.0f, 10.0f), 0.0);
body->CreateFixture(&shape, 5.0f);
shape.SetAsBox(10.0f, 0.5f, b2Vec2(0.0f, -10.0f), 0.0);
body->CreateFixture(&shape, 5.0f);
b2RevoluteJointDef jd;
jd.bodyA = ground;
jd.bodyB = body;
jd.localAnchorA.Set(0.0f, 10.0f);
jd.localAnchorB.Set(0.0f, 0.0f);
jd.referenceAngle = 0.0f;
jd.motorSpeed = 0.05f * b2_pi;
jd.maxMotorTorque = 1e8f;
jd.enableMotor = true;
m_joint = (b2RevoluteJoint*)m_world->CreateJoint(&jd);
}
m_count = 0;
}
void Step(Settings* settings)
{
Test::Step(settings);
if (m_count < e_count)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 10.0f);
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.125f, 0.125f);
body->CreateFixture(&shape, 1.0f);
++m_count;
}
}
static Test* Create()
{
return new Tumbler;
}
b2RevoluteJoint* m_joint;
int32 m_count;
};
#endif
@@ -0,0 +1,124 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef VARYING_FRICTION_H
#define VARYING_FRICTION_H
class VaryingFriction : public Test
{
public:
VaryingFriction()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(13.0f, 0.25f);
b2BodyDef bd;
bd.position.Set(-4.0f, 22.0f);
bd.angle = -0.25f;
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.25f, 1.0f);
b2BodyDef bd;
bd.position.Set(10.5f, 19.0f);
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(13.0f, 0.25f);
b2BodyDef bd;
bd.position.Set(4.0f, 14.0f);
bd.angle = 0.25f;
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.25f, 1.0f);
b2BodyDef bd;
bd.position.Set(-10.5f, 11.0f);
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(13.0f, 0.25f);
b2BodyDef bd;
bd.position.Set(-4.0f, 6.0f);
bd.angle = -0.25f;
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.5f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 25.0f;
float friction[5] = {0.75f, 0.5f, 0.35f, 0.1f, 0.0f};
for (int i = 0; i < 5; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-15.0f + 4.0f * i, 28.0f);
b2Body* body = m_world->CreateBody(&bd);
fd.friction = friction[i];
body->CreateFixture(&fd);
}
}
}
static Test* Create()
{
return new VaryingFriction;
}
};
#endif
@@ -0,0 +1,69 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef VARYING_RESTITUTION_H
#define VARYING_RESTITUTION_H
// Note: even with a restitution of 1.0, there is some energy change
// due to position correction.
class VaryingRestitution : public Test
{
public:
VaryingRestitution()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2CircleShape shape;
shape.m_radius = 1.0f;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
float32 restitution[7] = {0.0f, 0.1f, 0.3f, 0.5f, 0.75f, 0.9f, 1.0f};
for (int32 i = 0; i < 7; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-10.0f + 3.0f * i, 20.0f);
b2Body* body = m_world->CreateBody(&bd);
fd.restitution = restitution[i];
body->CreateFixture(&fd);
}
}
}
static Test* Create()
{
return new VaryingRestitution;
}
};
#endif
@@ -0,0 +1,165 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef VERTICAL_STACK_H
#define VERTICAL_STACK_H
class VerticalStack : public Test
{
public:
enum
{
e_columnCount = 5,
e_rowCount = 16
//e_columnCount = 1,
//e_rowCount = 1
};
VerticalStack()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
shape.Set(b2Vec2(20.0f, 0.0f), b2Vec2(20.0f, 20.0f));
ground->CreateFixture(&shape, 0.0f);
}
float32 xs[5] = {0.0f, -10.0f, -5.0f, 5.0f, 10.0f};
for (int32 j = 0; j < e_columnCount; ++j)
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.5f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
fd.friction = 0.3f;
for (int i = 0; i < e_rowCount; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
int32 n = j * e_rowCount + i;
b2Assert(n < e_rowCount * e_columnCount);
m_indices[n] = n;
bd.userData = m_indices + n;
float32 x = 0.0f;
//float32 x = RandomFloat(-0.02f, 0.02f);
//float32 x = i % 2 == 0 ? -0.025f : 0.025f;
bd.position.Set(xs[j] + x, 0.752f + 1.54f * i);
b2Body* body = m_world->CreateBody(&bd);
m_bodies[n] = body;
body->CreateFixture(&fd);
}
}
m_bullet = NULL;
}
void Keyboard(unsigned char key)
{
switch (key)
{
case ',':
if (m_bullet != NULL)
{
m_world->DestroyBody(m_bullet);
m_bullet = NULL;
}
{
b2CircleShape shape;
shape.m_radius = 0.25f;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
fd.restitution = 0.05f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.bullet = true;
bd.position.Set(-31.0f, 5.0f);
m_bullet = m_world->CreateBody(&bd);
m_bullet->CreateFixture(&fd);
m_bullet->SetLinearVelocity(b2Vec2(400.0f, 0.0f));
}
break;
}
}
void Step(Settings* settings)
{
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "Press: (,) to launch a bullet.");
m_textLine += 15;
//if (m_stepCount == 300)
//{
// if (m_bullet != NULL)
// {
// m_world->DestroyBody(m_bullet);
// m_bullet = NULL;
// }
// {
// b2CircleShape shape;
// shape.m_radius = 0.25f;
// b2FixtureDef fd;
// fd.shape = &shape;
// fd.density = 20.0f;
// fd.restitution = 0.05f;
// b2BodyDef bd;
// bd.type = b2_dynamicBody;
// bd.bullet = true;
// bd.position.Set(-31.0f, 5.0f);
// m_bullet = m_world->CreateBody(&bd);
// m_bullet->CreateFixture(&fd);
// m_bullet->SetLinearVelocity(b2Vec2(400.0f, 0.0f));
// }
//}
}
static Test* Create()
{
return new VerticalStack;
}
b2Body* m_bullet;
b2Body* m_bodies[e_rowCount * e_columnCount];
int32 m_indices[e_rowCount * e_columnCount];
};
#endif
@@ -0,0 +1,209 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef WEB_H
#define WEB_H
// This tests distance joints, body destruction, and joint destruction.
class Web : public Test
{
public:
Web()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.5f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-5.0f, 5.0f);
m_bodies[0] = m_world->CreateBody(&bd);
m_bodies[0]->CreateFixture(&shape, 5.0f);
bd.position.Set(5.0f, 5.0f);
m_bodies[1] = m_world->CreateBody(&bd);
m_bodies[1]->CreateFixture(&shape, 5.0f);
bd.position.Set(5.0f, 15.0f);
m_bodies[2] = m_world->CreateBody(&bd);
m_bodies[2]->CreateFixture(&shape, 5.0f);
bd.position.Set(-5.0f, 15.0f);
m_bodies[3] = m_world->CreateBody(&bd);
m_bodies[3]->CreateFixture(&shape, 5.0f);
b2DistanceJointDef jd;
b2Vec2 p1, p2, d;
jd.frequencyHz = 2.0f;
jd.dampingRatio = 0.0f;
jd.bodyA = ground;
jd.bodyB = m_bodies[0];
jd.localAnchorA.Set(-10.0f, 0.0f);
jd.localAnchorB.Set(-0.5f, -0.5f);
p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
d = p2 - p1;
jd.length = d.Length();
m_joints[0] = m_world->CreateJoint(&jd);
jd.bodyA = ground;
jd.bodyB = m_bodies[1];
jd.localAnchorA.Set(10.0f, 0.0f);
jd.localAnchorB.Set(0.5f, -0.5f);
p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
d = p2 - p1;
jd.length = d.Length();
m_joints[1] = m_world->CreateJoint(&jd);
jd.bodyA = ground;
jd.bodyB = m_bodies[2];
jd.localAnchorA.Set(10.0f, 20.0f);
jd.localAnchorB.Set(0.5f, 0.5f);
p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
d = p2 - p1;
jd.length = d.Length();
m_joints[2] = m_world->CreateJoint(&jd);
jd.bodyA = ground;
jd.bodyB = m_bodies[3];
jd.localAnchorA.Set(-10.0f, 20.0f);
jd.localAnchorB.Set(-0.5f, 0.5f);
p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
d = p2 - p1;
jd.length = d.Length();
m_joints[3] = m_world->CreateJoint(&jd);
jd.bodyA = m_bodies[0];
jd.bodyB = m_bodies[1];
jd.localAnchorA.Set(0.5f, 0.0f);
jd.localAnchorB.Set(-0.5f, 0.0f);;
p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
d = p2 - p1;
jd.length = d.Length();
m_joints[4] = m_world->CreateJoint(&jd);
jd.bodyA = m_bodies[1];
jd.bodyB = m_bodies[2];
jd.localAnchorA.Set(0.0f, 0.5f);
jd.localAnchorB.Set(0.0f, -0.5f);
p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
d = p2 - p1;
jd.length = d.Length();
m_joints[5] = m_world->CreateJoint(&jd);
jd.bodyA = m_bodies[2];
jd.bodyB = m_bodies[3];
jd.localAnchorA.Set(-0.5f, 0.0f);
jd.localAnchorB.Set(0.5f, 0.0f);
p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
d = p2 - p1;
jd.length = d.Length();
m_joints[6] = m_world->CreateJoint(&jd);
jd.bodyA = m_bodies[3];
jd.bodyB = m_bodies[0];
jd.localAnchorA.Set(0.0f, -0.5f);
jd.localAnchorB.Set(0.0f, 0.5f);
p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
d = p2 - p1;
jd.length = d.Length();
m_joints[7] = m_world->CreateJoint(&jd);
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'b':
for (int32 i = 0; i < 4; ++i)
{
if (m_bodies[i])
{
m_world->DestroyBody(m_bodies[i]);
m_bodies[i] = NULL;
break;
}
}
break;
case 'j':
for (int32 i = 0; i < 8; ++i)
{
if (m_joints[i])
{
m_world->DestroyJoint(m_joints[i]);
m_joints[i] = NULL;
break;
}
}
break;
}
}
void Step(Settings* settings)
{
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "This demonstrates a soft distance joint.");
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "Press: (b) to delete a body, (j) to delete a joint");
m_textLine += 15;
}
void JointDestroyed(b2Joint* joint)
{
for (int32 i = 0; i < 8; ++i)
{
if (m_joints[i] == joint)
{
m_joints[i] = NULL;
break;
}
}
}
static Test* Create()
{
return new Web;
}
b2Body* m_bodies[4];
b2Joint* m_joints[8];
};
#endif
@@ -0,0 +1,209 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-12 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../JuceDemoHeader.h"
#if JUCE_USE_CAMERA
//==============================================================================
class CameraDemo : public Component,
private ComboBoxListener,
private ButtonListener,
private CameraDevice::Listener,
private AsyncUpdater
{
public:
CameraDemo()
: cameraSelectorComboBox ("Camera"),
snapshotButton ("Take a snapshot"),
recordMovieButton ("Record a movie (to your desktop)..."),
recordingMovie (false)
{
setOpaque (true);
addAndMakeVisible (cameraSelectorComboBox);
updateCameraList();
cameraSelectorComboBox.setSelectedId (1);
cameraSelectorComboBox.addListener (this);
addAndMakeVisible (snapshotButton);
snapshotButton.addListener (this);
snapshotButton.setEnabled (false);
addAndMakeVisible (recordMovieButton);
recordMovieButton.addListener (this);
recordMovieButton.setEnabled (false);
addAndMakeVisible (lastSnapshot);
cameraSelectorComboBox.setSelectedId (2);
}
//==============================================================================
void paint (Graphics& g) override
{
g.fillAll (Colours::black);
}
void resized() override
{
Rectangle<int> r (getLocalBounds().reduced (5));
Rectangle<int> top (r.removeFromTop (25));
cameraSelectorComboBox.setBounds (top.removeFromLeft (250));
r.removeFromTop (4);
top = r.removeFromTop (25);
snapshotButton.changeWidthToFitText (24);
snapshotButton.setBounds (top.removeFromLeft (snapshotButton.getWidth()));
top.removeFromLeft (4);
recordMovieButton.changeWidthToFitText (24);
recordMovieButton.setBounds (top.removeFromLeft (recordMovieButton.getWidth()));
r.removeFromTop (4);
Rectangle<int> previewArea (r.removeFromTop (r.getHeight() / 2));
if (cameraPreviewComp != nullptr)
cameraPreviewComp->setBounds (previewArea);
r.removeFromTop (4);
lastSnapshot.setBounds (r);
}
private:
//==============================================================================
ScopedPointer<CameraDevice> cameraDevice;
ScopedPointer<Component> cameraPreviewComp;
ImageComponent lastSnapshot;
ComboBox cameraSelectorComboBox;
TextButton snapshotButton;
TextButton recordMovieButton;
bool recordingMovie;
void updateCameraList()
{
cameraSelectorComboBox.clear();
cameraSelectorComboBox.addItem ("No camera", 1);
cameraSelectorComboBox.addSeparator();
StringArray cameras = CameraDevice::getAvailableDevices();
for (int i = 0; i < cameras.size(); ++i)
cameraSelectorComboBox.addItem (cameras[i], i + 2);
}
void comboBoxChanged (ComboBox*) override
{
// This is called when the user chooses a camera from the drop-down list.
cameraDevice = nullptr;
cameraPreviewComp = nullptr;
recordingMovie = false;
if (cameraSelectorComboBox.getSelectedId() > 1)
{
// Try to open the user's choice of camera..
cameraDevice = CameraDevice::openDevice (cameraSelectorComboBox.getSelectedId() - 2);
// and if it worked, create a preview component for it..
if (cameraDevice != nullptr)
addAndMakeVisible (cameraPreviewComp = cameraDevice->createViewerComponent());
}
snapshotButton.setEnabled (cameraDevice != nullptr);
recordMovieButton.setEnabled (cameraDevice != nullptr);
resized();
}
void buttonClicked (Button* b) override
{
if (cameraDevice != nullptr)
{
if (b == &recordMovieButton)
{
// The user has clicked the record movie button..
if (! recordingMovie)
{
// Start recording to a file on the user's desktop..
recordingMovie = true;
File file (File::getSpecialLocation (File::userDesktopDirectory)
.getNonexistentChildFile ("JuceCameraDemo",
CameraDevice::getFileExtension()));
cameraDevice->startRecordingToFile (file);
recordMovieButton.setButtonText ("Stop Recording");
}
else
{
// Already recording, so stop...
recordingMovie = false;
cameraDevice->stopRecording();
recordMovieButton.setButtonText ("Start recording (to a file on your desktop)");
}
}
else
{
// When the user clicks the snapshot button, we'll attach ourselves to
// the camera as a listener, and wait for an image to arrive...
cameraDevice->addListener (this);
}
}
}
// This is called by the camera device when a new image arrives
void imageReceived (const Image& image) override
{
// In this app we just want to take one image, so as soon as this happens,
// we'll unregister ourselves as a listener.
if (cameraDevice != nullptr)
cameraDevice->removeListener (this);
// This callback won't be on the message thread, so to get the image back to
// the message thread, we'll stash a pointer to it (which is reference-counted in
// a thead-safe way), and trigger an async callback which will then display the
// new image..
incomingImage = image;
triggerAsyncUpdate();
}
Image incomingImage;
void handleAsyncUpdate() override
{
if (incomingImage.isValid())
lastSnapshot.setImage (incomingImage);
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CameraDemo);
};
// This static object will register this demo type in a global list of demos..
static JuceDemoType<CameraDemo> demo ("29 Graphics: Camera Capture");
#endif // JUCE_USE_CAMERA
@@ -0,0 +1,281 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-12 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../JuceDemoHeader.h"
#if JUCE_WINDOWS || JUCE_MAC || JUCE_LINUX
//==============================================================================
// This is a token that's used at both ends of our parent-child processes, to
// act as a unique token in the command line arguments.
static const char* demoCommandLineUID = "demoUID";
// A few quick utility functions to convert between raw data and ValueTrees
static ValueTree memoryBlockToValueTree (const MemoryBlock& mb)
{
return ValueTree::readFromData (mb.getData(), mb.getSize());
}
static MemoryBlock valueTreeToMemoryBlock (const ValueTree& v)
{
MemoryOutputStream mo;
v.writeToStream (mo);
return mo.getMemoryBlock();
}
static String valueTreeToString (const ValueTree& v)
{
const ScopedPointer<XmlElement> xml (v.createXml());
return xml != nullptr ? xml->createDocument ("", true, false) : String();
}
//==============================================================================
class ChildProcessDemo : public Component,
private Button::Listener,
private MessageListener
{
public:
ChildProcessDemo()
{
setOpaque (true);
addAndMakeVisible (launchButton);
launchButton.setButtonText ("Launch Child Process");
launchButton.addListener (this);
addAndMakeVisible (pingButton);
pingButton.setButtonText ("Send Ping");
pingButton.addListener (this);
addAndMakeVisible (killButton);
killButton.setButtonText ("Kill Child Process");
killButton.addListener (this);
addAndMakeVisible (testResultsBox);
testResultsBox.setMultiLine (true);
testResultsBox.setFont (Font (Font::getDefaultMonospacedFontName(), 12.0f, Font::plain));
logMessage (String ("This demo uses the ChildProcessMaster and ChildProcessSlave classes to launch and communicate "
"with a child process, sending messages in the form of serialised ValueTree objects.") + newLine);
}
~ChildProcessDemo()
{
masterProcess = nullptr;
}
void paint (Graphics& g) override
{
fillTiledBackground (g);
}
void resized() override
{
Rectangle<int> area (getLocalBounds());
Rectangle<int> top (area.removeFromTop (40));
launchButton.setBounds (top.removeFromLeft (180).reduced (8));
pingButton.setBounds (top.removeFromLeft (180).reduced (8));
killButton.setBounds (top.removeFromLeft (180).reduced (8));
testResultsBox.setBounds (area.reduced (8));
}
// Appends a message to the textbox that's shown in the demo as the console
void logMessage (const String& message)
{
postMessage (new LogMessage (message));
}
// invoked by the 'launch' button.
void launchChildProcess()
{
if (masterProcess == nullptr)
{
masterProcess = new DemoMasterProcess (*this);
if (masterProcess->launchSlaveProcess (File::getSpecialLocation (File::currentExecutableFile), demoCommandLineUID))
logMessage ("Child process started");
}
}
// invoked by the 'ping' button.
void pingChildProcess()
{
if (masterProcess != nullptr)
masterProcess->sendPingMessageToSlave();
else
logMessage ("Child process is not running!");
}
// invoked by the 'kill' button.
void killChildProcess()
{
if (masterProcess != nullptr)
{
masterProcess = nullptr;
logMessage ("Child process killed");
}
}
//==============================================================================
// This class is used by the main process, acting as the master and receiving messages
// from the slave process.
class DemoMasterProcess : public ChildProcessMaster,
private DeletedAtShutdown
{
public:
DemoMasterProcess (ChildProcessDemo& d) : demo (d), count (0) {}
// This gets called when a message arrives from the slave process..
void handleMessageFromSlave (const MemoryBlock& mb) override
{
ValueTree incomingMessage (memoryBlockToValueTree (mb));
demo.logMessage ("Received: " + valueTreeToString (incomingMessage));
}
// This gets called if the slave process dies.
void handleConnectionLost() override
{
demo.logMessage ("Connection lost to child process!");
demo.killChildProcess();
}
void sendPingMessageToSlave()
{
ValueTree message ("MESSAGE");
message.setProperty ("count", count++, nullptr);
demo.logMessage ("Sending: " + valueTreeToString (message));
sendMessageToSlave (valueTreeToMemoryBlock (message));
}
ChildProcessDemo& demo;
int count;
};
//==============================================================================
ScopedPointer<DemoMasterProcess> masterProcess;
private:
TextButton launchButton, pingButton, killButton;
TextEditor testResultsBox;
void buttonClicked (Button* button) override
{
if (button == &launchButton) launchChildProcess();
if (button == &pingButton) pingChildProcess();
if (button == &killButton) killChildProcess();
}
struct LogMessage : public Message
{
LogMessage (const String& m) : message (m) {}
String message;
};
void handleMessage (const Message& message) override
{
testResultsBox.moveCaretToEnd();
testResultsBox.insertTextAtCaret (static_cast<const LogMessage&> (message).message + newLine);
testResultsBox.moveCaretToEnd();
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessDemo)
};
//==============================================================================
/* This class gets instantiated in the child process, and receives messages from
the master process.
*/
class DemoSlaveProcess : public ChildProcessSlave,
private DeletedAtShutdown
{
public:
DemoSlaveProcess() {}
void handleMessageFromMaster (const MemoryBlock& mb) override
{
ValueTree incomingMessage (memoryBlockToValueTree (mb));
/* In the demo we're only expecting one type of message, which will contain a 'count' parameter -
we'll just increment that number and send back a new message containing the new number.
Obviously in a real app you'll probably want to look at the type of the message, and do
some more interesting behaviour.
*/
ValueTree reply ("REPLY");
reply.setProperty ("countPlusOne", static_cast<int> (incomingMessage["count"]) + 1, nullptr);
sendMessageToMaster (valueTreeToMemoryBlock (reply));
}
void handleConnectionMade() override
{
// This method is called when the connection is established, and in response, we'll just
// send off a message to say hello.
ValueTree reply ("HelloWorld");
sendMessageToMaster (valueTreeToMemoryBlock (reply));
}
/* If no pings are received from the master process for a number of seconds, then this will get invoked.
Typically you'll want to use this as a signal to kill the process as quickly as possible, as you
don't want to leave it hanging around as a zombie..
*/
void handleConnectionLost() override
{
JUCEApplication::quit();
}
};
//==============================================================================
/* The JuceDemoApplication::initialise method calls this function to allow the
child process to launch when the command line parameters indicate that we're
being asked to run as a child process..
*/
bool invokeChildProcessDemo (const String& commandLine)
{
ScopedPointer<DemoSlaveProcess> slave (new DemoSlaveProcess());
if (slave->initialiseFromCommandLine (commandLine, demoCommandLineUID))
{
slave.release(); // allow the slave object to stay alive - it'll handle its own deletion.
return true;
}
return false;
}
// This static object will register this demo type in a global list of demos..
static JuceDemoType<ChildProcessDemo> childProcessDemo ("40 Child Process Comms");
#else
// (Dummy stub for platforms that don't support this demo)
bool invokeChildProcessDemo (const String&) { return false; }
#endif
@@ -0,0 +1,98 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-12 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../JuceDemoHeader.h"
//==============================================================================
class CodeEditorDemo : public Component,
private FilenameComponentListener
{
public:
CodeEditorDemo()
: fileChooser ("File", File::nonexistent, true, false, false,
"*.cpp;*.h;*.hpp;*.c;*.mm;*.m", String::empty,
"Choose a C++ file to open it in the editor")
{
setOpaque (true);
// Create the editor..
addAndMakeVisible (editor = new CodeEditorComponent (codeDocument, &cppTokeniser));
editor->loadContent ("\n"
"/* Code editor demo!\n"
"\n"
" To see a real-world example of the code editor\n"
" in action, try the Introjucer!\n"
"\n"
"*/\n"
"\n");
// Create a file chooser control to load files into it..
addAndMakeVisible (fileChooser);
fileChooser.addListener (this);
}
~CodeEditorDemo()
{
fileChooser.removeListener (this);
}
void paint (Graphics& g) override
{
g.fillAll (Colours::lightgrey);
}
void resized() override
{
Rectangle<int> r (getLocalBounds().reduced (8));
fileChooser.setBounds (r.removeFromTop (25));
editor->setBounds (r.withTrimmedTop (8));
}
private:
// this is the document that the editor component is showing
CodeDocument codeDocument;
// this is a tokeniser to apply the C++ syntax highlighting
CPlusPlusCodeTokeniser cppTokeniser;
// the editor component
ScopedPointer<CodeEditorComponent> editor;
FilenameComponent fileChooser;
void filenameComponentChanged (FilenameComponent*) override
{
editor->loadContent (fileChooser.getCurrentFile().loadFileAsString());
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CodeEditorDemo);
};
// This static object will register this demo type in a global list of demos..
static JuceDemoType<CodeEditorDemo> demo ("10 Components: Code Editor");
@@ -0,0 +1,156 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-12 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../JuceDemoHeader.h"
//==============================================================================
class ComponentTransformsDemo : public Component
{
public:
ComponentTransformsDemo()
{
addAndMakeVisible (content = createContentComp());
content->setSize (800, 600);
for (int i = 0; i < 3; ++i)
{
CornerDragger* d = new CornerDragger();
draggers.add (d);
addAndMakeVisible (d);
}
draggers.getUnchecked(0)->relativePos = Point<float> (0.10f, 0.15f);
draggers.getUnchecked(1)->relativePos = Point<float> (0.95f, 0.05f);
draggers.getUnchecked(2)->relativePos = Point<float> (0.05f, 0.85f);
}
void paint (Graphics& g)
{
fillTiledBackground (g);
g.setColour (Colours::white);
g.setFont (15.0f);
g.drawFittedText ("Drag the corner-points around to show how complex components can have affine-transforms applied...",
getLocalBounds().removeFromBottom (40).reduced (10, 0), Justification::centred, 3);
}
void resized() override
{
for (int i = 0; i < 3; ++i)
{
CornerDragger* d = draggers.getUnchecked(i);
d->setCentrePosition (proportionOfWidth (d->relativePos.x),
proportionOfHeight (d->relativePos.y));
}
}
void childBoundsChanged (Component* child) override
{
if (dynamic_cast<CornerDragger*> (child) != nullptr)
updateTransform();
}
private:
ScopedPointer<Component> content;
static Component* createContentComp()
{
Array<JuceDemoTypeBase*>& demos (JuceDemoTypeBase::getDemoTypeList());
for (int i = 0; i < demos.size(); ++i)
if (demos.getUnchecked(i)->name.containsIgnoreCase ("Widgets"))
return demos.getUnchecked (i)->createComponent();
jassertfalse;
return nullptr;
}
struct CornerDragger : public Component
{
CornerDragger()
{
setSize (30, 30);
setRepaintsOnMouseActivity (true);
}
void paint (Graphics& g)
{
g.setColour (Colours::white.withAlpha (isMouseOverOrDragging() ? 0.9f : 0.5f));
g.fillEllipse (getLocalBounds().reduced (3).toFloat());
g.setColour (Colours::darkgreen);
g.drawEllipse (getLocalBounds().reduced (3).toFloat(), 2.0f);
}
void resized() override
{
constrainer.setMinimumOnscreenAmounts (getHeight(), getWidth(), getHeight(), getWidth());
}
void moved() override
{
if (isMouseButtonDown())
relativePos = getBounds().getCentre().toFloat() / Point<int> (getParentWidth(), getParentHeight()).toFloat();
}
void mouseDown (const MouseEvent& e) override { dragger.startDraggingComponent (this, e); }
void mouseDrag (const MouseEvent& e) override { dragger.dragComponent (this, e, &constrainer); }
Point<float> relativePos;
private:
ComponentBoundsConstrainer constrainer;
ComponentDragger dragger;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CornerDragger);
};
OwnedArray<CornerDragger> draggers;
Point<float> getDraggerPos (int index) const
{
return draggers.getUnchecked(index)->getBounds().getCentre().toFloat();
}
void updateTransform()
{
const Point<float> p0 (getDraggerPos(0));
const Point<float> p1 (getDraggerPos(1));
const Point<float> p2 (getDraggerPos(2));
if (p0 != p1 && p1 != p2 && p0 != p2)
content->setTransform (AffineTransform::fromTargetPoints (0, 0, p0.x, p0.y,
(float) content->getWidth(), 0, p1.x, p1.y,
0, (float) content->getHeight(), p2.x, p2.y));
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentTransformsDemo);
};
// This static object will register this demo type in a global list of demos..
static JuceDemoType<ComponentTransformsDemo> demo ("10 Components: Transforms");
@@ -0,0 +1,248 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-12 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../JuceDemoHeader.h"
class RSAComponent : public Component,
private Button::Listener
{
public:
RSAComponent()
{
addAndMakeVisible (rsaGroup);
rsaGroup.setText ("RSA Encryption");
rsaGroup.setColour (GroupComponent::outlineColourId, Colours::darkgrey);
rsaGroup.setColour (GroupComponent::textColourId, Colours::black);
bitSizeLabel.setText ("Num Bits to Use:", dontSendNotification);
bitSizeLabel.attachToComponent (&bitSize, true);
addAndMakeVisible (bitSize);
bitSize.setText (String (256));
addAndMakeVisible (generateRSAButton);
generateRSAButton.setButtonText ("Generate RSA");
generateRSAButton.addListener (this);
addAndMakeVisible (rsaResultBox);
rsaResultBox.setColour (TextEditor::backgroundColourId, Colours::white.withAlpha (0.5f));
rsaResultBox.setReadOnly (true);
rsaResultBox.setMultiLine (true);
}
void resized() override
{
Rectangle<int> area (getLocalBounds());
rsaGroup.setBounds (area);
area.removeFromTop (10);
area.reduce (5, 5);
Rectangle<int> topArea (area.removeFromTop (34));
topArea.removeFromLeft (110);
bitSize.setBounds (topArea.removeFromLeft (topArea.getWidth() / 2).reduced (5));
generateRSAButton.setBounds (topArea.reduced (5));
rsaResultBox.setBounds (area.reduced (5));
}
private:
void createRSAKey()
{
int bits = jlimit (32, 512, bitSize.getText().getIntValue());
bitSize.setText (String (bits), dontSendNotification);
// Create a key-pair...
RSAKey publicKey, privateKey;
RSAKey::createKeyPair (publicKey, privateKey, bits);
// Test the new key on a piece of data...
BigInteger testValue;
testValue.parseString ("1234567890abcdef", 16);
BigInteger encodedValue (testValue);
publicKey.applyToValue (encodedValue);
BigInteger decodedValue (encodedValue);
privateKey.applyToValue (decodedValue);
// ..and show the results..
String message;
message << "Number of bits: " << bits << newLine
<< "Public Key: " << publicKey.toString() << newLine
<< "Private Key: " << privateKey.toString() << newLine
<< newLine
<< "Test input: " << testValue.toString (16) << newLine
<< "Encoded: " << encodedValue.toString (16) << newLine
<< "Decoded: " << decodedValue.toString (16) << newLine;
rsaResultBox.setText (message, false);
}
GroupComponent rsaGroup;
TextButton generateRSAButton;
Label bitSizeLabel;
TextEditor bitSize, rsaResultBox;
void buttonClicked (Button* buttonThatWasClicked) override
{
if (buttonThatWasClicked == &generateRSAButton)
createRSAKey();
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RSAComponent)
};
//==============================================================================
class HashesComponent : public Component,
private TextEditor::Listener
{
public:
HashesComponent()
{
addAndMakeVisible (hashGroup);
hashGroup.setText ("Hashes");
hashGroup.setColour (GroupComponent::outlineColourId, Colours::darkgrey);
hashGroup.setColour (GroupComponent::textColourId, Colours::black);
addAndMakeVisible (hashEntryBox);
hashEntryBox.setMultiLine (true);
hashEntryBox.setColour (TextEditor::backgroundColourId, Colours::white.withAlpha (0.5f));
hashEntryBox.setReturnKeyStartsNewLine (true);
hashEntryBox.setText ("Type some text in this box and the resulting MD5 and SHA hashes will update below");
hashEntryBox.addListener (this);
hashLabel1.setText ("Text to Hash:", dontSendNotification);
hashLabel2.setText ("MD5 Result:", dontSendNotification);
hashLabel3.setText ("SHA Result:", dontSendNotification);
hashLabel1.attachToComponent (&hashEntryBox, true);
hashLabel2.attachToComponent (&md5Result, true);
hashLabel3.attachToComponent (&shaResult, true);
addAndMakeVisible (md5Result);
addAndMakeVisible (shaResult);
updateHashes();
}
void updateHashes()
{
updateMD5Result();
updateSHA256Result();
}
void updateMD5Result()
{
const MD5 md5 (hashEntryBox.getText().toUTF8());
md5Result.setText (md5.toHexString(), dontSendNotification);
}
void updateSHA256Result()
{
const SHA256 sha (hashEntryBox.getText().toUTF8());
shaResult.setText (sha.toHexString(), dontSendNotification);
}
void resized() override
{
Rectangle<int> area (getLocalBounds());
hashGroup.setBounds (area);
area.removeFromLeft (80);
area.removeFromTop (10);
area.reduce (5, 5);
shaResult.setBounds (area.removeFromBottom (34).reduced (5));
md5Result.setBounds (area.removeFromBottom (34).reduced (5));
hashEntryBox.setBounds (area.reduced (5));
}
private:
GroupComponent hashGroup;
TextEditor hashEntryBox;
Label md5Result, shaResult;
Label hashLabel1, hashLabel2, hashLabel3;
void textEditorTextChanged (TextEditor& editor) override
{
if (&editor == &hashEntryBox)
updateHashes();
}
void textEditorReturnKeyPressed (TextEditor& editor) override
{
if (&editor == &hashEntryBox)
updateHashes();
}
void textEditorEscapeKeyPressed (TextEditor& editor) override
{
if (&editor == &hashEntryBox)
updateHashes();
}
void textEditorFocusLost (TextEditor& editor) override
{
if (&editor == &hashEntryBox)
updateHashes();
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HashesComponent)
};
//==============================================================================
class CryptographyDemo : public Component
{
public:
CryptographyDemo()
{
addAndMakeVisible (rsaDemo);
addAndMakeVisible (hashDemo);
}
void paint (Graphics& g) override
{
g.fillAll (Colour::greyLevel (0.4f));
}
void resized() override
{
Rectangle<int> area (getLocalBounds());
rsaDemo.setBounds (area.removeFromTop (getHeight() / 2).reduced (5));
hashDemo.setBounds (area.reduced (5));
}
private:
RSAComponent rsaDemo;
HashesComponent hashDemo;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CryptographyDemo)
};
// This static object will register this demo type in a global list of demos..
static JuceDemoType<CryptographyDemo> demo ("40 Cryptography");
@@ -0,0 +1,366 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-12 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../JuceDemoHeader.h"
//==============================================================================
class DemoBackgroundThread : public ThreadWithProgressWindow
{
public:
DemoBackgroundThread()
: ThreadWithProgressWindow ("busy doing some important things...", true, true)
{
setStatusMessage ("Getting ready...");
}
void run() override
{
setProgress (-1.0); // setting a value beyond the range 0 -> 1 will show a spinning bar..
setStatusMessage ("Preparing to do some stuff...");
wait (2000);
const int thingsToDo = 10;
for (int i = 0; i < thingsToDo; ++i)
{
// must check this as often as possible, because this is
// how we know if the user's pressed 'cancel'
if (threadShouldExit())
return;
// this will update the progress bar on the dialog box
setProgress (i / (double) thingsToDo);
setStatusMessage (String (thingsToDo - i) + " things left to do...");
wait (500);
}
setProgress (-1.0); // setting a value beyond the range 0 -> 1 will show a spinning bar..
setStatusMessage ("Finishing off the last few bits and pieces!");
wait (2000);
}
// This method gets called on the message thread once our thread has finished..
void threadComplete (bool userPressedCancel) override
{
if (userPressedCancel)
{
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
"Progress window",
"You pressed cancel!");
}
else
{
// thread finished normally..
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
"Progress window",
"Thread finished ok!");
}
// ..and clean up by deleting our thread object..
delete this;
}
};
//==============================================================================
class DialogsDemo : public Component,
private Button::Listener
{
public:
enum DialogType
{
plainAlertWindow,
warningAlertWindow,
infoAlertWindow,
questionAlertWindow,
okCancelAlertWindow,
extraComponentsAlertWindow,
calloutBoxWindow,
progressWindow,
loadChooser,
loadWithPreviewChooser,
directoryChooser,
saveChooser,
numDialogs
};
DialogsDemo()
{
setOpaque (true);
addAndMakeVisible (nativeButton);
nativeButton.setButtonText ("Use Native Windows");
nativeButton.addListener (this);
static const char* windowNames[] =
{
"Plain Alert Window",
"Alert Window With Warning Icon",
"Alert Window With Info Icon",
"Alert Window With Question Icon",
"OK Cancel Alert Window",
"Alert Window With Extra Components",
"CalloutBox",
"Thread With Progress Window",
"'Load' File Browser",
"'Load' File Browser With Image Preview",
"'Choose Directory' File Browser",
"'Save' File Browser"
};
// warn in case we add any windows
jassert (numElementsInArray (windowNames) == numDialogs);
for (int i = 0; i < numDialogs; ++i)
{
TextButton* newButton = new TextButton();
windowButtons.add (newButton);
addAndMakeVisible (newButton);
newButton->setButtonText (windowNames[i]);
newButton->addListener (this);
}
}
~DialogsDemo()
{
nativeButton.removeListener (this);
for (int i = windowButtons.size(); --i >= 0;)
if (TextButton* button = windowButtons.getUnchecked (i))
button->removeListener (this);
}
//==============================================================================
void paint (Graphics& g)
{
fillTiledBackground (g);
}
void resized() override
{
Rectangle<int> area (getLocalBounds().reduced (5, 15));
Rectangle<int> topRow;
for (int i = 0; i < windowButtons.size(); ++i)
{
if (topRow.getWidth() < 10 || i == loadChooser)
topRow = area.removeFromTop (26);
if (i == progressWindow)
area.removeFromTop (20);
windowButtons.getUnchecked (i)
->setBounds (topRow.removeFromLeft (area.getWidth() / 2).reduced (4, 2));
}
area.removeFromTop (15);
nativeButton.setBounds (area.removeFromTop (24));
}
private:
OwnedArray<TextButton> windowButtons;
ToggleButton nativeButton;
static void alertBoxResultChosen (int result, DialogsDemo*)
{
AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon,
"Alert Box",
"Result code: " + String (result));
}
void showWindow (Component& button, DialogType type)
{
if (type >= plainAlertWindow && type <= questionAlertWindow)
{
AlertWindow::AlertIconType icon = AlertWindow::NoIcon;
switch (type)
{
case warningAlertWindow: icon = AlertWindow::WarningIcon; break;
case infoAlertWindow: icon = AlertWindow::InfoIcon; break;
case questionAlertWindow: icon = AlertWindow::QuestionIcon; break;
default: break;
}
AlertWindow::showMessageBoxAsync (icon,
"This is an AlertWindow",
"And this is the AlertWindow's message. Blah blah blah blah blah blah blah blah blah blah blah blah blah.",
"OK");
}
else if (type == okCancelAlertWindow)
{
AlertWindow::showOkCancelBox (AlertWindow::QuestionIcon,
"This is an ok/cancel AlertWindow",
"And this is the AlertWindow's message. Blah blah blah blah blah blah blah blah blah blah blah blah blah.",
String::empty,
String::empty,
0,
ModalCallbackFunction::forComponent (alertBoxResultChosen, this));
}
else if (type == calloutBoxWindow)
{
ColourSelector* colourSelector = new ColourSelector();
colourSelector->setName ("background");
colourSelector->setCurrentColour (findColour (TextButton::buttonColourId));
colourSelector->setColour (ColourSelector::backgroundColourId, Colours::transparentBlack);
colourSelector->setSize (300, 400);
CallOutBox::launchAsynchronously (colourSelector, button.getScreenBounds(), nullptr);
}
else if (type == extraComponentsAlertWindow)
{
#if JUCE_MODAL_LOOPS_PERMITTED
AlertWindow w ("AlertWindow demo..",
"This AlertWindow has a couple of extra components added to show how to add drop-down lists and text entry boxes.",
AlertWindow::QuestionIcon);
w.addTextEditor ("text", "enter some text here", "text field:");
const char* options[] = { "option 1", "option 2", "option 3", "option 4", nullptr };
w.addComboBox ("option", StringArray (options), "some options");
w.addButton ("OK", 1, KeyPress (KeyPress::returnKey, 0, 0));
w.addButton ("Cancel", 0, KeyPress (KeyPress::escapeKey, 0, 0));
if (w.runModalLoop() != 0) // is they picked 'ok'
{
// this is the item they chose in the drop-down list..
const int optionIndexChosen = w.getComboBoxComponent ("option")->getSelectedItemIndex();
(void) optionIndexChosen; // (just avoids a compiler warning about unused variables)
// this is the text they entered..
String text = w.getTextEditorContents ("text");
}
#endif
}
else if (type == progressWindow)
{
// This will launch our ThreadWithProgressWindow in a modal state. (Our subclass
// will take care of deleting the object when the task has finished)
(new DemoBackgroundThread())->launchThread();
}
else if (type >= loadChooser && type <= saveChooser)
{
#if JUCE_MODAL_LOOPS_PERMITTED
const bool useNativeVersion = nativeButton.getToggleState();
if (type == loadChooser)
{
FileChooser fc ("Choose a file to open...",
File::getCurrentWorkingDirectory(),
"*",
useNativeVersion);
if (fc.browseForMultipleFilesToOpen())
{
String chosen;
for (int i = 0; i < fc.getResults().size(); ++i)
chosen << fc.getResults().getReference(i).getFullPathName() << "\n";
AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon,
"File Chooser...",
"You picked: " + chosen);
}
}
else if (type == loadWithPreviewChooser)
{
ImagePreviewComponent imagePreview;
imagePreview.setSize (200, 200);
FileChooser fc ("Choose an image to open...",
File::getSpecialLocation (File::userPicturesDirectory),
"*.jpg;*.jpeg;*.png;*.gif",
useNativeVersion);
if (fc.browseForMultipleFilesToOpen (&imagePreview))
{
String chosen;
for (int i = 0; i < fc.getResults().size(); ++i)
chosen << fc.getResults().getReference (i).getFullPathName() << "\n";
AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon,
"File Chooser...",
"You picked: " + chosen);
}
}
else if (type == saveChooser)
{
FileChooser fc ("Choose a file to save...",
File::getCurrentWorkingDirectory(),
"*",
useNativeVersion);
if (fc.browseForFileToSave (true))
{
File chosenFile = fc.getResult();
AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon,
"File Chooser...",
"You picked: " + chosenFile.getFullPathName());
}
}
else if (type == directoryChooser)
{
FileChooser fc ("Choose a directory...",
File::getCurrentWorkingDirectory(),
"*",
useNativeVersion);
if (fc.browseForDirectory())
{
File chosenDirectory = fc.getResult();
AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon,
"File Chooser...",
"You picked: " + chosenDirectory.getFullPathName());
}
}
#endif
}
}
void buttonClicked (Button* button) override
{
if (button == &nativeButton)
{
getLookAndFeel().setUsingNativeAlertWindows (nativeButton.getToggleState());
return;
}
for (int i = windowButtons.size(); --i >= 0;)
if (button == windowButtons.getUnchecked (i))
return showWindow (*button, static_cast<DialogType> (i));
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DialogsDemo);
};
// This static object will register this demo type in a global list of demos..
static JuceDemoType<DialogsDemo> demo ("10 Components: Dialog Boxes");
@@ -0,0 +1,258 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-12 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../JuceDemoHeader.h"
//==============================================================================
class FontsDemo : public Component,
private ListBoxModel,
private Slider::Listener,
private Button::Listener,
private ComboBox::Listener
{
public:
FontsDemo()
: heightLabel (String::empty, "Height:"),
kerningLabel (String::empty, "Kerning:"),
scaleLabel (String::empty, "Scale:"),
styleLabel ("Style"),
boldToggle ("Bold"),
italicToggle ("Italic")
{
setOpaque (true);
addAndMakeVisible (listBox);
addAndMakeVisible (demoTextBox);
addAndMakeVisible (heightSlider);
addAndMakeVisible (heightLabel);
addAndMakeVisible (kerningLabel);
addAndMakeVisible (kerningSlider);
addAndMakeVisible (scaleLabel);
addAndMakeVisible (scaleSlider);
addAndMakeVisible (boldToggle);
addAndMakeVisible (italicToggle);
addAndMakeVisible (styleBox);
kerningLabel.attachToComponent (&kerningSlider, true);
heightLabel.attachToComponent (&heightSlider, true);
scaleLabel.attachToComponent (&scaleSlider, true);
styleLabel.attachToComponent (&styleBox, true);
heightSlider.addListener (this);
kerningSlider.addListener (this);
scaleSlider.addListener (this);
boldToggle.addListener (this);
italicToggle.addListener (this);
styleBox.addListener (this);
Font::findFonts (fonts); // Generate the list of fonts
listBox.setRowHeight (20);
listBox.setModel (this); // Tell the listbox where to get its data model
heightSlider.setRange (3.0, 150.0, 0.01);
scaleSlider.setRange (0.2, 3.0, 0.01);
kerningSlider.setRange (-2.0, 2.0, 0.01);
scaleSlider.setValue (1.0); // Set some initial values for the sliders.
heightSlider.setValue (20.0);
kerningSlider.setValue (0);
// set up the layout and resizer bars..
verticalLayout.setItemLayout (0, -0.2, -0.8, -0.35); // width of the font list must be
// between 20% and 80%, preferably 50%
verticalLayout.setItemLayout (1, 8, 8, 8); // the vertical divider drag-bar thing is always 8 pixels wide
verticalLayout.setItemLayout (2, 150, -1.0, -0.65); // the components on the right must be
// at least 150 pixels wide, preferably 50% of the total width
verticalDividerBar = new StretchableLayoutResizerBar (&verticalLayout, 1, true);
addAndMakeVisible (verticalDividerBar);
// ..and pick a random font to select intially
listBox.selectRow (Random::getSystemRandom().nextInt (fonts.size()));
demoTextBox.setMultiLine (true);
demoTextBox.setReturnKeyStartsNewLine (true);
demoTextBox.setText ("Aa Bb Cc Dd Ee Ff Gg Hh Ii\n"
"Jj Kk Ll Mm Nn Oo Pp Qq Rr\n"
"Ss Tt Uu Vv Ww Xx Yy Zz\n"
"0123456789\n\n"
"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt "
"ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco "
"laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in "
"voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat "
"non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.");
demoTextBox.setCaretPosition (0);
}
//==============================================================================
void paint (Graphics& g)
{
fillTiledBackground (g);
}
void resized()
{
Rectangle<int> r (getLocalBounds().reduced (5));
// lay out the list box and vertical divider..
Component* vcomps[] = { &listBox, verticalDividerBar, nullptr };
verticalLayout.layOutComponents (vcomps, 3,
r.getX(), r.getY(), r.getWidth(), r.getHeight(),
false, // lay out side-by-side
true); // resize the components' heights as well as widths
r.removeFromLeft (verticalDividerBar->getRight());
styleBox.setBounds (r.removeFromBottom (26));
r.removeFromBottom (8);
const int labelWidth = 60;
Rectangle<int> row (r.removeFromBottom (30));
row.removeFromLeft (labelWidth);
boldToggle.setBounds (row.removeFromLeft (row.getWidth() / 2));
italicToggle.setBounds (row);
r.removeFromBottom (8);
scaleSlider.setBounds (r.removeFromBottom (30).withTrimmedLeft (labelWidth));
r.removeFromBottom (8);
kerningSlider.setBounds (r.removeFromBottom (30).withTrimmedLeft (labelWidth));
r.removeFromBottom (8);
heightSlider.setBounds (r.removeFromBottom (30).withTrimmedLeft (labelWidth));
r.removeFromBottom (8);
demoTextBox.setBounds (r);
}
void sliderValueChanged (Slider* sliderThatWasMoved)
{
if (sliderThatWasMoved == &heightSlider) refreshPreviewBoxFont();
else if (sliderThatWasMoved == &kerningSlider) refreshPreviewBoxFont();
else if (sliderThatWasMoved == &scaleSlider) refreshPreviewBoxFont();
}
void buttonClicked (Button* buttonThatWasClicked)
{
if (buttonThatWasClicked == &boldToggle) refreshPreviewBoxFont();
else if (buttonThatWasClicked == &italicToggle) refreshPreviewBoxFont();
}
// The following methods implement the ListBoxModel virtual methods:
int getNumRows()
{
return fonts.size();
}
void paintListBoxItem (int rowNumber, Graphics& g,
int width, int height, bool rowIsSelected)
{
if (rowIsSelected)
g.fillAll (Colours::lightblue);
Font font (fonts [rowNumber]);
AttributedString s;
s.setWordWrap (AttributedString::none);
s.setJustification (Justification::centredLeft);
s.append (font.getTypefaceName(), font.withPointHeight (height * 0.7f), Colours::black);
s.append (" " + font.getTypefaceName(), Font (height * 0.5f, Font::italic), Colours::grey);
s.draw (g, Rectangle<int> (width, height).expanded (-4, 50).toFloat());
}
void selectedRowsChanged (int /*lastRowselected*/)
{
refreshPreviewBoxFont();
}
private:
Array<Font> fonts;
StringArray currentStyleList;
ListBox listBox;
TextEditor demoTextBox;
Label heightLabel, kerningLabel, scaleLabel, styleLabel;
Slider heightSlider, kerningSlider, scaleSlider;
ToggleButton boldToggle, italicToggle;
ComboBox styleBox;
StretchableLayoutManager verticalLayout;
ScopedPointer<StretchableLayoutResizerBar> verticalDividerBar;
void refreshPreviewBoxFont()
{
const bool bold = boldToggle.getToggleState();
const bool italic = italicToggle.getToggleState();
const bool useStyle = ! (bold || italic);
Font font (fonts [listBox.getSelectedRow()]);
font = font.withPointHeight ((float) heightSlider.getValue())
.withExtraKerningFactor ((float) kerningSlider.getValue())
.withHorizontalScale ((float) scaleSlider.getValue());
if (bold) font = font.boldened();
if (italic) font = font.italicised();
updateStylesList (font);
styleBox.setEnabled (useStyle);
if (useStyle)
font = font.withTypefaceStyle (styleBox.getText());
demoTextBox.applyFontToAllText (font);
}
void updateStylesList (const Font& newFont)
{
const StringArray newStyles (newFont.getAvailableStyles());
if (newStyles != currentStyleList)
{
currentStyleList = newStyles;
styleBox.clear();
styleBox.addItemList (newStyles, 1);
styleBox.setSelectedItemIndex (0);
}
}
void comboBoxChanged (ComboBox* box) override
{
if (box == &styleBox)
refreshPreviewBoxFont();
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FontsDemo)
};
// This static object will register this demo type in a global list of demos..
static JuceDemoType<FontsDemo> demo ("20 Graphics: Fonts");
@@ -0,0 +1,715 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-12 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../JuceDemoHeader.h"
/** Holds the various toggle buttons for the animation modes. */
class ControllersComponent : public Component
{
public:
ControllersComponent()
{
setOpaque (true);
initialiseToggle (animatePosition, "Animate Position", true);
initialiseToggle (animateRotation, "Animate Rotation", true);
initialiseToggle (animateSize, "Animate Size", false);
initialiseToggle (animateShear, "Animate Shearing", false);
initialiseToggle (animateAlpha, "Animate Alpha", false);
initialiseToggle (clipToRectangle, "Clip to Rectangle", false);
initialiseToggle (clipToPath, "Clip to Path", false);
initialiseToggle (clipToImage, "Clip to Image", false);
initialiseToggle (quality, "Higher quality image interpolation", false);
}
void paint (Graphics& g)
{
fillTiledBackground (g);
}
void resized() override
{
Rectangle<int> r (getLocalBounds().reduced (4));
int buttonHeight = 22;
Rectangle<int> columns (r.removeFromTop (buttonHeight * 4));
Rectangle<int> col (columns.removeFromLeft (200));
animatePosition.setBounds (col.removeFromTop (buttonHeight));
animateRotation.setBounds (col.removeFromTop (buttonHeight));
animateSize.setBounds (col.removeFromTop (buttonHeight));
animateShear.setBounds (col.removeFromTop (buttonHeight));
columns.removeFromLeft (20);
col = columns.removeFromLeft (200);
animateAlpha.setBounds (col.removeFromTop (buttonHeight));
clipToRectangle.setBounds (col.removeFromTop (buttonHeight));
clipToPath.setBounds (col.removeFromTop (buttonHeight));
clipToImage.setBounds (col.removeFromTop (buttonHeight));
r.removeFromBottom (6);
quality.setBounds (r.removeFromTop (buttonHeight));
}
void initialiseToggle (ToggleButton& b, const char* name, bool on)
{
addAndMakeVisible (b);
b.setButtonText (name);
b.setToggleState (on, dontSendNotification);
}
ToggleButton animateRotation, animatePosition, animateAlpha, animateSize, animateShear;
ToggleButton clipToRectangle, clipToPath, clipToImage, quality;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ControllersComponent)
};
//==============================================================================
class GraphicsDemoBase : public Component
{
public:
GraphicsDemoBase (ControllersComponent& cc, const String& name)
: Component (name), controls (cc),
lastRenderStartTime (0),
averageTimeMs (0),
averageActualFPS (0)
{
displayFont = Font (Font::getDefaultMonospacedFontName(), 12.0f, Font::bold);
}
AffineTransform getTransform()
{
const float hw = 0.5f * getWidth();
const float hh = 0.5f * getHeight();
AffineTransform t;
if (controls.animateRotation.getToggleState())
t = t.rotated (rotation.getValue() * float_Pi * 2.0f);
if (controls.animateSize.getToggleState())
t = t.scaled (0.3f + size.getValue() * 2.0f);
if (controls.animatePosition.getToggleState())
t = t.translated (hw + hw * (offsetX.getValue() - 0.5f),
hh + hh * (offsetY.getValue() - 0.5f));
else
t = t.translated (hw, hh);
if (controls.animateShear.getToggleState())
t = t.sheared (shear.getValue() * 2.0f - 1.0f, 0.0f);
return t;
}
float getAlpha() const
{
if (controls.animateAlpha.getToggleState())
return alpha.getValue();
return 1.0f;
}
void paint (Graphics& g) override
{
double startTime = 0.0;
{
// A ScopedSaveState will return the Graphics context to the state it was at the time of
// construction when it goes out of scope. We use it here to avoid clipping the fps text
const Graphics::ScopedSaveState state (g);
if (controls.clipToRectangle.getToggleState()) clipToRectangle (g);
if (controls.clipToPath .getToggleState()) clipToPath (g);
if (controls.clipToImage .getToggleState()) clipToImage (g);
g.setImageResamplingQuality (controls.quality.getToggleState() ? Graphics::highResamplingQuality
: Graphics::mediumResamplingQuality);
// take a note of the time before the render
startTime = Time::getMillisecondCounterHiRes();
// then let the demo draw itself..
drawDemo (g);
}
double now = Time::getMillisecondCounterHiRes();
double filtering = 0.08;
const double elapsedMs = now - startTime;
averageTimeMs += (elapsedMs - averageTimeMs) * filtering;
const double sinceLastRender = now - lastRenderStartTime;
lastRenderStartTime = now;
const double effectiveFPS = 1000.0 / averageTimeMs;
const double actualFPS = sinceLastRender > 0 ? (1000.0 / sinceLastRender) : 0;
averageActualFPS += (actualFPS - averageActualFPS) * filtering;
GlyphArrangement ga;
ga.addFittedText (displayFont,
"Time: " + String (averageTimeMs, 2)
+ " ms\nEffective FPS: " + String (effectiveFPS, 1)
+ "\nActual FPS: " + String (averageActualFPS, 1),
0, 10.0f, getWidth() - 10.0f, (float) getHeight(), Justification::topRight, 3);
g.setColour (Colours::white.withAlpha (0.5f));
g.fillRect (ga.getBoundingBox (0, ga.getNumGlyphs(), true).getSmallestIntegerContainer().expanded (4));
g.setColour (Colours::black);
ga.draw (g);
}
virtual void drawDemo (Graphics&) = 0;
void clipToRectangle (Graphics& g)
{
int w = getWidth() / 2;
int h = getHeight() / 2;
int x = (int) (w * clipRectX.getValue());
int y = (int) (h * clipRectY.getValue());
g.reduceClipRegion (x, y, w, h);
}
void clipToPath (Graphics& g)
{
float pathSize = (float) jmin (getWidth(), getHeight());
Path p;
p.addStar (Point<float> (clipPathX.getValue(),
clipPathY.getValue()) * pathSize,
7,
pathSize * (0.5f + clipPathDepth.getValue()),
pathSize * 0.5f,
clipPathAngle.getValue());
g.reduceClipRegion (p, AffineTransform::identity);
}
void clipToImage (Graphics& g)
{
if (! clipImage.isValid())
createClipImage();
AffineTransform transform (AffineTransform::translation (clipImage.getWidth() / -2.0f,
clipImage.getHeight() / -2.0f)
.rotated (clipImageAngle.getValue() * float_Pi * 2.0f)
.scaled (2.0f + clipImageSize.getValue() * 3.0f)
.translated (getWidth() * 0.5f,
getHeight() * 0.5f));
g.reduceClipRegion (clipImage, transform);
}
void createClipImage()
{
clipImage = Image (Image::ARGB, 300, 300, true);
Graphics g (clipImage);
g.setGradientFill (ColourGradient (Colours::transparentBlack, 0, 0,
Colours::black, 0, 300, false));
for (int i = 0; i < 20; ++i)
g.fillRect (Random::getSystemRandom().nextInt (200),
Random::getSystemRandom().nextInt (200),
Random::getSystemRandom().nextInt (100),
Random::getSystemRandom().nextInt (100));
}
//==============================================================================
ControllersComponent& controls;
SlowerBouncingNumber offsetX, offsetY, rotation, size, shear, alpha, clipRectX,
clipRectY, clipPathX, clipPathY, clipPathDepth, clipPathAngle,
clipImageX, clipImageY, clipImageAngle, clipImageSize;
double lastRenderStartTime, averageTimeMs, averageActualFPS;
Image clipImage;
Font displayFont;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GraphicsDemoBase)
};
//==============================================================================
class RectangleFillTypesDemo : public GraphicsDemoBase
{
public:
RectangleFillTypesDemo (ControllersComponent& cc)
: GraphicsDemoBase (cc, "Fill Types: Rectangles"),
colour1 (Colours::red),
colour2 (Colours::green)
{
}
void drawDemo (Graphics& g) override
{
g.addTransform (getTransform());
const int rectSize = jmin (getWidth(), getHeight()) / 2 - 20;
g.setColour (colour1.withAlpha (getAlpha()));
g.fillRect (-rectSize, -rectSize, rectSize, rectSize);
g.setGradientFill (ColourGradient (colour1, 10.0f, (float) -rectSize,
colour2, 10.0f + rectSize, 0.0f, false));
g.setOpacity (getAlpha());
g.fillRect (10, -rectSize, rectSize, rectSize);
g.setGradientFill (ColourGradient (colour1, rectSize * -0.5f, 10.0f + rectSize * 0.5f,
colour2, 0, 10.0f + rectSize, true));
g.setOpacity (getAlpha());
g.fillRect (-rectSize, 10, rectSize, rectSize);
g.setGradientFill (ColourGradient (colour1, 10.0f, 10.0f,
colour2, 10.0f + rectSize, 10.0f + rectSize, false));
g.setOpacity (getAlpha());
g.drawRect (10, 10, rectSize, rectSize, 5);
}
Colour colour1, colour2;
};
//==============================================================================
class PathsDemo : public GraphicsDemoBase
{
public:
PathsDemo (ControllersComponent& cc, bool linear, bool radial)
: GraphicsDemoBase (cc, String ("Paths") + (radial ? ": Radial Gradients"
: (linear ? ": Linear Gradients"
: ": Solid"))),
useLinearGradient (linear), useRadialGradient (radial)
{
logoPath = MainAppWindow::getJUCELogoPath();
// rescale the logo path so that it's centred about the origin and has the right size.
logoPath.applyTransform (RectanglePlacement (RectanglePlacement::centred)
.getTransformToFit (logoPath.getBounds(),
Rectangle<float> (-200.0f, -200.0f, 400.0f, 400.0f)));
// Surround it with some other shapes..
logoPath.addStar (Point<float> (-300.0f, -50.0f), 7, 30.0f, 70.0f, 0.1f);
logoPath.addStar (Point<float> (300.0f, 50.0f), 6, 40.0f, 70.0f, 0.1f);
logoPath.addEllipse (-100.0f, 150.0f, 200.0f, 140.0f);
logoPath.addRectangle (-100.0f, -280.0f, 200.0f, 140.0f);
}
void drawDemo (Graphics& g) override
{
const Path& p = logoPath;
if (useLinearGradient || useRadialGradient)
{
Colour c1 (gradientColours[0].getValue(), gradientColours[1].getValue(), gradientColours[2].getValue(), 1.0f);
Colour c2 (gradientColours[3].getValue(), gradientColours[4].getValue(), gradientColours[5].getValue(), 1.0f);
Colour c3 (gradientColours[6].getValue(), gradientColours[7].getValue(), gradientColours[8].getValue(), 1.0f);
float x1 = gradientPositions[0].getValue() * getWidth() * 0.25f;
float y1 = gradientPositions[1].getValue() * getHeight() * 0.25f;
float x2 = gradientPositions[2].getValue() * getWidth() * 0.75f;
float y2 = gradientPositions[3].getValue() * getHeight() * 0.75f;
ColourGradient gradient (c1, x1, y1,
c2, x2, y2,
useRadialGradient);
gradient.addColour (gradientIntermediate.getValue(), c3);
g.setGradientFill (gradient);
}
else
{
g.setColour (Colours::blue);
}
g.setOpacity (getAlpha());
g.fillPath (p, getTransform());
}
Path logoPath;
bool useLinearGradient, useRadialGradient;
SlowerBouncingNumber gradientColours[9], gradientPositions[4], gradientIntermediate;
};
//==============================================================================
class StrokesDemo : public GraphicsDemoBase
{
public:
StrokesDemo (ControllersComponent& cc)
: GraphicsDemoBase (cc, "Paths: Stroked")
{
}
void drawDemo (Graphics& g) override
{
float w = (float) getWidth();
float h = (float) getHeight();
Path p;
p.startNewSubPath (points[0].getValue() * w,
points[1].getValue() * h);
for (int i = 2; i < numElementsInArray (points); i += 4)
p.quadraticTo (points[i].getValue() * w,
points[i + 1].getValue() * h,
points[i + 2].getValue() * w,
points[i + 3].getValue() * h);
p.closeSubPath();
PathStrokeType stroke (0.5f + 10.0f * thickness.getValue());
g.setColour (Colours::purple.withAlpha (getAlpha()));
g.strokePath (p, stroke, AffineTransform::identity);
}
SlowerBouncingNumber points[2 + 4 * 8], thickness;
};
//==============================================================================
class ImagesRenderingDemo : public GraphicsDemoBase
{
public:
ImagesRenderingDemo (ControllersComponent& cc, bool argb_, bool tiled_)
: GraphicsDemoBase (cc, String ("Images") + (argb_ ? ": ARGB" : ": RGB") + (tiled_ ? " Tiled" : String::empty )),
isArgb (argb_), isTiled (tiled_)
{
argbImage = ImageFileFormat::loadFrom (BinaryData::juce_icon_png, (size_t) BinaryData::juce_icon_pngSize);
rgbImage = ImageFileFormat::loadFrom (BinaryData::portmeirion_jpg, (size_t) BinaryData::portmeirion_jpgSize);
}
void drawDemo (Graphics& g) override
{
Image image = isArgb ? argbImage : rgbImage;
AffineTransform transform (AffineTransform::translation ((float) (image.getWidth() / -2),
(float) (image.getHeight() / -2))
.followedBy (getTransform()));
if (isTiled)
{
FillType fill (image, transform);
fill.setOpacity (getAlpha());
g.setFillType (fill);
g.fillAll();
}
else
{
g.setOpacity (getAlpha());
g.drawImageTransformed (image, transform, false);
}
}
bool isArgb, isTiled;
Image rgbImage, argbImage;
};
//==============================================================================
class GlyphsDemo : public GraphicsDemoBase
{
public:
GlyphsDemo (ControllersComponent& cc)
: GraphicsDemoBase (cc, "Glyphs")
{
glyphs.addFittedText (Font (20.0f), "The Quick Brown Fox Jumped Over The Lazy Dog",
-120, -50, 240, 100, Justification::centred, 2, 1.0f);
}
void drawDemo (Graphics& g) override
{
g.setColour (Colours::black.withAlpha (getAlpha()));
glyphs.draw (g, getTransform());
}
GlyphArrangement glyphs;
};
//==============================================================================
class SVGDemo : public GraphicsDemoBase
{
public:
SVGDemo (ControllersComponent& cc)
: GraphicsDemoBase (cc, "SVG")
{
createSVGDrawable();
}
void drawDemo (Graphics& g) override
{
if (Time::getCurrentTime().toMilliseconds() > lastSVGLoadTime.toMilliseconds() + 2000)
createSVGDrawable();
svgDrawable->draw (g, getAlpha(), getTransform());
}
void createSVGDrawable()
{
lastSVGLoadTime = Time::getCurrentTime();
MemoryInputStream iconsFileStream (BinaryData::icons_zip, BinaryData::icons_zipSize, false);
ZipFile icons (&iconsFileStream, false);
// Load a random SVG file from our embedded icons.zip file.
const ScopedPointer<InputStream> svgFileStream (icons.createStreamForEntry (Random::getSystemRandom().nextInt (icons.getNumEntries())));
if (svgFileStream != nullptr)
{
svgDrawable = dynamic_cast <DrawableComposite*> (Drawable::createFromImageDataStream (*svgFileStream));
if (svgDrawable != nullptr)
{
// to make our icon the right size, we'll set its bounding box to the size and position that we want.
svgDrawable->setBoundingBox (RelativeParallelogram (Point<float> (-100, -100),
Point<float> (100, -100),
Point<float> (-100, 100)));
}
}
}
Time lastSVGLoadTime;
ScopedPointer<DrawableComposite> svgDrawable;
};
//==============================================================================
class LinesDemo : public GraphicsDemoBase
{
public:
LinesDemo (ControllersComponent& cc)
: GraphicsDemoBase (cc, "Lines")
{
}
void drawDemo (Graphics& g) override
{
{
RectangleList<float> verticalLines;
verticalLines.ensureStorageAllocated (getWidth());
float pos = offset.getValue();
for (int x = 0; x < getWidth(); ++x)
{
float y = getHeight() * 0.3f;
float length = y * std::abs (std::sin (x / 100.0f + 2.0f * pos));
verticalLines.addWithoutMerging (Rectangle<float> ((float) x, y - length * 0.5f, 1.0f, length));
}
g.setColour (Colours::blue.withAlpha (getAlpha()));
g.fillRectList (verticalLines);
}
{
RectangleList<float> horizontalLines;
horizontalLines.ensureStorageAllocated (getHeight());
float pos = offset.getValue();
for (int y = 0; y < getHeight(); ++y)
{
float x = getWidth() * 0.3f;
float length = x * std::abs (std::sin (y / 100.0f + 2.0f * pos));
horizontalLines.addWithoutMerging (Rectangle<float> (x - length * 0.5f, (float) y, length, 1.0f));
}
g.setColour (Colours::green.withAlpha (getAlpha()));
g.fillRectList (horizontalLines);
}
g.setColour (Colours::red.withAlpha (getAlpha()));
const float w = (float) getWidth();
const float h = (float) getHeight();
g.drawLine (positions[0].getValue() * w,
positions[1].getValue() * h,
positions[2].getValue() * w,
positions[3].getValue() * h);
g.drawLine (positions[4].getValue() * w,
positions[5].getValue() * h,
positions[6].getValue() * w,
positions[7].getValue() * h);
}
SlowerBouncingNumber offset, positions[8];
};
//==============================================================================
class DemoHolderComponent : public Component,
private Timer
{
public:
DemoHolderComponent()
: currentDemo (nullptr)
{
setOpaque (true);
}
void paint (Graphics& g)
{
g.fillCheckerBoard (getLocalBounds(), 48, 48,
Colours::lightgrey, Colours::white);
}
void timerCallback()
{
if (currentDemo != nullptr)
currentDemo->repaint();
}
void setDemo (GraphicsDemoBase* newDemo)
{
if (currentDemo != nullptr)
removeChildComponent (currentDemo);
currentDemo = newDemo;
if (currentDemo != nullptr)
{
addAndMakeVisible (currentDemo);
startTimerHz (60);
resized();
}
}
void resized()
{
if (currentDemo != nullptr)
currentDemo->setBounds (getLocalBounds());
}
private:
GraphicsDemoBase* currentDemo;
};
//==============================================================================
class TestListComponent : public Component,
private ListBoxModel
{
public:
TestListComponent (DemoHolderComponent& holder, ControllersComponent& controls)
: demoHolder (holder)
{
demos.add (new PathsDemo (controls, false, true));
demos.add (new PathsDemo (controls, true, false));
demos.add (new PathsDemo (controls, false, false));
demos.add (new RectangleFillTypesDemo (controls));
demos.add (new StrokesDemo (controls));
demos.add (new ImagesRenderingDemo (controls, false, false));
demos.add (new ImagesRenderingDemo (controls, false, true));
demos.add (new ImagesRenderingDemo (controls, true, false));
demos.add (new ImagesRenderingDemo (controls, true, true));
demos.add (new GlyphsDemo (controls));
demos.add (new SVGDemo (controls));
demos.add (new LinesDemo (controls));
addAndMakeVisible (listBox);
listBox.setModel (this);
listBox.selectRow (0);
listBox.setColour (ListBox::backgroundColourId, Colour::greyLevel (0.9f));
}
void resized()
{
listBox.setBounds (getLocalBounds());
}
int getNumRows()
{
return demos.size();
}
void paintListBoxItem (int rowNumber, Graphics& g, int width, int height, bool rowIsSelected)
{
Component* demo = demos [rowNumber];
if (demo != nullptr)
{
if (rowIsSelected)
g.fillAll (findColour (TextEditor::highlightColourId));
g.setColour (Colours::black);
g.setFont (14.0f);
g.drawFittedText (demo->getName(), 8, 0, width - 10, height, Justification::centredLeft, 2);
}
}
void selectedRowsChanged (int lastRowSelected)
{
demoHolder.setDemo (demos [lastRowSelected]);
}
private:
DemoHolderComponent& demoHolder;
ListBox listBox;
OwnedArray<GraphicsDemoBase> demos;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TestListComponent);
};
//==============================================================================
class GraphicsDemo : public Component
{
public:
GraphicsDemo()
: testList (demoHolder, controllersComponent)
{
setOpaque (true);
addAndMakeVisible (demoHolder);
addAndMakeVisible (controllersComponent);
addAndMakeVisible (performanceDisplay);
addAndMakeVisible (testList);
}
void paint (Graphics& g)
{
g.fillAll (Colours::grey);
}
void resized() override
{
Rectangle<int> area (getLocalBounds());
controllersComponent.setBounds (area.removeFromBottom (150));
testList.setBounds (area.removeFromRight (150));
demoHolder.setBounds (area);
performanceDisplay.setBounds (area.removeFromTop (20).removeFromRight (100));
}
private:
ControllersComponent controllersComponent;
DemoHolderComponent demoHolder;
Label performanceDisplay;
TestListComponent testList;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GraphicsDemo);
};
// This static object will register this demo type in a global list of demos..
static JuceDemoType<GraphicsDemo> demo ("20 Graphics: 2D Rendering");
@@ -0,0 +1,122 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-12 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../JuceDemoHeader.h"
//==============================================================================
class ImagesDemo : public Component,
public FileBrowserListener
{
public:
ImagesDemo()
: imagesWildcardFilter ("*.jpeg;*.jpg;*.png;*.gif", "*", "Image File Filter"),
directoryThread ("Image File Scanner Thread"),
imageList (&imagesWildcardFilter, directoryThread),
fileTree (imageList),
resizerBar (&stretchableManager, 1, false)
{
setOpaque (true);
imageList.setDirectory (File::getSpecialLocation (File::userPicturesDirectory), true, true);
directoryThread.startThread (1);
fileTree.addListener (this);
fileTree.setColour (TreeView::backgroundColourId, Colours::lightgrey.withAlpha (0.6f));
addAndMakeVisible (fileTree);
addAndMakeVisible (resizerBar);
addAndMakeVisible (imagePreview);
// we have to set up our StretchableLayoutManager so it know the limits and preferred sizes of it's contents
stretchableManager.setItemLayout (0, // for the fileTree
-0.1, -0.9, // must be between 50 pixels and 90% of the available space
-0.3); // and its preferred size is 30% of the total available space
stretchableManager.setItemLayout (1, // for the resize bar
5, 5, 5); // hard limit to 5 pixels
stretchableManager.setItemLayout (2, // for the imagePreview
-0.1, -0.9, // size must be between 50 pixels and 90% of the available space
-0.7); // and its preferred size is 70% of the total available space
}
~ImagesDemo()
{
fileTree.removeListener (this);
}
void paint (Graphics& g) override
{
fillTiledBackground (g);
}
void resized() override
{
Rectangle<int> r (getLocalBounds().reduced (4));
// make a list of two of our child components that we want to reposition
Component* comps[] = { &fileTree, &resizerBar, &imagePreview };
// this will position the 3 components, one above the other, to fit
// vertically into the rectangle provided.
stretchableManager.layOutComponents (comps, 3,
r.getX(), r.getY(), r.getWidth(), r.getHeight(),
true, true);
}
private:
WildcardFileFilter imagesWildcardFilter;
TimeSliceThread directoryThread;
DirectoryContentsList imageList;
FileTreeComponent fileTree;
ImageComponent imagePreview;
StretchableLayoutManager stretchableManager;
StretchableLayoutResizerBar resizerBar;
void selectionChanged() override
{
// we're only really interested in when the selection changes, regardless of if it was
// clicked or not so we'll only override this method
const File selectedFile (fileTree.getSelectedFile());
if (selectedFile.existsAsFile())
imagePreview.setImage (ImageCache::getFromFile (selectedFile));
// the image cahce is a handly way to load images from files or directly from memory and
// will keep them hanging around for a few seconds in case they are requested elsewhere
}
void fileClicked (const File&, const MouseEvent&) override {}
void fileDoubleClicked (const File&) override {}
void browserRootChanged (const File&) override {}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ImagesDemo)
};
// This static object will register this demo type in a global list of demos..
static JuceDemoType<ImagesDemo> demo ("20 Graphics: Image formats");
@@ -0,0 +1,159 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-12 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../JuceDemoHeader.h"
//==============================================================================
class JavaScriptDemo : public Component,
private CodeDocument::Listener,
private Timer
{
public:
JavaScriptDemo()
{
setOpaque (true);
addAndMakeVisible (editor = new CodeEditorComponent (codeDocument, nullptr));
editor->setFont (Font (Font::getDefaultMonospacedFontName(), 14.0f, Font::plain));
editor->setTabSize (4, true);
outputDisplay.setMultiLine (true);
outputDisplay.setReadOnly (true);
outputDisplay.setCaretVisible (false);
outputDisplay.setFont (Font (Font::getDefaultMonospacedFontName(), 14.0f, Font::plain));
addAndMakeVisible (outputDisplay);
codeDocument.addListener (this);
editor->loadContent (
"/*\n"
" Javascript! In this simple demo, the native\n"
" code provides an object called \'Demo\' which\n"
" has a method \'print\' that writes to the\n"
" console below...\n"
"*/\n"
"\n"
"Demo.print (\"Hello World in JUCE + Javascript!\");\n"
"Demo.print (\"\");\n"
"\n"
"function factorial (n)\n"
"{\n"
" var total = 1;\n"
" while (n > 0)\n"
" total = total * n--;\n"
" return total;\n"
"}\n"
"\n"
"for (var i = 1; i < 10; ++i)\n"
" Demo.print (\"Factorial of \" + i \n"
" + \" = \" + factorial (i));\n");
}
void runScript()
{
outputDisplay.clear();
JavascriptEngine engine;
engine.maximumExecutionTime = RelativeTime::seconds (5);
engine.registerNativeObject ("Demo", new DemoClass (*this));
const double startTime = Time::getMillisecondCounterHiRes();
Result result = engine.execute (codeDocument.getAllContent());
const double elapsedMs = Time::getMillisecondCounterHiRes() - startTime;
if (result.failed())
outputDisplay.setText (result.getErrorMessage());
else
outputDisplay.insertTextAtCaret ("\n(Execution time: " + String (elapsedMs, 2) + " milliseconds)");
}
void consoleOutput (const String& message)
{
outputDisplay.moveCaretToEnd();
outputDisplay.insertTextAtCaret (message + newLine);
}
//==============================================================================
// This class is used by the script, and provides methods that the JS can call.
struct DemoClass : public DynamicObject
{
DemoClass (JavaScriptDemo& demo) : owner (demo)
{
setMethod ("print", print);
}
static Identifier getClassName() { return "Demo"; }
static var print (const var::NativeFunctionArgs& args)
{
if (args.numArguments > 0)
if (DemoClass* thisObject = dynamic_cast<DemoClass*> (args.thisObject.getObject()))
thisObject->owner.consoleOutput (args.arguments[0].toString());
return var::undefined();
}
JavaScriptDemo& owner;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DemoClass)
};
void paint (Graphics& g)
{
fillTiledBackground (g);
}
private:
CodeDocument codeDocument;
ScopedPointer<CodeEditorComponent> editor;
TextEditor outputDisplay;
void codeDocumentTextInserted (const String&, int) override { startTimer (300); }
void codeDocumentTextDeleted (int, int) override { startTimer (300); }
void timerCallback() override
{
stopTimer();
runScript();
}
void resized() override
{
Rectangle<int> r (getLocalBounds().reduced (8));
editor->setBounds (r.removeFromTop (proportionOfHeight (0.6f)));
outputDisplay.setBounds (r.withTrimmedTop (8));
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JavaScriptDemo)
};
// This static object will register this demo type in a global list of demos..
static JuceDemoType<JavaScriptDemo> demo ("40 JavaScript");
@@ -0,0 +1,58 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-12 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../JuceDemoHeader.h"
//==============================================================================
class KeyMappingsDemo : public Component
{
public:
KeyMappingsDemo()
: keyMappingEditor (*MainAppWindow::getApplicationCommandManager().getKeyMappings(), true)
{
setOpaque (true);
addAndMakeVisible (keyMappingEditor);
}
void paint (Graphics& g) override
{
g.fillAll (Colour::greyLevel (0.93f));
}
void resized() override
{
keyMappingEditor.setBounds (getLocalBounds().reduced (4));
}
private:
KeyMappingEditorComponent keyMappingEditor;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (KeyMappingsDemo);
};
// This static object will register this demo type in a global list of demos..
static JuceDemoType<KeyMappingsDemo> demo ("01 Shortcut Keys");
@@ -0,0 +1,119 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-12 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../JuceDemoHeader.h"
//==============================================================================
struct LiveConstantDemoComponent : public Component
{
LiveConstantDemoComponent() {}
void paint (Graphics& g)
{
g.fillAll (JUCE_LIVE_CONSTANT (Colour (0xffe5e7a7)));
g.setColour (JUCE_LIVE_CONSTANT (Colours::red.withAlpha (0.2f)));
int blockWidth = JUCE_LIVE_CONSTANT (0x120);
int blockHeight = JUCE_LIVE_CONSTANT (200);
g.fillRect ((getWidth() - blockWidth) / 2, (getHeight() - blockHeight) / 2, blockWidth, blockHeight);
Colour fontColour = JUCE_LIVE_CONSTANT (Colour (0xff000a55));
float fontSize = JUCE_LIVE_CONSTANT (30.0f);
g.setColour (fontColour);
g.setFont (fontSize);
g.drawFittedText (getDemoText(), getLocalBounds(), Justification::centred, 2);
}
static String getDemoText()
{
return JUCE_LIVE_CONSTANT ("Hello world!");
}
};
//==============================================================================
class LiveConstantEditorDemo : public Component,
private ButtonListener
{
public:
LiveConstantEditorDemo()
: startButton ("Begin Demo")
{
descriptionLabel.setMinimumHorizontalScale (1.0f);
descriptionLabel.setText ("This demonstrates the JUCE_LIVE_CONSTANT macro, which allows you to quickly "
"adjust primitive values at runtime by just wrapping them in a macro.\n\n"
"To understand what's going on in this demo, you should have a look at the "
"LiveConstantDemoComponent class in LiveConstantDemo.cpp, where you can see "
"the code that's invoking the demo below...",
dontSendNotification);
addAndMakeVisible (descriptionLabel);
addAndMakeVisible (startButton);
addChildComponent (demoComp);
startButton.addListener (this);
}
void paint (Graphics& g) override
{
fillTiledBackground (g);
}
void resized() override
{
Rectangle<int> r (getLocalBounds().reduced (10));
demoComp.setBounds (r);
descriptionLabel.setBounds (r.removeFromTop (200));
startButton.setBounds (r.removeFromTop (22).removeFromLeft (250));
demoComp.setBounds (r.withTrimmedTop (10));
}
private:
Label descriptionLabel;
TextButton startButton;
LiveConstantDemoComponent demoComp;
void buttonClicked (Button*) override
{
startButton.setVisible (false);
demoComp.setVisible (true);
descriptionLabel.setText ("Tweak some of the colours and values in the pop-up window to see what "
"the effect of your changes would be on the component below...",
dontSendNotification);
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LiveConstantEditorDemo);
};
#if ! (JUCE_IOS || JUCE_ANDROID)
// This static object will register this demo type in a global list of demos..
static JuceDemoType<LiveConstantEditorDemo> demo ("10 Components: Live Constants");
#endif
@@ -0,0 +1,615 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-12 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../JuceDemoHeader.h"
//==============================================================================
/** Custom Look And Feel subclasss.
Simply override the methods you need to, anything else will be inherited from the base class.
It's a good idea not to hard code your colours, use the findColour method along with appropriate
ColourIds so you can set these on a per-component basis.
*/
struct CustomLookAndFeel : public LookAndFeel_V3
{
void drawRoundThumb (Graphics& g, const float x, const float y,
const float diameter, const Colour& colour, float outlineThickness)
{
const Rectangle<float> a (x, y, diameter, diameter);
const float halfThickness = outlineThickness * 0.5f;
Path p;
p.addEllipse (x + halfThickness, y + halfThickness, diameter - outlineThickness, diameter - outlineThickness);
const DropShadow ds (Colours::black, 1, Point<int> (0, 0));
ds.drawForPath (g, p);
g.setColour (colour);
g.fillPath (p);
g.setColour (colour.brighter());
g.strokePath (p, PathStrokeType (outlineThickness));
}
void drawButtonBackground (Graphics& g, Button& button, const Colour& backgroundColour,
bool isMouseOverButton, bool isButtonDown) override
{
Colour baseColour (backgroundColour.withMultipliedSaturation (button.hasKeyboardFocus (true) ? 1.3f : 0.9f)
.withMultipliedAlpha (button.isEnabled() ? 0.9f : 0.5f));
if (isButtonDown || isMouseOverButton)
baseColour = baseColour.contrasting (isButtonDown ? 0.2f : 0.1f);
const bool flatOnLeft = button.isConnectedOnLeft();
const bool flatOnRight = button.isConnectedOnRight();
const bool flatOnTop = button.isConnectedOnTop();
const bool flatOnBottom = button.isConnectedOnBottom();
const float width = button.getWidth() - 1.0f;
const float height = button.getHeight() - 1.0f;
if (width > 0 && height > 0)
{
const float cornerSize = jmin (15.0f, jmin (width, height) * 0.45f);
const float lineThickness = cornerSize * 0.1f;
const float halfThickness = lineThickness * 0.5f;
Path outline;
outline.addRoundedRectangle (0.5f + halfThickness, 0.5f + halfThickness, width - lineThickness, height - lineThickness,
cornerSize, cornerSize,
! (flatOnLeft || flatOnTop),
! (flatOnRight || flatOnTop),
! (flatOnLeft || flatOnBottom),
! (flatOnRight || flatOnBottom));
const Colour outlineColour (button.findColour (button.getToggleState() ? TextButton::textColourOnId
: TextButton::textColourOffId));
g.setColour (baseColour);
g.fillPath (outline);
if (! button.getToggleState())
{
g.setColour (outlineColour);
g.strokePath (outline, PathStrokeType (lineThickness));
}
}
}
void drawTickBox (Graphics& g, Component& component,
float x, float y, float w, float h,
bool ticked,
bool isEnabled,
bool isMouseOverButton,
bool isButtonDown) override
{
const float boxSize = w * 0.7f;
bool isDownOrDragging = component.isEnabled() && (component.isMouseOverOrDragging() || component.isMouseButtonDown());
const Colour colour (component.findColour (TextButton::buttonColourId).withMultipliedSaturation ((component.hasKeyboardFocus (false) || isDownOrDragging) ? 1.3f : 0.9f)
.withMultipliedAlpha (component.isEnabled() ? 1.0f : 0.7f));
drawRoundThumb (g, x, y + (h - boxSize) * 0.5f, boxSize, colour,
isEnabled ? ((isButtonDown || isMouseOverButton) ? 1.1f : 0.5f) : 0.3f);
if (ticked)
{
const Path tick (LookAndFeel_V2::getTickShape (6.0f));
g.setColour (isEnabled ? findColour (TextButton::buttonOnColourId) : Colours::grey);
const float scale = 9.0f;
const AffineTransform trans (AffineTransform::scale (w / scale, h / scale)
.translated (x - 2.5f, y + 1.0f));
g.fillPath (tick, trans);
}
}
void drawLinearSliderThumb (Graphics& g, int x, int y, int width, int height,
float sliderPos, float minSliderPos, float maxSliderPos,
const Slider::SliderStyle style, Slider& slider) override
{
const float sliderRadius = (float) (getSliderThumbRadius (slider) - 2);
bool isDownOrDragging = slider.isEnabled() && (slider.isMouseOverOrDragging() || slider.isMouseButtonDown());
Colour knobColour (slider.findColour (Slider::thumbColourId).withMultipliedSaturation ((slider.hasKeyboardFocus (false) || isDownOrDragging) ? 1.3f : 0.9f)
.withMultipliedAlpha (slider.isEnabled() ? 1.0f : 0.7f));
if (style == Slider::LinearHorizontal || style == Slider::LinearVertical)
{
float kx, ky;
if (style == Slider::LinearVertical)
{
kx = x + width * 0.5f;
ky = sliderPos;
}
else
{
kx = sliderPos;
ky = y + height * 0.5f;
}
const float outlineThickness = slider.isEnabled() ? 0.8f : 0.3f;
drawRoundThumb (g,
kx - sliderRadius,
ky - sliderRadius,
sliderRadius * 2.0f,
knobColour, outlineThickness);
}
else
{
// Just call the base class for the demo
LookAndFeel_V2::drawLinearSliderThumb (g, x, y, width, height, sliderPos, minSliderPos, maxSliderPos, style, slider);
}
}
void drawLinearSlider (Graphics& g, int x, int y, int width, int height,
float sliderPos, float minSliderPos, float maxSliderPos,
const Slider::SliderStyle style, Slider& slider) override
{
g.fillAll (slider.findColour (Slider::backgroundColourId));
if (style == Slider::LinearBar || style == Slider::LinearBarVertical)
{
const float fx = (float) x, fy = (float) y, fw = (float) width, fh = (float) height;
Path p;
if (style == Slider::LinearBarVertical)
p.addRectangle (fx, sliderPos, fw, 1.0f + fh - sliderPos);
else
p.addRectangle (fx, fy, sliderPos - fx, fh);
Colour baseColour (slider.findColour (Slider::rotarySliderFillColourId)
.withMultipliedSaturation (slider.isEnabled() ? 1.0f : 0.5f)
.withMultipliedAlpha (0.8f));
g.setColour (baseColour);
g.fillPath (p);
const float lineThickness = jmin (15.0f, jmin (width, height) * 0.45f) * 0.1f;
g.drawRect (slider.getLocalBounds().toFloat(), lineThickness);
}
else
{
drawLinearSliderBackground (g, x, y, width, height, sliderPos, minSliderPos, maxSliderPos, style, slider);
drawLinearSliderThumb (g, x, y, width, height, sliderPos, minSliderPos, maxSliderPos, style, slider);
}
}
void drawLinearSliderBackground (Graphics& g, int x, int y, int width, int height,
float /*sliderPos*/,
float /*minSliderPos*/,
float /*maxSliderPos*/,
const Slider::SliderStyle /*style*/, Slider& slider) override
{
const float sliderRadius = getSliderThumbRadius (slider) - 5.0f;
Path on, off;
if (slider.isHorizontal())
{
const float iy = x + width * 0.5f - sliderRadius * 0.5f;
Rectangle<float> r (x - sliderRadius * 0.5f, iy, width + sliderRadius, sliderRadius);
const float onW = r.getWidth() * ((float) slider.valueToProportionOfLength (slider.getValue()));
on.addRectangle (r.removeFromLeft (onW));
off.addRectangle (r);
}
else
{
const float ix = x + width * 0.5f - sliderRadius * 0.5f;
Rectangle<float> r (ix, y - sliderRadius * 0.5f, sliderRadius, height + sliderRadius);
const float onH = r.getHeight() * ((float) slider.valueToProportionOfLength (slider.getValue()));
on.addRectangle (r.removeFromBottom (onH));
off.addRectangle (r);
}
g.setColour (slider.findColour (Slider::rotarySliderFillColourId));
g.fillPath (on);
g.setColour (slider.findColour (Slider::trackColourId));
g.fillPath (off);
}
void drawRotarySlider (Graphics& g, int x, int y, int width, int height, float sliderPos,
float rotaryStartAngle, float rotaryEndAngle, Slider& slider) override
{
const float radius = jmin (width / 2, height / 2) - 2.0f;
const float centreX = x + width * 0.5f;
const float centreY = y + height * 0.5f;
const float rx = centreX - radius;
const float ry = centreY - radius;
const float rw = radius * 2.0f;
const float angle = rotaryStartAngle + sliderPos * (rotaryEndAngle - rotaryStartAngle);
const bool isMouseOver = slider.isMouseOverOrDragging() && slider.isEnabled();
if (slider.isEnabled())
g.setColour (slider.findColour (Slider::rotarySliderFillColourId).withAlpha (isMouseOver ? 1.0f : 0.7f));
else
g.setColour (Colour (0x80808080));
{
Path filledArc;
filledArc.addPieSegment (rx, ry, rw, rw, rotaryStartAngle, angle, 0.0);
g.fillPath (filledArc);
}
{
const float lineThickness = jmin (15.0f, jmin (width, height) * 0.45f) * 0.1f;
Path outlineArc;
outlineArc.addPieSegment (rx, ry, rw, rw, rotaryStartAngle, rotaryEndAngle, 0.0);
g.strokePath (outlineArc, PathStrokeType (lineThickness));
}
}
};
//==============================================================================
/** Another really simple look and feel that is very flat and square.
This inherits from CustomLookAndFeel above for the linear bar and slider backgrounds.
*/
struct SquareLookAndFeel : public CustomLookAndFeel
{
void drawButtonBackground (Graphics& g, Button& button, const Colour& backgroundColour,
bool isMouseOverButton, bool isButtonDown) override
{
Colour baseColour (backgroundColour.withMultipliedSaturation (button.hasKeyboardFocus (true) ? 1.3f : 0.9f)
.withMultipliedAlpha (button.isEnabled() ? 0.9f : 0.5f));
if (isButtonDown || isMouseOverButton)
baseColour = baseColour.contrasting (isButtonDown ? 0.2f : 0.1f);
const float width = button.getWidth() - 1.0f;
const float height = button.getHeight() - 1.0f;
if (width > 0 && height > 0)
{
g.setGradientFill (ColourGradient (baseColour, 0.0f, 0.0f,
baseColour.darker (0.1f), 0.0f, height,
false));
g.fillRect (button.getLocalBounds());
}
}
void drawTickBox (Graphics& g, Component& component,
float x, float y, float w, float h,
bool ticked,
bool isEnabled,
bool /*isMouseOverButton*/,
bool /*isButtonDown*/) override
{
const float boxSize = w * 0.7f;
bool isDownOrDragging = component.isEnabled() && (component.isMouseOverOrDragging() || component.isMouseButtonDown());
const Colour colour (component.findColour (TextButton::buttonOnColourId).withMultipliedSaturation ((component.hasKeyboardFocus (false) || isDownOrDragging) ? 1.3f : 0.9f)
.withMultipliedAlpha (component.isEnabled() ? 1.0f : 0.7f));
g.setColour (colour);
Rectangle<float> r (x, y + (h - boxSize) * 0.5f, boxSize, boxSize);
g.fillRect (r);
if (ticked)
{
const Path tick (LookAndFeel_V3::getTickShape (6.0f));
g.setColour (isEnabled ? findColour (TextButton::buttonColourId) : Colours::grey);
const AffineTransform trans (RectanglePlacement (RectanglePlacement::centred)
.getTransformToFit (tick.getBounds(), r.reduced (r.getHeight() * 0.05f)));
g.fillPath (tick, trans);
}
}
void drawLinearSliderThumb (Graphics& g, int x, int y, int width, int height,
float sliderPos, float minSliderPos, float maxSliderPos,
const Slider::SliderStyle style, Slider& slider) override
{
const float sliderRadius = (float) getSliderThumbRadius (slider);
bool isDownOrDragging = slider.isEnabled() && (slider.isMouseOverOrDragging() || slider.isMouseButtonDown());
Colour knobColour (slider.findColour (Slider::rotarySliderFillColourId).withMultipliedSaturation ((slider.hasKeyboardFocus (false) || isDownOrDragging) ? 1.3f : 0.9f)
.withMultipliedAlpha (slider.isEnabled() ? 1.0f : 0.7f));
g.setColour (knobColour);
if (style == Slider::LinearHorizontal || style == Slider::LinearVertical)
{
float kx, ky;
if (style == Slider::LinearVertical)
{
kx = x + width * 0.5f;
ky = sliderPos;
g.fillRect (Rectangle<float> (kx - sliderRadius, ky - 2.5f, sliderRadius * 2.0f, 5.0f));
}
else
{
kx = sliderPos;
ky = y + height * 0.5f;
g.fillRect (Rectangle<float> (kx - 2.5f, ky - sliderRadius, 5.0f, sliderRadius * 2.0f));
}
}
else
{
// Just call the base class for the demo
LookAndFeel_V2::drawLinearSliderThumb (g, x, y, width, height, sliderPos, minSliderPos, maxSliderPos, style, slider);
}
}
void drawRotarySlider (Graphics& g, int x, int y, int width, int height, float sliderPos,
float rotaryStartAngle, float rotaryEndAngle, Slider& slider) override
{
const float diameter = jmin (width, height) - 4.0f;
const float radius = (diameter / 2.0f) * std::cos (float_Pi / 4.0f);
const float centreX = x + width * 0.5f;
const float centreY = y + height * 0.5f;
const float rx = centreX - radius;
const float ry = centreY - radius;
const float rw = radius * 2.0f;
const float angle = rotaryStartAngle + sliderPos * (rotaryEndAngle - rotaryStartAngle);
const bool isMouseOver = slider.isMouseOverOrDragging() && slider.isEnabled();
const Colour baseColour (slider.isEnabled() ? slider.findColour (Slider::rotarySliderFillColourId).withAlpha (isMouseOver ? 0.8f : 1.0f)
: Colour (0x80808080));
Rectangle<float> r (rx, ry, rw, rw);
AffineTransform t (AffineTransform::rotation (angle, r.getCentreX(), r.getCentreY()));
float x1 = r.getTopLeft().getX(), y1 = r.getTopLeft().getY(), x2 = r.getBottomLeft().getX(), y2 = r.getBottomLeft().getY();
t.transformPoints (x1, y1, x2, y2);
g.setGradientFill (ColourGradient (baseColour, x1, y1,
baseColour.darker (0.1f), x2, y2,
false));
Path knob;
knob.addRectangle (r);
g.fillPath (knob, t);
Path needle;
Rectangle<float> r2 (r * 0.1f);
needle.addRectangle (r2.withPosition (Point<float> (r.getCentreX() - (r2.getWidth() / 2.0f), r.getY())));
g.setColour (slider.findColour (Slider::rotarySliderOutlineColourId));
g.fillPath (needle, AffineTransform::rotation (angle, r.getCentreX(), r.getCentreY()));
}
};
//==============================================================================
struct LookAndFeelDemoComponent : public Component
{
LookAndFeelDemoComponent()
{
addAndMakeVisible (rotarySlider);
rotarySlider.setSliderStyle (Slider::RotaryHorizontalVerticalDrag);
rotarySlider.setTextBoxStyle (Slider::NoTextBox, false, 0, 0);
rotarySlider.setValue (2.5);
addAndMakeVisible (verticalSlider);
verticalSlider.setSliderStyle (Slider::LinearVertical);
verticalSlider.setTextBoxStyle (Slider::NoTextBox, false, 90, 20);
verticalSlider.setValue (6.2);
addAndMakeVisible (barSlider);
barSlider.setSliderStyle (Slider::LinearBar);
barSlider.setValue (4.5);
addAndMakeVisible (incDecSlider);
incDecSlider.setSliderStyle (Slider::IncDecButtons);
incDecSlider.setRange (0.0, 10.0, 1.0);
incDecSlider.setIncDecButtonsMode (Slider::incDecButtonsDraggable_Horizontal);
incDecSlider.setTextBoxStyle (Slider::TextBoxBelow, false, 90, 20);
addAndMakeVisible (button1);
button1.setButtonText ("Hello World!");
addAndMakeVisible (button2);
button2.setButtonText ("Hello World!");
button2.setClickingTogglesState (true);
button2.setToggleState (true, dontSendNotification);
addAndMakeVisible (button3);
button3.setButtonText ("Hello World!");
addAndMakeVisible (button4);
button4.setButtonText ("Toggle Me");
button4.setToggleState (true, dontSendNotification);
for (int i = 0; i < 3; ++i)
{
TextButton* b = radioButtons.add (new TextButton());
addAndMakeVisible (b);
b->setRadioGroupId (42);
b->setClickingTogglesState (true);
b->setButtonText ("Button " + String (i + 1));
switch (i)
{
case 0: b->setConnectedEdges (Button::ConnectedOnRight); break;
case 1: b->setConnectedEdges (Button::ConnectedOnRight + Button::ConnectedOnLeft); break;
case 2: b->setConnectedEdges (Button::ConnectedOnLeft); break;
default: break;
}
}
radioButtons.getUnchecked (2)->setToggleState (true, dontSendNotification);
}
void resized() override
{
Rectangle<int> area (getLocalBounds().reduced (10));
Rectangle<int> row (area.removeFromTop (100));
rotarySlider.setBounds (row.removeFromLeft (100).reduced (5));
verticalSlider.setBounds (row.removeFromLeft (100).reduced (5));
barSlider.setBounds (row.removeFromLeft (100).reduced (5, 25));
incDecSlider.setBounds (row.removeFromLeft (100).reduced (5, 28));
row = area.removeFromTop (100);
button1.setBounds (row.removeFromLeft (100).reduced (5));
Rectangle<int> row2 (row.removeFromTop (row.getHeight() / 2).reduced (0, 10));
button2.setBounds (row2.removeFromLeft (100).reduced (5, 0));
button3.setBounds (row2.removeFromLeft (100).reduced (5, 0));
button4.setBounds (row2.removeFromLeft (100).reduced (5, 0));
row2 = (row.removeFromTop (row2.getHeight() + 20).reduced (5, 10));
for (int i = 0; i < radioButtons.size(); ++i)
radioButtons.getUnchecked (i)->setBounds (row2.removeFromLeft (100));
}
Slider rotarySlider, verticalSlider, barSlider, incDecSlider;
TextButton button1, button2, button3;
ToggleButton button4;
OwnedArray<TextButton> radioButtons;
};
//==============================================================================
class LookAndFeelDemo : public Component,
private ComboBox::Listener,
private Button::Listener
{
public:
LookAndFeelDemo()
{
descriptionLabel.setMinimumHorizontalScale (1.0f);
descriptionLabel.setText ("This demonstrates how to create a custom look and feel by overriding only the desired methods.\n\n"
"Components can have their look and feel individually assigned or they will inherit it from their parent. "
"Colours work in a similar way, they can be set for individual components or a look and feel as a whole.",
dontSendNotification);
addAndMakeVisible (descriptionLabel);
addAndMakeVisible (lafBox);
addAndMakeVisible (demoComp);
addLookAndFeel (new LookAndFeel_V1(), "LookAndFeel_V1");
addLookAndFeel (new LookAndFeel_V2(), "LookAndFeel_V2");
addLookAndFeel (new LookAndFeel_V3(), "LookAndFeel_V3");
CustomLookAndFeel* claf = new CustomLookAndFeel();
addLookAndFeel (claf, "Custom Look And Feel");
setupCustomLookAndFeelColours (*claf);
SquareLookAndFeel* slaf = new SquareLookAndFeel();
addLookAndFeel (slaf, "Square Look And Feel");
setupSquareLookAndFeelColours (*slaf);
lafBox.addListener (this);
lafBox.setSelectedItemIndex (3);
addAndMakeVisible (randomButton);
randomButton.setButtonText ("Assign Randomly");
randomButton.addListener (this);
}
void paint (Graphics& g) override
{
g.fillAll (Colour::greyLevel (0.4f));
}
void resized() override
{
Rectangle<int> r (getLocalBounds().reduced (10));
demoComp.setBounds (r);
descriptionLabel.setBounds (r.removeFromTop (200));
lafBox.setBounds (r.removeFromTop (22).removeFromLeft (250));
randomButton.setBounds (lafBox.getBounds().withX (lafBox.getRight() + 20).withWidth (140));
demoComp.setBounds (r.withTrimmedTop (10));
}
private:
Label descriptionLabel;
ComboBox lafBox;
TextButton randomButton;
OwnedArray<LookAndFeel> lookAndFeels;
LookAndFeelDemoComponent demoComp;
void addLookAndFeel (LookAndFeel* laf, const String& name)
{
lookAndFeels.add (laf);
lafBox.addItem (name, lafBox.getNumItems() + 1);
}
void setupCustomLookAndFeelColours (LookAndFeel& laf)
{
laf.setColour (Slider::thumbColourId, Colour::greyLevel (0.95f));
laf.setColour (Slider::textBoxOutlineColourId, Colours::transparentWhite);
laf.setColour (Slider::rotarySliderFillColourId, Colour (0xff00b5f6));
laf.setColour (Slider::rotarySliderOutlineColourId, Colours::white);
laf.setColour (TextButton::buttonColourId, Colours::white);
laf.setColour (TextButton::textColourOffId, Colour (0xff00b5f6));
laf.setColour (TextButton::buttonOnColourId, laf.findColour (TextButton::textColourOffId));
laf.setColour (TextButton::textColourOnId, laf.findColour (TextButton::buttonColourId));
}
void setupSquareLookAndFeelColours (LookAndFeel& laf)
{
const Colour baseColour (Colours::red);
laf.setColour (Slider::thumbColourId, Colour::greyLevel (0.95f));
laf.setColour (Slider::textBoxOutlineColourId, Colours::transparentWhite);
laf.setColour (Slider::rotarySliderFillColourId, baseColour);
laf.setColour (Slider::rotarySliderOutlineColourId, Colours::white);
laf.setColour (Slider::trackColourId, Colours::black);
laf.setColour (TextButton::buttonColourId, Colours::white);
laf.setColour (TextButton::textColourOffId, baseColour);
laf.setColour (TextButton::buttonOnColourId, laf.findColour (TextButton::textColourOffId));
laf.setColour (TextButton::textColourOnId, laf.findColour (TextButton::buttonColourId));
}
void setAllLookAndFeels (LookAndFeel* laf)
{
for (int i = 0; i < demoComp.getNumChildComponents(); ++i)
if (Component* c = demoComp.getChildComponent (i))
c->setLookAndFeel (laf);
}
void comboBoxChanged (ComboBox* comboBoxThatHasChanged) override
{
if (comboBoxThatHasChanged == &lafBox)
setAllLookAndFeels (lookAndFeels[lafBox.getSelectedItemIndex()]);
}
void buttonClicked (Button* b) override
{
if (b == &randomButton)
lafBox.setSelectedItemIndex (Random::getSystemRandom().nextInt (lafBox.getNumItems()));
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LookAndFeelDemo);
};
// This static object will register this demo type in a global list of demos..
static JuceDemoType<LookAndFeelDemo> demo ("10 Components: Look And Feel");
@@ -0,0 +1,271 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-12 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../JuceDemoHeader.h"
//==============================================================================
/** The Note class contains text editor used to display and edit the note's contents and will
also listen to changes in the text and mark the FileBasedDocument as 'dirty'. This 'dirty'
flag is used to promt the user to save the note when it is closed.
*/
class Note : public Component,
public FileBasedDocument,
private TextEditor::Listener
{
public:
Note (const String& name, const String& contents)
: FileBasedDocument (".jnote", "*.jnote",
"Browse for note to load",
"Choose file to save note to"),
textValueObject (contents)
{
// we need to use an separate Value object as our text source so it doesn't get marked
// as changed immediately
setName (name);
editor.setMultiLine (true);
editor.setReturnKeyStartsNewLine (true);
editor.getTextValue().referTo (textValueObject);
addAndMakeVisible (editor);
editor.addListener (this);
}
~Note()
{
editor.removeListener (this);
}
void resized() override
{
editor.setBounds (getLocalBounds());
}
String getDocumentTitle() override
{
return getName();
}
Result loadDocument (const File& file) override
{
editor.setText (file.loadFileAsString());
return Result::ok();
}
Result saveDocument (const File& file) override
{
// attempt to save the contents into the given file
FileOutputStream os (file);
if (os.openedOk())
os.writeText (editor.getText(), false, false);
return Result::ok();
}
File getLastDocumentOpened() override
{
// not interested in this for now
return File::nonexistent;
}
void setLastDocumentOpened (const File& /*file*/) override
{
// not interested in this for now
}
File getSuggestedSaveAsFile (const File&)
{
return File::getSpecialLocation (File::userDesktopDirectory).getChildFile (getName()).withFileExtension ("jnote");
}
private:
Value textValueObject;
TextEditor editor;
void textEditorTextChanged (TextEditor& ed) override
{
// let our FileBasedDocument know we've changed
if (&ed == &editor)
changed();
}
void textEditorReturnKeyPressed (TextEditor&) override {}
void textEditorEscapeKeyPressed (TextEditor&) override {}
void textEditorFocusLost (TextEditor&) override {}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Note);
};
//==============================================================================
/** Simple MultiDocumentPanel that just tries to save our notes when they are closed.
*/
class DemoMultiDocumentPanel : public MultiDocumentPanel
{
public:
DemoMultiDocumentPanel()
{
}
~DemoMultiDocumentPanel()
{
closeAllDocuments (true);
}
bool tryToCloseDocument (Component* component) override
{
#if JUCE_MODAL_LOOPS_PERMITTED
if (Note* note = dynamic_cast<Note*> (component))
return note->saveIfNeededAndUserAgrees() != FileBasedDocument::failedToWriteToFile;
#endif
return true;
}
private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DemoMultiDocumentPanel);
};
//==============================================================================
/** Simple multi-document panel that manages a number of notes that you can store to files.
By default this will look for notes saved to the desktop and load them up.
*/
class MDIDemo : public Component,
public FileDragAndDropTarget,
private Button::Listener
{
public:
MDIDemo()
{
setOpaque (true);
showInTabsButton.setButtonText ("Show with tabs");
showInTabsButton.setToggleState (false, dontSendNotification);
showInTabsButton.addListener (this);
addAndMakeVisible (showInTabsButton);
addNoteButton.setButtonText ("Create a new note");
addNoteButton.addListener (this);
addAndMakeVisible (addNoteButton);
addAndMakeVisible (multiDocumentPanel);
multiDocumentPanel.setBackgroundColour (Colours::transparentBlack);
updateLayoutMode();
addNote ("Notes Demo", "You can drag-and-drop text files onto this page to open them as notes..");
addExistingNotes();
}
~MDIDemo()
{
addNoteButton.removeListener (this);
showInTabsButton.removeListener (this);
}
void paint (Graphics& g) override
{
fillTiledBackground (g);
}
void resized() override
{
Rectangle<int> area (getLocalBounds());
Rectangle<int> buttonArea (area.removeFromTop (28).reduced (2));
addNoteButton.setBounds (buttonArea.removeFromRight (150));
showInTabsButton.setBounds (buttonArea);
multiDocumentPanel.setBounds (area);
}
bool isInterestedInFileDrag (const StringArray&) override
{
return true;
}
void filesDropped (const StringArray& filenames, int /* x */, int /* y */) override
{
Array<File> files;
for (int i = 0; i < filenames.size(); ++i)
files.add (File (filenames[i]));
createNotesForFiles (files);
}
void createNotesForFiles (const Array<File>& files)
{
for (int i = 0; i < files.size(); ++i)
{
const File file (files[i]);
String content = file.loadFileAsString();
if (content.length() > 20000)
content = "Too long!";
addNote (file.getFileName(), content);
}
}
private:
ToggleButton showInTabsButton;
TextButton addNoteButton;
DemoMultiDocumentPanel multiDocumentPanel;
void updateLayoutMode()
{
multiDocumentPanel.setLayoutMode (showInTabsButton.getToggleState() ? MultiDocumentPanel::MaximisedWindowsWithTabs
: MultiDocumentPanel::FloatingWindows);
}
void addNote (const String& name, const String& content)
{
Note* newNote = new Note (name, content);
newNote->setSize (200, 200);
multiDocumentPanel.addDocument (newNote, Colours::lightblue.withAlpha (0.6f), true);
}
void addExistingNotes()
{
Array<File> files;
File::getSpecialLocation (File::userDesktopDirectory).findChildFiles (files, File::findFiles, false, "*.jnote");
createNotesForFiles (files);
}
void buttonClicked (Button* b) override
{
if (b == &showInTabsButton)
updateLayoutMode();
else if (b == &addNoteButton)
addNote (String ("Note ") + String (multiDocumentPanel.getNumDocuments() + 1), "Hello World!");
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MDIDemo);
};
// This static object will register this demo type in a global list of demos..
static JuceDemoType<MDIDemo> demo ("10 Components: MDI");
@@ -0,0 +1,268 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-12 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../JuceDemoHeader.h"
static String getMidiMessageDescription (const MidiMessage& m)
{
if (m.isNoteOn()) return "Note on " + MidiMessage::getMidiNoteName (m.getNoteNumber(), true, true, 3);
if (m.isNoteOff()) return "Note off " + MidiMessage::getMidiNoteName (m.getNoteNumber(), true, true, 3);
if (m.isProgramChange()) return "Program change " + String (m.getProgramChangeNumber());
if (m.isPitchWheel()) return "Pitch wheel " + String (m.getPitchWheelValue());
if (m.isAftertouch()) return "After touch " + MidiMessage::getMidiNoteName (m.getNoteNumber(), true, true, 3) + ": " + String (m.getAfterTouchValue());
if (m.isChannelPressure()) return "Channel pressure " + String (m.getChannelPressureValue());
if (m.isAllNotesOff()) return "All notes off";
if (m.isAllSoundOff()) return "All sound off";
if (m.isMetaEvent()) return "Meta event";
if (m.isController())
{
String name (MidiMessage::getControllerName (m.getControllerNumber()));
if (name.isEmpty())
name = "[" + String (m.getControllerNumber()) + "]";
return "Controler " + name + ": " + String (m.getControllerValue());
}
return String::toHexString (m.getRawData(), m.getRawDataSize());
}
//==============================================================================
/** Simple list box that just displays a StringArray. */
class MidiLogListBoxModel : public ListBoxModel
{
public:
MidiLogListBoxModel (const Array<MidiMessage>& list)
: midiMessageList (list)
{
}
int getNumRows() override { return midiMessageList.size(); }
void paintListBoxItem (int row, Graphics& g, int width, int height, bool rowIsSelected) override
{
if (rowIsSelected)
g.fillAll (Colours::blue.withAlpha (0.2f));
if (isPositiveAndBelow (row, midiMessageList.size()))
{
g.setColour (Colours::black);
const MidiMessage& message = midiMessageList.getReference (row);
double time = message.getTimeStamp();
g.drawText (String::formatted ("%02d:%02d:%02d",
((int) (time / 3600.0)) % 24,
((int) (time / 60.0)) % 60,
((int) time) % 60)
+ " - " + getMidiMessageDescription (message),
Rectangle<int> (width, height).reduced (4, 0),
Justification::centredLeft, true);
}
}
private:
const Array<MidiMessage>& midiMessageList;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiLogListBoxModel)
};
//==============================================================================
class MidiDemo : public Component,
private ComboBox::Listener,
private MidiInputCallback,
private MidiKeyboardStateListener,
private AsyncUpdater
{
public:
MidiDemo()
: deviceManager (MainAppWindow::getSharedAudioDeviceManager()),
lastInputIndex (0), isAddingFromMidiInput (false),
keyboardComponent (keyboardState, MidiKeyboardComponent::horizontalKeyboard),
midiLogListBoxModel (midiMessageList)
{
setOpaque (true);
addAndMakeVisible (midiInputListLabel);
midiInputListLabel.setText ("MIDI Input:", dontSendNotification);
midiInputListLabel.attachToComponent (&midiInputList, true);
addAndMakeVisible (midiInputList);
midiInputList.setTextWhenNoChoicesAvailable ("No MIDI Inputs Enabled");
const StringArray midiInputs (MidiInput::getDevices());
midiInputList.addItemList (midiInputs, 1);
midiInputList.addListener (this);
// find the first enabled device and use that bu default
for (int i = 0; i < midiInputs.size(); ++i)
{
if (deviceManager.isMidiInputEnabled (midiInputs[i]))
{
setMidiInput (i);
break;
}
}
// if no enabled devices were found just use the first one in the list
if (midiInputList.getSelectedId() == 0)
setMidiInput (0);
addAndMakeVisible (keyboardComponent);
keyboardState.addListener (this);
addAndMakeVisible (messageListBox);
messageListBox.setModel (&midiLogListBoxModel);
messageListBox.setColour (ListBox::backgroundColourId, Colour (0x32ffffff));
messageListBox.setColour (ListBox::outlineColourId, Colours::black);
}
~MidiDemo()
{
keyboardState.removeListener (this);
deviceManager.removeMidiInputCallback (MidiInput::getDevices()[midiInputList.getSelectedItemIndex()], this);
midiInputList.removeListener (this);
}
void paint (Graphics& g) override
{
fillTiledBackground (g);
}
void resized() override
{
Rectangle<int> area (getLocalBounds());
midiInputList.setBounds (area.removeFromTop (36).removeFromRight (getWidth() - 150).reduced (8));
keyboardComponent.setBounds (area.removeFromTop (80).reduced(8));
messageListBox.setBounds (area.reduced (8));
}
private:
AudioDeviceManager& deviceManager;
ComboBox midiInputList;
Label midiInputListLabel;
int lastInputIndex;
bool isAddingFromMidiInput;
MidiKeyboardState keyboardState;
MidiKeyboardComponent keyboardComponent;
ListBox messageListBox;
Array<MidiMessage> midiMessageList;
MidiLogListBoxModel midiLogListBoxModel;
//==============================================================================
/** Starts listening to a MIDI input device, enabling it if necessary. */
void setMidiInput (int index)
{
const StringArray list (MidiInput::getDevices());
deviceManager.removeMidiInputCallback (list[lastInputIndex], this);
const String newInput (list[index]);
if (! deviceManager.isMidiInputEnabled (newInput))
deviceManager.setMidiInputEnabled (newInput, true);
deviceManager.addMidiInputCallback (newInput, this);
midiInputList.setSelectedId (index + 1, dontSendNotification);
lastInputIndex = index;
}
void comboBoxChanged (ComboBox* box) override
{
if (box == &midiInputList)
setMidiInput (midiInputList.getSelectedItemIndex());
}
// These methods handle callbacks from the midi device + on-screen keyboard..
void handleIncomingMidiMessage (MidiInput*, const MidiMessage& message) override
{
const ScopedValueSetter<bool> scopedInputFlag (isAddingFromMidiInput, true);
keyboardState.processNextMidiEvent (message);
postMessageToList (message);
}
void handleNoteOn (MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity) override
{
if (! isAddingFromMidiInput)
{
MidiMessage m (MidiMessage::noteOn (midiChannel, midiNoteNumber, velocity));
m.setTimeStamp (Time::getMillisecondCounterHiRes() * 0.001);
postMessageToList (m);
}
}
void handleNoteOff (MidiKeyboardState*, int midiChannel, int midiNoteNumber) override
{
if (! isAddingFromMidiInput)
{
MidiMessage m (MidiMessage::noteOff (midiChannel, midiNoteNumber));
m.setTimeStamp (Time::getMillisecondCounterHiRes() * 0.001);
postMessageToList (m);
}
}
// This is used to dispach an incoming message to the message thread
struct IncomingMessageCallback : public CallbackMessage
{
IncomingMessageCallback (MidiDemo* d, const MidiMessage& m)
: demo (d), message (m) {}
void messageCallback() override
{
if (demo != nullptr)
demo->addMessageToList (message);
}
Component::SafePointer<MidiDemo> demo;
MidiMessage message;
};
void postMessageToList (const MidiMessage& message)
{
(new IncomingMessageCallback (this, message))->post();
}
void addMessageToList (const MidiMessage& message)
{
midiMessageList.add (message);
triggerAsyncUpdate();
}
void handleAsyncUpdate() override
{
messageListBox.updateContent();
messageListBox.scrollToEnsureRowIsOnscreen (midiMessageList.size() - 1);
messageListBox.repaint();
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiDemo);
};
// This static object will register this demo type in a global list of demos..
static JuceDemoType<MidiDemo> demo ("32 Audio: MIDI i/o");
@@ -0,0 +1,153 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-12 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../JuceDemoHeader.h"
//==============================================================================
class MultiTouchDemo : public Component
{
public:
MultiTouchDemo()
{
setOpaque (true);
}
void paint (Graphics& g) override
{
g.fillAll (Colour::greyLevel (0.4f));
g.setColour (Colours::lightgrey);
g.setFont (14.0f);
g.drawFittedText ("Drag here with as many fingers as you have!",
getLocalBounds().reduced (30), Justification::centred, 4);
for (int i = 0; i < trails.size(); ++i)
drawTrail (*trails.getUnchecked(i), g);
}
void mouseDrag (const MouseEvent& e) override
{
Trail* t = getTrail (e.source);
if (t == nullptr)
{
t = new Trail (e.source);
t->path.startNewSubPath (e.position);
trails.add (t);
}
t->pushPoint (e.position, e.mods);
repaint();
}
void mouseUp (const MouseEvent& e) override
{
trails.removeObject (getTrail (e.source));
repaint();
}
struct Trail
{
Trail (const MouseInputSource& ms)
: source (ms), colour (getRandomBrightColour().withAlpha (0.6f))
{}
void pushPoint (Point<float> p, ModifierKeys newMods)
{
currentPosition = p;
modifierKeys = newMods;
if (lastPoint.getDistanceFrom(p) > 5.0f)
{
if (lastPoint != Point<float>())
{
path.quadraticTo (lastPoint, p);
lastPoint = Point<float>();
}
else
{
lastPoint = p;
}
}
}
MouseInputSource source;
Path path;
Colour colour;
Point<float> lastPoint, currentPosition;
ModifierKeys modifierKeys;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Trail)
};
OwnedArray<Trail> trails;
void drawTrail (Trail& trail, Graphics& g)
{
g.setColour (trail.colour);
g.strokePath (trail.path, PathStrokeType (20.0f, PathStrokeType::curved, PathStrokeType::rounded));
const float radius = 40.0f;
g.setColour (Colours::black);
g.drawEllipse (trail.currentPosition.x - radius,
trail.currentPosition.y - radius,
radius * 2.0f, radius * 2.0f, 2.0f);
g.setFont (14.0f);
String desc ("Mouse #");
desc << trail.source.getIndex();
if (trail.modifierKeys.isCommandDown()) desc << " (CMD)";
if (trail.modifierKeys.isShiftDown()) desc << " (SHIFT)";
if (trail.modifierKeys.isCtrlDown()) desc << " (CTRL)";
if (trail.modifierKeys.isAltDown()) desc << " (ALT)";
g.drawText (desc,
Rectangle<int> ((int) trail.currentPosition.x - 200,
(int) trail.currentPosition.y - 60,
400, 20),
Justification::centredTop, false);
}
Trail* getTrail (const MouseInputSource& source)
{
for (int i = 0; i < trails.size(); ++i)
{
Trail* t = trails.getUnchecked(i);
if (t->source == source)
return t;
}
return nullptr;
}
};
// This static object will register this demo type in a global list of demos..
static JuceDemoType<MultiTouchDemo> demo ("10 Components: Multi-touch");
@@ -0,0 +1,341 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-12 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../JuceDemoHeader.h"
//==============================================================================
class BouncingBallComp : public Component
{
public:
BouncingBallComp()
{
x = Random::getSystemRandom().nextFloat() * 200.0f;
y = Random::getSystemRandom().nextFloat() * 200.0f;
parentWidth = 50;
parentHeight = 50;
innerX = 0;
innerY = 0;
threadId = 0;
const float speed = 5.0f; // give each ball a fixed speed so we can
// see the effects of thread priority on how fast
// they actually go.
const float angle = Random::getSystemRandom().nextFloat() * float_Pi * 2.0f;
dx = std::sin (angle) * speed;
dy = std::cos (angle) * speed;
size = Random::getSystemRandom().nextFloat() * 30.0f + 30.0f;
colour = Colour ((juce::uint32) Random::getSystemRandom().nextInt())
.withAlpha (0.5f)
.withBrightness (0.7f);
}
void paint (Graphics& g) override
{
g.setColour (colour);
g.fillEllipse (innerX, innerY, size, size);
g.setColour (Colours::black);
g.setFont (10.0f);
g.drawText (String::toHexString ((int64) threadId), getLocalBounds(), Justification::centred, false);
}
void parentSizeChanged() override
{
parentWidth = getParentWidth() - size;
parentHeight = getParentHeight() - size;
}
void moveBall()
{
threadId = Thread::getCurrentThreadId(); // this is so the component can print the thread ID inside the ball
x += dx;
y += dy;
if (x < 0)
dx = std::abs (dx);
if (x > parentWidth)
dx = -std::abs (dx);
if (y < 0)
dy = std::abs (dy);
if (y > parentHeight)
dy = -std::abs (dy);
setBounds (((int) x) - 2,
((int) y) - 2,
((int) size) + 4,
((int) size) + 4);
innerX = x - getX();
innerY = y - getY();
repaint();
}
private:
float x, y, size, dx, dy, parentWidth, parentHeight;
float innerX, innerY;
Colour colour;
Thread::ThreadID threadId;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BouncingBallComp)
};
//==============================================================================
class DemoThread : public BouncingBallComp,
public Thread
{
public:
DemoThread()
: Thread ("Juce Demo Thread")
{
interval = Random::getSystemRandom().nextInt (50) + 6;
// give the threads a random priority, so some will move more
// smoothly than others..
startThread (Random::getSystemRandom().nextInt (3) + 3);
}
~DemoThread()
{
// allow the thread 2 seconds to stop cleanly - should be plenty of time.
stopThread (2000);
}
void run() override
{
// this is the code that runs this thread - we'll loop continuously,
// updating the coordinates of our blob.
// threadShouldExit() returns true when the stopThread() method has been
// called, so we should check it often, and exit as soon as it gets flagged.
while (! threadShouldExit())
{
// sleep a bit so the threads don't all grind the CPU to a halt..
wait (interval);
// because this is a background thread, we mustn't do any UI work without
// first grabbing a MessageManagerLock..
const MessageManagerLock mml (Thread::getCurrentThread());
if (! mml.lockWasGained()) // if something is trying to kill this job, the lock
return; // will fail, in which case we'd better return..
// now we've got the UI thread locked, we can mess about with the components
moveBall();
}
}
private:
int interval;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DemoThread)
};
//==============================================================================
class DemoThreadPoolJob : public BouncingBallComp,
public ThreadPoolJob
{
public:
DemoThreadPoolJob()
: ThreadPoolJob ("Demo Threadpool Job")
{
}
JobStatus runJob() override
{
// this is the code that runs this job. It'll be repeatedly called until we return
// jobHasFinished instead of jobNeedsRunningAgain.
Thread::sleep (30);
// because this is a background thread, we mustn't do any UI work without
// first grabbing a MessageManagerLock..
const MessageManagerLock mml (this);
// before moving the ball, we need to check whether the lock was actually gained, because
// if something is trying to stop this job, it will have failed..
if (mml.lockWasGained())
moveBall();
return jobNeedsRunningAgain;
}
void removedFromQueue()
{
// This is called to tell us that our job has been removed from the pool.
// In this case there's no need to do anything here.
}
private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DemoThreadPoolJob)
};
//==============================================================================
class MultithreadingDemo : public Component,
private Timer,
private Button::Listener
{
public:
MultithreadingDemo()
: pool (3),
controlButton ("Thread type"),
isUsingPool (false)
{
setOpaque (true);
addAndMakeVisible (controlButton);
controlButton.changeWidthToFitText (24);
controlButton.setTopLeftPosition (20, 20);
controlButton.setTriggeredOnMouseDown (true);
controlButton.setAlwaysOnTop (true);
controlButton.addListener (this);
}
~MultithreadingDemo()
{
pool.removeAllJobs (true, 2000);
}
void resetAllBalls()
{
stopTimer();
pool.removeAllJobs (true, 4000);
balls.clear();
if (isShowing())
{
while (balls.size() < 5)
addABall();
startTimer (300);
}
}
void paint (Graphics& g) override
{
fillTiledBackground (g);
}
private:
ThreadPool pool;
TextButton controlButton;
bool isUsingPool;
OwnedArray<Component> balls;
void setUsingPool (bool usePool)
{
isUsingPool = usePool;
resetAllBalls();
}
void addABall()
{
if (isUsingPool)
{
DemoThreadPoolJob* newBall = new DemoThreadPoolJob();
balls.add (newBall);
addAndMakeVisible (newBall);
newBall->parentSizeChanged();
pool.addJob (newBall, false);
}
else
{
DemoThread* newBall = new DemoThread();
balls.add (newBall);
addAndMakeVisible (newBall);
newBall->parentSizeChanged();
}
}
void removeABall()
{
if (balls.size() > 0)
{
int indexToRemove = Random::getSystemRandom().nextInt (balls.size());
if (isUsingPool)
pool.removeJob (dynamic_cast <DemoThreadPoolJob*> (balls [indexToRemove]), true, 4000);
balls.remove (indexToRemove);
}
}
// this gets called when a component is added or removed from a parent component.
void parentHierarchyChanged() override
{
// we'll use this as an opportunity to start and stop the threads, so that
// we don't leave them going when the component's not actually visible.
resetAllBalls();
}
void timerCallback() override
{
if (Random::getSystemRandom().nextBool())
{
if (balls.size() <= 10)
addABall();
}
else
{
if (balls.size() > 3)
removeABall();
}
}
void buttonClicked (Button*) override
{
PopupMenu m;
m.addItem (1, "Use one thread per ball", true, ! isUsingPool);
m.addItem (2, "Use a thread pool", true, isUsingPool);
m.showMenuAsync (PopupMenu::Options().withTargetComponent (&controlButton),
ModalCallbackFunction::forComponent (menuItemChosenCallback, this));
}
static void menuItemChosenCallback (int result, MultithreadingDemo* demoComponent)
{
if (result != 0 && demoComponent != nullptr)
demoComponent->setUsingPool (result == 2);
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MultithreadingDemo);
};
// This static object will register this demo type in a global list of demos..
static JuceDemoType<MultithreadingDemo> demo ("40 Multi-threading");
@@ -0,0 +1,123 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-12 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../JuceDemoHeader.h"
//==============================================================================
class NetworkingDemo : public Component,
private Button::Listener,
private Thread
{
public:
NetworkingDemo()
: Thread ("Network Demo"),
resultsBox (resultsDocument, nullptr)
{
setOpaque (true);
addAndMakeVisible (urlBox);
urlBox.setText ("http://www.google.com");
addAndMakeVisible (fetchButton);
fetchButton.setButtonText ("Download URL Contents");
fetchButton.addListener (this);
addAndMakeVisible (resultsBox);
}
~NetworkingDemo()
{
fetchButton.removeListener (this);
}
void paint (Graphics& g) override
{
fillTiledBackground (g);
}
void resized() override
{
Rectangle<int> area (getLocalBounds());
{
Rectangle<int> topArea (area.removeFromTop (40));
fetchButton.setBounds (topArea.removeFromRight (180).reduced (8));
urlBox.setBounds (topArea.reduced (8));
}
resultsBox.setBounds (area.reduced (8));
}
void run() override
{
String result (getResultText (urlBox.getText()));
MessageManagerLock mml (this);
if (mml.lockWasGained())
resultsBox.loadContent (result);
}
String getResultText (const URL& url)
{
StringPairArray responseHeaders;
int statusCode = 0;
ScopedPointer<InputStream> stream (url.createInputStream (false, nullptr, nullptr, String(),
10000, // timeout in millisecs
&responseHeaders, &statusCode));
if (stream != nullptr)
return (statusCode != 0 ? "Status code: " + String (statusCode) + newLine : String())
+ "Response headers: " + newLine
+ responseHeaders.getDescription() + newLine
+ "----------------------------------------------------" + newLine
+ stream->readEntireStreamAsString();
if (statusCode != 0)
return "Failed to connect, status code = " + String (statusCode);
return "Failed to connect!";
}
private:
TextEditor urlBox;
TextButton fetchButton;
CodeDocument resultsDocument;
CodeEditorComponent resultsBox;
void buttonClicked (Button* button) override
{
if (button == &fetchButton)
startThread();
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NetworkingDemo)
};
// This static object will register this demo type in a global list of demos..
static JuceDemoType<NetworkingDemo> demo ("40 HTTP");
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,249 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-12 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../JuceDemoHeader.h"
#if JUCE_OPENGL
//==============================================================================
class OpenGL2DShaderDemo : public Component,
private CodeDocument::Listener,
private ComboBox::Listener,
private Timer
{
public:
OpenGL2DShaderDemo()
: fragmentEditorComp (fragmentDocument, nullptr)
{
setOpaque (true);
MainAppWindow::getMainAppWindow()->setOpenGLRenderingEngine();
addAndMakeVisible (statusLabel);
statusLabel.setJustificationType (Justification::topLeft);
statusLabel.setColour (Label::textColourId, Colours::black);
statusLabel.setFont (Font (14.0f));
Array<ShaderPreset> presets (getPresets());
StringArray presetNames;
for (int i = 0; i < presets.size(); ++i)
presetBox.addItem (presets[i].name, i + 1);
addAndMakeVisible (presetLabel);
presetLabel.setText ("Shader Preset:", dontSendNotification);
presetLabel.attachToComponent (&presetBox, true);
addAndMakeVisible (presetBox);
presetBox.addListener (this);
Colour editorBackground (Colours::white.withAlpha (0.6f));
fragmentEditorComp.setColour (CodeEditorComponent::backgroundColourId, editorBackground);
fragmentEditorComp.setOpaque (false);
fragmentDocument.addListener (this);
addAndMakeVisible (fragmentEditorComp);
presetBox.setSelectedItemIndex (0);
}
~OpenGL2DShaderDemo()
{
shader = nullptr;
}
void paint (Graphics& g)
{
g.fillCheckerBoard (getLocalBounds(), 48, 48, Colours::lightgrey, Colours::white);
if (shader == nullptr || shader->getFragmentShaderCode() != fragmentCode)
{
shader = nullptr;
if (fragmentCode.isNotEmpty())
{
shader = new OpenGLGraphicsContextCustomShader (fragmentCode);
Result result (shader->checkCompilation (g.getInternalContext()));
if (result.failed())
{
statusLabel.setText (result.getErrorMessage(), dontSendNotification);
shader = nullptr;
}
}
}
if (shader != nullptr)
{
statusLabel.setText (String::empty, dontSendNotification);
shader->fillRect (g.getInternalContext(), getLocalBounds());
}
}
void resized() override
{
Rectangle<int> area (getLocalBounds().reduced (4));
statusLabel.setBounds (area.removeFromTop (75));
area.removeFromTop (area.getHeight() / 2);
Rectangle<int> presets (area.removeFromTop (25));
presets.removeFromLeft (100);
presetBox.setBounds (presets.removeFromLeft (150));
area.removeFromTop (4);
fragmentEditorComp.setBounds (area);
}
void selectPreset (int preset)
{
fragmentDocument.replaceAllContent (getPresets()[preset].fragmentShader);
startTimer (1);
}
ScopedPointer<OpenGLGraphicsContextCustomShader> shader;
Label statusLabel, presetLabel;
ComboBox presetBox;
CodeDocument fragmentDocument;
CodeEditorComponent fragmentEditorComp;
String fragmentCode;
private:
enum { shaderLinkDelay = 500 };
void codeDocumentTextInserted (const String& /*newText*/, int /*insertIndex*/) override
{
startTimer (shaderLinkDelay);
}
void codeDocumentTextDeleted (int /*startIndex*/, int /*endIndex*/) override
{
startTimer (shaderLinkDelay);
}
void timerCallback() override
{
stopTimer();
fragmentCode = fragmentDocument.getAllContent();
repaint();
}
void comboBoxChanged (ComboBox*) override
{
selectPreset (presetBox.getSelectedItemIndex());
}
struct ShaderPreset
{
const char* name;
const char* fragmentShader;
};
static Array<ShaderPreset> getPresets()
{
#define SHADER_DEMO_HEADER \
"/* This demo shows the use of the OpenGLGraphicsContextCustomShader,\n" \
" which allows a 2D area to be filled using a GL shader program.\n" \
"\n" \
" Edit the shader program below and it will be \n" \
" recompiled in real-time!\n" \
"*/\n\n"
ShaderPreset presets[] =
{
{
"Simple Gradient",
SHADER_DEMO_HEADER
"void main()\n"
"{\n"
" " JUCE_MEDIUMP " vec4 colour1 = vec4 (1.0, 0.4, 0.6, 1.0);\n"
" " JUCE_MEDIUMP " vec4 colour2 = vec4 (0.0, 0.8, 0.6, 1.0);\n"
" " JUCE_MEDIUMP " float alpha = pixelPos.x / 1000.0;\n"
" gl_FragColor = pixelAlpha * mix (colour1, colour2, alpha);\n"
"}\n"
},
{
"Circular Gradient",
SHADER_DEMO_HEADER
"void main()\n"
"{\n"
" " JUCE_MEDIUMP " vec4 colour1 = vec4 (1.0, 0.4, 0.6, 1.0);\n"
" " JUCE_MEDIUMP " vec4 colour2 = vec4 (0.3, 0.4, 0.4, 1.0);\n"
" " JUCE_MEDIUMP " float alpha = distance (pixelPos, vec2 (600.0, 500.0)) / 400.0;\n"
" gl_FragColor = pixelAlpha * mix (colour1, colour2, alpha);\n"
"}\n"
},
{
"Circle",
SHADER_DEMO_HEADER
"void main()\n"
"{\n"
" " JUCE_MEDIUMP " vec4 colour1 = vec4 (0.1, 0.1, 0.9, 1.0);\n"
" " JUCE_MEDIUMP " vec4 colour2 = vec4 (0.0, 0.8, 0.6, 1.0);\n"
" " JUCE_MEDIUMP " float distance = distance (pixelPos, vec2 (600.0, 500.0));\n"
"\n"
" " JUCE_MEDIUMP " float innerRadius = 200.0;\n"
" " JUCE_MEDIUMP " float outerRadius = 210.0;\n"
"\n"
" if (distance < innerRadius)\n"
" gl_FragColor = colour1;\n"
" else if (distance > outerRadius)\n"
" gl_FragColor = colour2;\n"
" else\n"
" gl_FragColor = mix (colour1, colour2, (distance - innerRadius) / (outerRadius - innerRadius));\n"
"\n"
" gl_FragColor *= pixelAlpha;\n"
"}\n"
},
{
"Solid Colour",
SHADER_DEMO_HEADER
"void main()\n"
"{\n"
" gl_FragColor = vec4 (1.0, 0.6, 0.1, pixelAlpha);\n"
"}\n"
}
};
return Array<ShaderPreset> (presets, numElementsInArray (presets));
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGL2DShaderDemo)
};
//==============================================================================
// This static object will register this demo type in a global list of demos..
static JuceDemoType<OpenGL2DShaderDemo> demo ("20 Graphics: OpenGL 2D");
#endif
@@ -0,0 +1,245 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-12 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../JuceDemoHeader.h"
//==============================================================================
class DemoButtonPropertyComponent : public ButtonPropertyComponent
{
public:
DemoButtonPropertyComponent (const String& propertyName)
: ButtonPropertyComponent (propertyName, true),
counter (0)
{
refresh();
}
void buttonClicked() override
{
++counter;
AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, "Action Button Pressed",
"Pressing this type of property component can trigger an action such as showing an alert window!");
refresh();
}
String getButtonText() const override
{
String text ("Button clicked ");
text << counter << " times";
return text;
}
private:
int counter;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DemoButtonPropertyComponent)
};
//==============================================================================
class DemoSliderPropertyComponent : public SliderPropertyComponent
{
public:
DemoSliderPropertyComponent (const String& propertyName)
: SliderPropertyComponent (propertyName, 0.0, 100.0, 0.001)
{
setValue (Random::getSystemRandom().nextDouble() * 42.0);
}
void setValue (double newValue) override
{
slider.setValue (newValue);
}
private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DemoSliderPropertyComponent)
};
//==============================================================================
static Array<PropertyComponent*> createTextEditors()
{
Array<PropertyComponent*> comps;
comps.add (new TextPropertyComponent (Value (var ("This is a single-line Text Property")), "Text 1", 200, false));
comps.add (new TextPropertyComponent (Value (var ("Another one")), "Text 2", 200, false));
comps.add (new TextPropertyComponent (Value (var (
"Lorem ipsum dolor sit amet, cu mei labore admodum facilisi. Iriure iuvaret invenire ea vim, cum quod"
"si intellegat delicatissimi an. Cetero recteque ei eos, his an scripta fastidii placerat. Nec et anc"
"illae nominati corrumpit. Vis dictas audire accumsan ad, elit fabulas saperet mel eu.\n"
"\n"
"Dicam utroque ius ne, eum choro phaedrum eu. Ut mel omnes virtute appareat, semper quodsi labitur in"
" cum. Est aeque eripuit deleniti in, amet ferri recusabo ea nec. Cu persius maiorum corrumpit mei, i"
"n ridens perpetua mea, pri nobis tation inermis an. Vis alii autem cotidieque ut, ius harum salutatu"
"s ut. Mel eu purto veniam dissentias, malis doctus bonorum ne vel, mundi aperiam adversarium cu eum."
" Mei quando graeci te, dolore accusata mei te.")),
"Multi-line text",
1000, true));
return comps;
}
static Array<PropertyComponent*> createSliders (int howMany)
{
Array<PropertyComponent*> comps;
for (int i = 0; i < howMany; ++i)
comps.add (new DemoSliderPropertyComponent ("Slider " + String (i + 1)));
return comps;
}
static Array<PropertyComponent*> createButtons (int howMany)
{
Array<PropertyComponent*> comps;
for (int i = 0; i < howMany; ++i)
comps.add (new DemoButtonPropertyComponent ("Button " + String (i + 1)));
for (int i = 0; i < howMany; ++i)
comps.add (new BooleanPropertyComponent (Value (Random::getSystemRandom().nextBool()), "Toggle " + String (i + 1), "Description of toggleable thing"));
return comps;
}
static Array<PropertyComponent*> createChoices (int howMany)
{
Array<PropertyComponent*> comps;
StringArray choices;
Array<var> choiceVars;
for (int i = 0; i < howMany; ++i)
{
choices.add ("Item " + String (i));
choiceVars.add (i);
}
for (int i = 0; i < howMany; ++i)
comps.add (new ChoicePropertyComponent (Value (Random::getSystemRandom().nextInt (6)), "Choice Property " + String (i + 1), choices, choiceVars));
return comps;
}
//==============================================================================
class PropertiesDemo : public Component
{
public:
PropertiesDemo()
{
setOpaque (true);
addAndMakeVisible (propertyPanel);
propertyPanel.addSection ("Text Editors", createTextEditors());
propertyPanel.addSection ("Sliders", createSliders (3));
propertyPanel.addSection ("Choice Properties", createChoices (6));
propertyPanel.addSection ("Buttons & Toggles", createButtons (3));
}
void paint (Graphics& g) override
{
g.fillAll (Colour::greyLevel (0.8f));
}
void resized() override
{
propertyPanel.setBounds (getLocalBounds().reduced (4));
}
private:
PropertyPanel propertyPanel;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PropertiesDemo);
};
//==============================================================================
class ConcertinaDemo : public Component,
private Timer
{
public:
ConcertinaDemo()
{
setOpaque (true);
addAndMakeVisible (concertinaPanel);
{
PropertyPanel* panel = new PropertyPanel ("Text Editors");
panel->addProperties (createTextEditors());
addPanel (panel);
}
{
PropertyPanel* panel = new PropertyPanel ("Sliders");
panel->addSection ("Section 1", createSliders (4), true);
panel->addSection ("Section 2", createSliders (3), true);
addPanel (panel);
}
{
PropertyPanel* panel = new PropertyPanel ("Choice Properties");
panel->addProperties (createChoices (12));
addPanel (panel);
}
{
PropertyPanel* panel = new PropertyPanel ("Buttons & Toggles");
panel->addProperties (createButtons (6));
addPanel (panel);
}
startTimer (300);
}
void paint (Graphics& g) override
{
g.fillAll (Colour::greyLevel (0.8f));
}
void resized() override
{
concertinaPanel.setBounds (getLocalBounds().reduced (4));
}
void timerCallback() override
{
stopTimer();
concertinaPanel.expandPanelFully (concertinaPanel.getPanel(0), true);
}
private:
ConcertinaPanel concertinaPanel;
void addPanel (PropertyPanel* panel)
{
concertinaPanel.addPanel (-1, panel, true);
concertinaPanel.setMaximumPanelSize (panel, panel->getTotalContentHeight());
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConcertinaDemo);
};
// This static object will register this demo type in a global list of demos..
static JuceDemoType <PropertiesDemo> demo1 ("10 Components: Property Panels");
static JuceDemoType <ConcertinaDemo> demo2 ("10 Components: Concertina Panels");
@@ -0,0 +1,192 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-12 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../JuceDemoHeader.h"
static String getMacAddressList()
{
Array<MACAddress> macAddresses;
MACAddress::findAllAddresses (macAddresses);
String addressList;
for (int i = 0; i < macAddresses.size(); ++i)
addressList << " " << macAddresses[i].toString() << newLine;
return addressList;
}
static String getFileSystemRoots()
{
Array<File> roots;
File::findFileSystemRoots (roots);
StringArray rootList;
for (int i = 0; i < roots.size(); ++i)
rootList.add (roots[i].getFullPathName());
return rootList.joinIntoString (", ");
}
static String getIPAddressList()
{
Array<IPAddress> addresses;
IPAddress::findAllAddresses (addresses);
String addressList;
for (int i = 0; i < addresses.size(); ++i)
addressList << " " << addresses[i].toString() << newLine;
return addressList;
}
static const char* getDisplayOrientation()
{
switch (Desktop::getInstance().getCurrentOrientation())
{
case Desktop::upright: return "Upright";
case Desktop::upsideDown: return "Upside-down";
case Desktop::rotatedClockwise: return "Rotated Clockwise";
case Desktop::rotatedAntiClockwise: return "Rotated Anti-clockwise";
default: jassertfalse; break;
}
return nullptr;
}
static String getDisplayInfo()
{
const Desktop::Displays& displays = Desktop::getInstance().getDisplays();
String displayDesc;
for (int i = 0; i < displays.displays.size(); ++i)
{
const Desktop::Displays::Display display = displays.displays.getReference(i);
displayDesc
<< "Display " << (i + 1) << (display.isMain ? " (main)" : "") << ":" << newLine
<< " Total area: " << display.totalArea.toString() << newLine
<< " User area: " << display.userArea.toString() << newLine
<< " DPI: " << display.dpi << newLine
<< " Scale: " << display.scale << newLine
<< newLine;
}
displayDesc << "Orientation: " << getDisplayOrientation() << newLine;
return displayDesc;
}
static String getAllSystemInfo()
{
String systemInfo;
systemInfo
<< "Here are a few system statistics..." << newLine
<< newLine
<< "Time and date: " << Time::getCurrentTime().toString (true, true) << newLine
<< "System up-time: " << RelativeTime::milliseconds ((int64) Time::getMillisecondCounterHiRes()).getDescription() << newLine
<< "Compilation date: " << Time::getCompilationDate().toString (true, false) << newLine
<< newLine
<< "Operating system: " << SystemStats::getOperatingSystemName() << newLine
<< "Host name: " << SystemStats::getComputerName() << newLine
<< "Device type: " << SystemStats::getDeviceDescription() << newLine
<< "User logon name: " << SystemStats::getLogonName() << newLine
<< "Full user name: " << SystemStats::getFullUserName() << newLine
<< "User region: " << SystemStats::getUserRegion() << newLine
<< "User language: " << SystemStats::getUserLanguage() << newLine
<< "Display language: " << SystemStats::getDisplayLanguage() << newLine
<< newLine
<< "Number of CPUs: " << SystemStats::getNumCpus() << newLine
<< "Memory size: " << SystemStats::getMemorySizeInMegabytes() << " MB" << newLine
<< "CPU vendor: " << SystemStats::getCpuVendor() << newLine
<< "CPU speed: " << SystemStats::getCpuSpeedInMegaherz() << " MHz" << newLine
<< "CPU has MMX: " << (SystemStats::hasMMX() ? "yes" : "no") << newLine
<< "CPU has SSE: " << (SystemStats::hasSSE() ? "yes" : "no") << newLine
<< "CPU has SSE2: " << (SystemStats::hasSSE2() ? "yes" : "no") << newLine
<< "CPU has SSE3: " << (SystemStats::hasSSE3() ? "yes" : "no") << newLine
<< "CPU has 3DNOW: " << (SystemStats::has3DNow() ? "yes" : "no") << newLine
<< newLine
<< "Current working directory: " << File::getCurrentWorkingDirectory().getFullPathName() << newLine
<< "Current application file: " << File::getSpecialLocation (File::currentApplicationFile).getFullPathName() << newLine
<< "Current executable file: " << File::getSpecialLocation (File::currentExecutableFile) .getFullPathName() << newLine
<< "Invoked executable file: " << File::getSpecialLocation (File::invokedExecutableFile) .getFullPathName() << newLine
<< newLine
<< "User home folder: " << File::getSpecialLocation (File::userHomeDirectory) .getFullPathName() << newLine
<< "User desktop folder: " << File::getSpecialLocation (File::userDesktopDirectory) .getFullPathName() << newLine
<< "User documents folder: " << File::getSpecialLocation (File::userDocumentsDirectory) .getFullPathName() << newLine
<< "User application data folder: " << File::getSpecialLocation (File::userApplicationDataDirectory) .getFullPathName() << newLine
<< "User music folder: " << File::getSpecialLocation (File::userMusicDirectory) .getFullPathName() << newLine
<< "User movies folder: " << File::getSpecialLocation (File::userMoviesDirectory) .getFullPathName() << newLine
<< "User pictures folder: " << File::getSpecialLocation (File::userPicturesDirectory) .getFullPathName() << newLine
<< "Common application data folder: " << File::getSpecialLocation (File::commonApplicationDataDirectory).getFullPathName() << newLine
<< "Common documents folder: " << File::getSpecialLocation (File::commonDocumentsDirectory) .getFullPathName() << newLine
<< "Local temp folder: " << File::getSpecialLocation (File::tempDirectory) .getFullPathName() << newLine
<< newLine
<< "File System roots: " << getFileSystemRoots() << newLine
<< "Free space in home folder: " << File::descriptionOfSizeInBytes (File::getSpecialLocation (File::userHomeDirectory)
.getBytesFreeOnVolume()) << newLine
<< newLine
<< getDisplayInfo() << newLine
<< "Network IP addresses: " << newLine << getIPAddressList() << newLine
<< "Network card MAC addresses: " << newLine << getMacAddressList() << newLine;
DBG (systemInfo);
return systemInfo;
}
class SystemInfoDemo : public Component
{
public:
SystemInfoDemo()
{
addAndMakeVisible (resultsBox);
resultsBox.setReadOnly (true);
resultsBox.setMultiLine (true);
resultsBox.setColour (TextEditor::backgroundColourId, Colours::transparentBlack);
resultsBox.setFont (Font (Font::getDefaultMonospacedFontName(), 12.0f, Font::plain));
resultsBox.setText (getAllSystemInfo());
}
void paint (Graphics& g) override
{
g.fillAll (Colour::greyLevel (0.93f));
}
void resized() override
{
resultsBox.setBounds (getLocalBounds().reduced (8));
}
private:
TextEditor resultsBox;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SystemInfoDemo);
};
// This static object will register this demo type in a global list of demos..
static JuceDemoType<SystemInfoDemo> demo ("02 System Info");
@@ -0,0 +1,254 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-12 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../JuceDemoHeader.h"
//==============================================================================
/** Simple message that holds a Colour. */
struct ColourMessage : public Message
{
ColourMessage (Colour col) : colour (col)
{
}
/** Returns the colour of a ColourMessage of white if the message is not a ColourMessage. */
static Colour getColour (const Message& message)
{
if (const ColourMessage* cm = dynamic_cast<const ColourMessage*> (&message))
return cm->colour;
return Colours::white;
}
private:
Colour colour;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ColourMessage)
};
//==============================================================================
/** Simple component that can be triggered to flash.
The flash will then fade using a Timer to repaint itself and will send a change
message once it is finished.
*/
class FlashingComponent : public Component,
public MessageListener,
public ChangeBroadcaster,
private Timer
{
public:
FlashingComponent()
: flashAlpha (0.0f),
colour (Colours::red)
{
}
void startFlashing()
{
flashAlpha = 1.0f;
startTimerHz (25);
}
/** Stops this component flashing without sending a change message. */
void stopFlashing()
{
flashAlpha = 0.0f;
stopTimer();
repaint();
}
/** Sets the colour of the component. */
void setFlashColour (const Colour newColour)
{
colour = newColour;
repaint();
}
/** Draws our component. */
void paint (Graphics& g) override
{
g.setColour (colour.overlaidWith (Colours::white.withAlpha (flashAlpha)));
g.fillEllipse (getLocalBounds().toFloat());
}
/** Custom mouse handler to trigger a flash. */
void mouseDown (const MouseEvent&) override
{
startFlashing();
}
/** Message listener callback used to change our colour */
void handleMessage (const Message& message) override
{
setFlashColour (ColourMessage::getColour (message));
}
private:
float flashAlpha;
Colour colour;
void timerCallback() override
{
// Reduce the alpha level of the flash slightly so it fades out
flashAlpha -= 0.075f;
if (flashAlpha < 0.05f)
{
stopFlashing();
sendChangeMessage();
// Once we've finsihed flashing send a change message to trigger the next component to flash
}
repaint();
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FlashingComponent)
};
//==============================================================================
class TimersAndEventsDemo : public Component,
private ChangeListener,
private Button::Listener
{
public:
TimersAndEventsDemo()
{
setOpaque (true);
// Create and add our FlashingComponents with some random colours and sizes
for (int i = 0; i < numFlashingComponents; ++i)
{
FlashingComponent* newFlasher = new FlashingComponent();
flashingComponents.add (newFlasher);
newFlasher->setFlashColour (getRandomBrightColour());
newFlasher->addChangeListener (this);
const int diameter = 25 + random.nextInt (75);
newFlasher->setSize (diameter, diameter);
addAndMakeVisible (newFlasher);
}
addAndMakeVisible (stopButton);
stopButton.addListener (this);
stopButton.setButtonText ("Stop");
addAndMakeVisible (randomColourButton);
randomColourButton.addListener (this);
randomColourButton.setButtonText ("Set Random Colour");
// lay out our components in a psudo random grid
Rectangle<int> area (0, 100, 150, 150);
for (int i = 0; i < flashingComponents.size(); ++i)
{
FlashingComponent* comp = flashingComponents.getUnchecked (i);
Rectangle<int> buttonArea (area.withSize (comp->getWidth(), comp->getHeight()));
buttonArea.translate (random.nextInt (area.getWidth() - comp->getWidth()),
random.nextInt (area.getHeight() - comp->getHeight()));
comp->setBounds (buttonArea);
area.translate (area.getWidth(), 0);
// if we go off the right start a new row
if (area.getRight() > (800 - area.getWidth()))
{
area.translate (0, area.getWidth());
area.setX (0);
}
}
}
~TimersAndEventsDemo()
{
stopButton.removeListener (this);
randomColourButton.removeListener (this);
for (int i = flashingComponents.size(); --i >= 0;)
flashingComponents.getUnchecked (i)->removeChangeListener (this);
}
void paint (Graphics& g) override
{
g.fillAll (Colours::darkgrey);
}
void paintOverChildren (Graphics& g) override
{
const Rectangle<int> explanationArea (getLocalBounds().removeFromTop (100));
AttributedString s;
s.append ("Click on a circle to make it flash. When it has finished flashing it will send a message which causes the next circle to flash");
s.append (newLine);
s.append ("Click the \"Set Random Colour\" button to change the colour of one of the circles.");
s.append (newLine);
s.setFont (Font (16.0f));
s.setColour (Colours::lightgrey);
s.draw (g, explanationArea.reduced (10).toFloat());
}
void resized() override
{
Rectangle<int> area (getLocalBounds().removeFromBottom (40));
randomColourButton.setBounds (area.removeFromLeft (166).reduced (8));
stopButton.setBounds (area.removeFromRight (166).reduced (8));
}
private:
enum { numFlashingComponents = 9 };
OwnedArray<FlashingComponent> flashingComponents;
TextButton randomColourButton, stopButton;
Random random;
void changeListenerCallback (ChangeBroadcaster* source) override
{
for (int i = 0; i < flashingComponents.size(); ++i)
if (source == flashingComponents.getUnchecked (i))
flashingComponents.getUnchecked ((i + 1) % flashingComponents.size())->startFlashing();
}
void buttonClicked (Button* button) override
{
if (button == &randomColourButton)
{
// Here we post a new ColourMessage with a random colour to a random flashing component.
// This will send a message to the component asynchronously and trigger its handleMessage callback
flashingComponents.getUnchecked (random.nextInt (flashingComponents.size()))->postMessage (new ColourMessage (getRandomBrightColour()));
}
else if (button == &stopButton)
{
for (int i = 0; i < flashingComponents.size(); ++i)
flashingComponents.getUnchecked (i)->stopFlashing();
}
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TimersAndEventsDemo)
};
// This static object will register this demo type in a global list of demos..
static JuceDemoType<TimersAndEventsDemo> demo ("40 Timers & Events");
@@ -0,0 +1,193 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-12 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../JuceDemoHeader.h"
struct UnitTestClasses
{
class UnitTestsDemo;
class TestRunnerThread;
//==============================================================================
// This subclass of UnitTestRunner is used to redirect the test output to our
// TextBox, and to interrupt the running tests when our thread is asked to stop..
class CustomTestRunner : public UnitTestRunner
{
public:
CustomTestRunner (TestRunnerThread& trt) : owner (trt)
{
}
void logMessage (const String& message) override
{
owner.logMessage (message);
}
bool shouldAbortTests() override
{
return owner.threadShouldExit();
}
private:
TestRunnerThread& owner;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CustomTestRunner);
};
//==============================================================================
class TestRunnerThread : public Thread,
private Timer
{
public:
TestRunnerThread (UnitTestsDemo& utd)
: Thread ("Unit Tests"),
owner (utd)
{
}
void run() override
{
CustomTestRunner runner (*this);
runner.runAllTests();
startTimer (50); // when finished, start the timer which will
// wait for the thread to end, then tell our component.
}
void logMessage (const String& message)
{
MessageManagerLock mm (this);
if (mm.lockWasGained()) // this lock may fail if this thread has been told to stop
owner.logMessage (message);
}
void timerCallback() override
{
if (! isThreadRunning())
owner.testFinished(); // inform the demo page when done, so it can delete this thread.
}
private:
UnitTestsDemo& owner;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TestRunnerThread);
};
//==============================================================================
class UnitTestsDemo : public Component,
public Button::Listener
{
public:
UnitTestsDemo()
: startTestButton ("Run Unit Tests...")
{
setOpaque (true);
addAndMakeVisible (startTestButton);
addAndMakeVisible (testResultsBox);
testResultsBox.setMultiLine (true);
testResultsBox.setFont (Font (Font::getDefaultMonospacedFontName(), 12.0f, Font::plain));
startTestButton.addListener (this);
logMessage ("This panel runs all the built-in JUCE unit-tests.\n");
logMessage ("To add your own unit-tests, see the JUCE_UNIT_TESTS macro.");
}
~UnitTestsDemo()
{
stopTest();
}
//==============================================================================
void paint (Graphics& g) override
{
g.fillAll (Colours::grey);
}
void resized() override
{
Rectangle<int> r (getLocalBounds().reduced (6));
startTestButton.setBounds (r.removeFromTop (25).removeFromLeft (200));
testResultsBox.setBounds (r.withTrimmedTop (5));
}
void buttonClicked (Button* buttonThatWasClicked) override
{
if (buttonThatWasClicked == &startTestButton)
{
startTest();
}
}
void startTest()
{
testResultsBox.clear();
startTestButton.setEnabled (false);
currentTestThread = new TestRunnerThread (*this);
currentTestThread->startThread();
}
void stopTest()
{
if (currentTestThread != nullptr)
{
currentTestThread->stopThread (15000);
currentTestThread = nullptr;
}
}
void logMessage (const String& message)
{
testResultsBox.moveCaretToEnd();
testResultsBox.insertTextAtCaret (message + newLine);
testResultsBox.moveCaretToEnd();
}
void testFinished()
{
stopTest();
startTestButton.setEnabled (true);
logMessage (newLine + "*** Tests finished ***");
}
private:
ScopedPointer<TestRunnerThread> currentTestThread;
TextButton startTestButton;
TextEditor testResultsBox;
Label label;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UnitTestsDemo);
};
};
// This static object will register this demo type in a global list of demos..
static JuceDemoType<UnitTestClasses::UnitTestsDemo> demo ("40 Unit Tests");
@@ -0,0 +1,296 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-12 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../JuceDemoHeader.h"
//==============================================================================
class ValueTreeItem : public TreeViewItem,
private ValueTree::Listener
{
public:
ValueTreeItem (const ValueTree& v, UndoManager& um)
: tree (v), undoManager (um)
{
tree.addListener (this);
}
String getUniqueName() const override
{
return tree["name"].toString();
}
bool mightContainSubItems() override
{
return tree.getNumChildren() > 0;
}
void paintItem (Graphics& g, int width, int height) override
{
g.setColour (Colours::black);
g.setFont (15.0f);
g.drawText (tree["name"].toString(),
4, 0, width - 4, height,
Justification::centredLeft, true);
}
void itemOpennessChanged (bool isNowOpen) override
{
if (isNowOpen && getNumSubItems() == 0)
refreshSubItems();
else
clearSubItems();
}
var getDragSourceDescription() override
{
return "Drag Demo";
}
bool isInterestedInDragSource (const DragAndDropTarget::SourceDetails& dragSourceDetails) override
{
return dragSourceDetails.description == "Drag Demo";
}
void itemDropped (const DragAndDropTarget::SourceDetails&, int insertIndex) override
{
moveItems (*getOwnerView(),
getSelectedTreeViewItems (*getOwnerView()),
tree, insertIndex, undoManager);
}
static void moveItems (TreeView& treeView, const Array<ValueTree>& items,
ValueTree newParent, int insertIndex, UndoManager& undoManager)
{
if (items.size() > 0)
{
ScopedPointer<XmlElement> oldOpenness (treeView.getOpennessState (false));
for (int i = items.size(); --i >= 0;)
{
ValueTree& v = items.getReference(i);
if (v.getParent().isValid() && newParent != v && ! newParent.isAChildOf (v))
{
v.getParent().removeChild (v, &undoManager);
newParent.addChild (v, insertIndex, &undoManager);
}
}
if (oldOpenness != nullptr)
treeView.restoreOpennessState (*oldOpenness, false);
}
}
static Array<ValueTree> getSelectedTreeViewItems (TreeView& treeView)
{
Array<ValueTree> items;
const int numSelected = treeView.getNumSelectedItems();
for (int i = 0; i < numSelected; ++i)
if (const ValueTreeItem* vti = dynamic_cast<ValueTreeItem*> (treeView.getSelectedItem (i)))
items.add (vti->tree);
return items;
}
private:
ValueTree tree;
UndoManager& undoManager;
void refreshSubItems()
{
clearSubItems();
for (int i = 0; i < tree.getNumChildren(); ++i)
addSubItem (new ValueTreeItem (tree.getChild (i), undoManager));
}
void valueTreePropertyChanged (ValueTree&, const Identifier&) override
{
repaintItem();
}
void valueTreeChildAdded (ValueTree& parentTree, ValueTree&) override { treeChildrenChanged (parentTree); }
void valueTreeChildRemoved (ValueTree& parentTree, ValueTree&) override { treeChildrenChanged (parentTree); }
void valueTreeChildOrderChanged (ValueTree& parentTree) override { treeChildrenChanged (parentTree); }
void valueTreeParentChanged (ValueTree&) override {}
void treeChildrenChanged (const ValueTree& parentTree)
{
if (parentTree == tree)
{
refreshSubItems();
treeHasChanged();
setOpen (true);
}
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueTreeItem)
};
//==============================================================================
class ValueTreesDemo : public Component,
public DragAndDropContainer,
private ButtonListener,
private Timer
{
public:
ValueTreesDemo()
: undoButton ("Undo"),
redoButton ("Redo")
{
addAndMakeVisible (tree);
tree.setDefaultOpenness (true);
tree.setMultiSelectEnabled (true);
tree.setRootItem (rootItem = new ValueTreeItem (createRootValueTree(), undoManager));
tree.setColour (TreeView::backgroundColourId, Colours::white);
addAndMakeVisible (undoButton);
addAndMakeVisible (redoButton);
undoButton.addListener (this);
redoButton.addListener (this);
startTimer (500);
}
~ValueTreesDemo()
{
tree.setRootItem (nullptr);
}
void paint (Graphics& g) override
{
fillTiledBackground (g);
}
void resized() override
{
Rectangle<int> r (getLocalBounds().reduced (8));
Rectangle<int> buttons (r.removeFromBottom (22));
undoButton.setBounds (buttons.removeFromLeft (100));
buttons.removeFromLeft (6);
redoButton.setBounds (buttons.removeFromLeft (100));
r.removeFromBottom (4);
tree.setBounds (r);
}
static ValueTree createTree (const String& desc)
{
ValueTree t ("Item");
t.setProperty ("name", desc, nullptr);
return t;
}
static ValueTree createRootValueTree()
{
ValueTree vt = createTree ("This demo displays a ValueTree as a treeview.");
vt.addChild (createTree ("You can drag around the nodes to rearrange them"), -1, nullptr);
vt.addChild (createTree ("..and press 'delete' to delete them"), -1, nullptr);
vt.addChild (createTree ("Then, you can use the undo/redo buttons to undo these changes"), -1, nullptr);
int n = 1;
vt.addChild (createRandomTree (n, 0), -1, nullptr);
return vt;
}
static ValueTree createRandomTree (int& counter, int depth)
{
ValueTree t = createTree ("Item " + String (counter++));
if (depth < 3)
for (int i = 1 + Random::getSystemRandom().nextInt (7); --i >= 0;)
t.addChild (createRandomTree (counter, depth + 1), -1, nullptr);
return t;
}
void deleteSelectedItems()
{
Array<ValueTree> selectedItems (ValueTreeItem::getSelectedTreeViewItems (tree));
for (int i = selectedItems.size(); --i >= 0;)
{
ValueTree& v = selectedItems.getReference(i);
if (v.getParent().isValid())
v.getParent().removeChild (v, &undoManager);
}
}
bool keyPressed (const KeyPress& key) override
{
if (key == KeyPress::deleteKey)
{
deleteSelectedItems();
return true;
}
if (key == KeyPress ('z', ModifierKeys::commandModifier, 0))
{
undoManager.undo();
return true;
}
if (key == KeyPress ('z', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0))
{
undoManager.redo();
return true;
}
return Component::keyPressed (key);
}
void buttonClicked (Button* b) override
{
if (b == &undoButton)
undoManager.undo();
else if (b == &redoButton)
undoManager.redo();
}
private:
TreeView tree;
TextButton undoButton, redoButton;
ScopedPointer<ValueTreeItem> rootItem;
UndoManager undoManager;
void timerCallback() override
{
undoManager.beginNewTransaction();
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueTreesDemo);
};
// This static object will register this demo type in a global list of demos..
static JuceDemoType<ValueTreesDemo> demo ("40 ValueTrees");
@@ -0,0 +1,253 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-12 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../JuceDemoHeader.h"
#if JUCE_QUICKTIME || JUCE_DIRECTSHOW
//==============================================================================
// so that we can easily have two video windows each with a file browser, wrap this up as a class..
class MovieComponentWithFileBrowser : public Component,
public DragAndDropTarget,
private FilenameComponentListener
{
public:
MovieComponentWithFileBrowser()
: isDragOver (false),
fileChooser ("movie", File::nonexistent, true, false, false,
"*", String::empty, "(choose a video file to play)")
{
addAndMakeVisible (videoComp);
addAndMakeVisible (fileChooser);
fileChooser.addListener (this);
fileChooser.setBrowseButtonText ("browse");
}
void setFile (const File& file)
{
fileChooser.setCurrentFile (file, true);
}
void paintOverChildren (Graphics& g)
{
if (isDragOver)
{
g.setColour (Colours::red);
g.drawRect (fileChooser.getBounds(), 2);
}
}
void resized()
{
videoComp.setBoundsWithCorrectAspectRatio (Rectangle<int> (0, 0, getWidth(), getHeight() - 30),
Justification::centred);
fileChooser.setBounds (0, getHeight() - 24, getWidth(), 24);
}
bool isInterestedInDragSource (const SourceDetails&) { return true; }
void itemDragEnter (const SourceDetails&)
{
isDragOver = true;
repaint();
}
void itemDragExit (const SourceDetails&)
{
isDragOver = false;
repaint();
}
void itemDropped (const SourceDetails& dragSourceDetails)
{
setFile (dragSourceDetails.description.toString());
isDragOver = false;
repaint();
}
private:
#if JUCE_QUICKTIME
QuickTimeMovieComponent videoComp;
#elif JUCE_DIRECTSHOW
DirectShowComponent videoComp;
#endif
bool isDragOver;
FilenameComponent fileChooser;
void filenameComponentChanged (FilenameComponent*) override
{
// this is called when the user changes the filename in the file chooser box
#if JUCE_QUICKTIME
if (videoComp.loadMovie (fileChooser.getCurrentFile(), true))
#elif JUCE_DIRECTSHOW
if (videoComp.loadMovie (fileChooser.getCurrentFile()))
#endif
{
// loaded the file ok, so let's start it playing..
videoComp.play();
resized(); // update to reflect the video's aspect ratio
}
else
{
AlertWindow::showMessageBox (AlertWindow::WarningIcon,
"Couldn't load the file!",
#if JUCE_QUICKTIME
"Sorry, QuickTime didn't manage to load that file!");
#elif JUCE_DIRECTSHOW
"Sorry, DirectShow didn't manage to load that file!");
#endif
}
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MovieComponentWithFileBrowser)
};
//==============================================================================
class VideoDemo : public Component,
public DragAndDropContainer,
private Button::Listener,
private FileBrowserListener
{
public:
VideoDemo()
: moviesWildcardFilter ("*", "*", "Movies File Filter"),
directoryThread ("Movie File Scanner Thread"),
movieList (&moviesWildcardFilter, directoryThread),
fileTree (movieList),
resizerBar (&stretchableManager, 1, false)
{
setOpaque (true);
movieList.setDirectory (File::getSpecialLocation (File::userMoviesDirectory), true, true);
directoryThread.startThread (1);
fileTree.addListener (this);
fileTree.setColour (TreeView::backgroundColourId, Colours::lightgrey);
addAndMakeVisible (fileTree);
addAndMakeVisible (resizerBar);
loadLeftButton.setButtonText ("Load Left");
loadRightButton.setButtonText ("Load Right");
loadLeftButton.addListener (this);
loadRightButton.addListener (this);
addAndMakeVisible (loadLeftButton);
addAndMakeVisible (loadRightButton);
addAndMakeVisible (movieCompLeft);
addAndMakeVisible (movieCompRight);
// we have to set up our StretchableLayoutManager so it know the limits and preferred sizes of it's contents
stretchableManager.setItemLayout (0, // for the fileTree
-0.1, -0.9, // must be between 50 pixels and 90% of the available space
-0.3); // and its preferred size is 30% of the total available space
stretchableManager.setItemLayout (1, // for the resize bar
5, 5, 5); // hard limit to 5 pixels
stretchableManager.setItemLayout (2, // for the movie components
-0.1, -0.9, // size must be between 50 pixels and 90% of the available space
-0.7); // and its preferred size is 70% of the total available space
}
~VideoDemo()
{
loadLeftButton.removeListener (this);
loadRightButton.removeListener (this);
fileTree.removeListener (this);
}
void paint (Graphics& g) override
{
fillTiledBackground (g);
}
void resized() override
{
// make a list of two of our child components that we want to reposition
Component* comps[] = { &fileTree, &resizerBar, nullptr };
// this will position the 3 components, one above the other, to fit
// vertically into the rectangle provided.
stretchableManager.layOutComponents (comps, 3,
0, 0, getWidth(), getHeight(),
true, true);
// now position out two video components in the space that's left
Rectangle<int> area (getLocalBounds().removeFromBottom (getHeight() - resizerBar.getBottom()));
{
Rectangle<int> buttonArea (area.removeFromTop (30));
loadLeftButton.setBounds (buttonArea.removeFromLeft (buttonArea.getWidth() / 2).reduced (5));
loadRightButton.setBounds (buttonArea.reduced (5));
}
movieCompLeft.setBounds (area.removeFromLeft (area.getWidth() / 2).reduced (5));
movieCompRight.setBounds (area.reduced (5));
}
private:
WildcardFileFilter moviesWildcardFilter;
TimeSliceThread directoryThread;
DirectoryContentsList movieList;
FileTreeComponent fileTree;
StretchableLayoutManager stretchableManager;
StretchableLayoutResizerBar resizerBar;
TextButton loadLeftButton, loadRightButton;
MovieComponentWithFileBrowser movieCompLeft, movieCompRight;
void buttonClicked (Button* button) override
{
if (button == &loadLeftButton)
movieCompLeft.setFile (fileTree.getSelectedFile (0));
else if (button == &loadRightButton)
movieCompRight.setFile (fileTree.getSelectedFile (0));
}
void selectionChanged() override
{
// we're just going to update the drag description of out tree so that rows can be dragged onto the file players
fileTree.setDragAndDropDescription (fileTree.getSelectedFile().getFullPathName());
}
void fileClicked (const File&, const MouseEvent&) override {}
void fileDoubleClicked (const File&) override {}
void browserRootChanged (const File&) override {}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VideoDemo)
};
// This static object will register this demo type in a global list of demos..
static JuceDemoType<VideoDemo> demo ("29 Graphics: Video Playback");
#endif
@@ -0,0 +1,359 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-12 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include <map>
//==============================================================================
/**
This is a quick-and-dirty parser for the 3D OBJ file format.
Just call load() and if there aren't any errors, the 'shapes' array should
be filled with all the shape objects that were loaded from the file.
*/
class WavefrontObjFile
{
public:
WavefrontObjFile() {}
Result load (const String& objFileContent)
{
shapes.clear();
return parseObjFile (StringArray::fromLines (objFileContent));
}
Result load (const File& file)
{
sourceFile = file;
return load (file.loadFileAsString());
}
//==============================================================================
typedef juce::uint32 Index;
struct Vertex { float x, y, z; };
struct TextureCoord { float x, y; };
struct Mesh
{
Array<Vertex> vertices, normals;
Array<TextureCoord> textureCoords;
Array<Index> indices;
};
struct Material
{
Material() noexcept : shininess (1.0f), refractiveIndex (0.0f)
{
zerostruct (ambient);
zerostruct (diffuse);
zerostruct (specular);
zerostruct (transmittance);
zerostruct (emission);
}
String name;
Vertex ambient, diffuse, specular, transmittance, emission;
float shininess, refractiveIndex;
String ambientTextureName, diffuseTextureName,
specularTextureName, normalTextureName;
StringPairArray parameters;
};
struct Shape
{
String name;
Mesh mesh;
Material material;
};
OwnedArray<Shape> shapes;
private:
//==============================================================================
File sourceFile;
struct TripleIndex
{
TripleIndex() noexcept : vertexIndex (-1), textureIndex (-1), normalIndex (-1) {}
bool operator< (const TripleIndex& other) const noexcept { return vertexIndex < other.vertexIndex; }
int vertexIndex, textureIndex, normalIndex;
};
struct IndexMap
{
std::map<TripleIndex, Index> map;
Index getIndexFor (TripleIndex i, Mesh& newMesh, const Mesh& srcMesh)
{
const std::map<TripleIndex, Index>::iterator it (map.find (i));
if (it != map.end())
return it->second;
const Index index = (Index) newMesh.vertices.size();
if (isPositiveAndBelow (i.vertexIndex, srcMesh.vertices.size()))
newMesh.vertices.add (srcMesh.vertices.getReference (i.vertexIndex));
if (isPositiveAndBelow (i.normalIndex, srcMesh.normals.size()))
newMesh.normals.add (srcMesh.normals.getReference (i.normalIndex));
if (isPositiveAndBelow (i.textureIndex, srcMesh.textureCoords.size()))
newMesh.textureCoords.add (srcMesh.textureCoords.getReference (i.textureIndex));
map[i] = index;
return index;
}
};
static float parseFloat (String::CharPointerType& t)
{
t = t.findEndOfWhitespace();
return (float) CharacterFunctions::readDoubleValue (t);
}
static Vertex parseVertex (String::CharPointerType t)
{
Vertex v;
v.x = parseFloat (t);
v.y = parseFloat (t);
v.z = parseFloat (t);
return v;
}
static TextureCoord parseTextureCoord (String::CharPointerType t)
{
TextureCoord tc;
tc.x = parseFloat (t);
tc.y = parseFloat (t);
return tc;
}
static bool matchToken (String::CharPointerType& t, const char* token)
{
const int len = (int) strlen (token);
if (CharacterFunctions::compareUpTo (CharPointer_ASCII (token), t, len) == 0)
{
String::CharPointerType end = t + len;
if (end.isEmpty() || end.isWhitespace())
{
t = end.findEndOfWhitespace();
return true;
}
}
return false;
}
struct Face
{
Face (String::CharPointerType t)
{
while (! t.isEmpty())
triples.add (parseTriple (t));
}
Array<TripleIndex> triples;
void addIndices (Mesh& newMesh, const Mesh& srcMesh, IndexMap& indexMap)
{
TripleIndex i0 (triples[0]), i1, i2 (triples[1]);
for (int i = 2; i < triples.size(); ++i)
{
i1 = i2;
i2 = triples.getReference (i);
newMesh.indices.add (indexMap.getIndexFor (i0, newMesh, srcMesh));
newMesh.indices.add (indexMap.getIndexFor (i1, newMesh, srcMesh));
newMesh.indices.add (indexMap.getIndexFor (i2, newMesh, srcMesh));
}
}
static TripleIndex parseTriple (String::CharPointerType& t)
{
TripleIndex i;
t = t.findEndOfWhitespace();
i.vertexIndex = t.getIntValue32() - 1;
t = findEndOfFaceToken (t);
if (t.isEmpty() || t.getAndAdvance() != '/')
return i;
if (*t == '/')
{
++t;
}
else
{
i.textureIndex = t.getIntValue32() - 1;
t = findEndOfFaceToken (t);
if (t.isEmpty() || t.getAndAdvance() != '/')
return i;
}
i.normalIndex = t.getIntValue32() - 1;
t = findEndOfFaceToken (t);
return i;
}
static String::CharPointerType findEndOfFaceToken (String::CharPointerType t) noexcept
{
return CharacterFunctions::findEndOfToken (t, CharPointer_ASCII ("/ \t"), String::empty.getCharPointer());
}
};
static Shape* parseFaceGroup (const Mesh& srcMesh,
const Array<Face>& faceGroup,
const Material& material,
const String& name)
{
if (faceGroup.size() == 0)
return nullptr;
ScopedPointer<Shape> shape (new Shape());
shape->name = name;
shape->material = material;
IndexMap indexMap;
for (int i = 0; i < faceGroup.size(); ++i)
faceGroup.getReference(i).addIndices (shape->mesh, srcMesh, indexMap);
return shape.release();
}
Result parseObjFile (const StringArray& lines)
{
Mesh mesh;
Array<Face> faceGroup;
Array<Material> knownMaterials;
Material lastMaterial;
String lastName;
for (int lineNum = 0; lineNum < lines.size(); ++lineNum)
{
String::CharPointerType l = lines[lineNum].getCharPointer().findEndOfWhitespace();
if (matchToken (l, "v")) { mesh.vertices.add (parseVertex (l)); continue; }
if (matchToken (l, "vn")) { mesh.normals.add (parseVertex (l)); continue; }
if (matchToken (l, "vt")) { mesh.textureCoords.add (parseTextureCoord (l)); continue; }
if (matchToken (l, "f")) { faceGroup.add (Face (l)); continue; }
if (matchToken (l, "usemtl"))
{
const String name (String (l).trim());
for (int i = knownMaterials.size(); --i >= 0;)
{
if (knownMaterials.getReference(i).name == name)
{
lastMaterial = knownMaterials.getReference(i);
break;
}
}
continue;
}
if (matchToken (l, "mtllib"))
{
Result r = parseMaterial (knownMaterials, String (l).trim());
continue;
}
if (matchToken (l, "g") || matchToken (l, "o"))
{
if (Shape* shape = parseFaceGroup (mesh, faceGroup, lastMaterial, lastName))
shapes.add (shape);
faceGroup.clear();
lastName = StringArray::fromTokens (l, " \t", "")[0];
continue;
}
}
if (Shape* shape = parseFaceGroup (mesh, faceGroup, lastMaterial, lastName))
shapes.add (shape);
return Result::ok();
}
Result parseMaterial (Array<Material>& materials, const String& filename)
{
jassert (sourceFile.exists());
File f (sourceFile.getSiblingFile (filename));
if (! f.exists())
return Result::fail ("Cannot open file: " + filename);
StringArray lines;
lines.addLines (f.loadFileAsString());
materials.clear();
Material material;
for (int i = 0; i < lines.size(); ++i)
{
String::CharPointerType l (lines[i].getCharPointer().findEndOfWhitespace());
if (matchToken (l, "newmtl")) { materials.add (material); material.name = String (l).trim(); continue; }
if (matchToken (l, "Ka")) { material.ambient = parseVertex (l); continue; }
if (matchToken (l, "Kd")) { material.diffuse = parseVertex (l); continue; }
if (matchToken (l, "Ks")) { material.specular = parseVertex (l); continue; }
if (matchToken (l, "Kt")) { material.transmittance = parseVertex (l); continue; }
if (matchToken (l, "Ke")) { material.emission = parseVertex (l); continue; }
if (matchToken (l, "Ni")) { material.refractiveIndex = parseFloat (l); continue; }
if (matchToken (l, "Ns")) { material.shininess = parseFloat (l); continue; }
if (matchToken (l, "map_Ka")) { material.ambientTextureName = String (l).trim(); continue; }
if (matchToken (l, "map_Kd")) { material.diffuseTextureName = String (l).trim(); continue; }
if (matchToken (l, "map_Ks")) { material.specularTextureName = String (l).trim(); continue; }
if (matchToken (l, "map_Ns")) { material.normalTextureName = String (l).trim(); continue; }
StringArray tokens;
tokens.addTokens (l, " \t", "");
if (tokens.size() >= 2)
material.parameters.set (tokens[0].trim(), tokens[1].trim());
}
materials.add (material);
return Result::ok();
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WavefrontObjFile)
};
@@ -0,0 +1,148 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-12 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../JuceDemoHeader.h"
#if JUCE_WEB_BROWSER
//==============================================================================
/** We'll use a subclass of WebBrowserComponent to demonstrate how to get callbacks
when the browser changes URL. You don't need to do this, you can just also
just use the WebBrowserComponent class directly.
*/
class DemoBrowserComponent : public WebBrowserComponent
{
public:
//==============================================================================
DemoBrowserComponent (TextEditor& addressBox) : addressTextBox (addressBox)
{
}
// This method gets called when the browser is about to go to a new URL..
bool pageAboutToLoad (const String& newURL) override
{
// We'll just update our address box to reflect the new location..
addressTextBox.setText (newURL, false);
// we could return false here to tell the browser not to go ahead with
// loading the page.
return true;
}
// This method gets called when the browser is requested to launch a new window
void newWindowAttemptingToLoad (const String& newURL) override
{
// We'll just load the URL into the main window
goToURL (newURL);
}
private:
TextEditor& addressTextBox;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DemoBrowserComponent)
};
//==============================================================================
class WebBrowserDemo : public Component,
private TextEditor::Listener,
private ButtonListener
{
public:
WebBrowserDemo()
: goButton ("Go", "Go to URL"),
backButton ("<<", "Back"),
forwardButton (">>", "Forward")
{
setOpaque (true);
// Create an address box..
addAndMakeVisible (addressTextBox);
addressTextBox.setTextToShowWhenEmpty ("Enter a web address, e.g. http://www.juce.com", Colours::grey);
addressTextBox.addListener (this);
// create the actual browser component
addAndMakeVisible (webView = new DemoBrowserComponent (addressTextBox));
// add some buttons..
addAndMakeVisible (goButton);
goButton.addListener (this);
addAndMakeVisible (backButton);
backButton.addListener (this);
addAndMakeVisible (forwardButton);
forwardButton.addListener (this);
// send the browser to a start page..
webView->goToURL ("http://www.juce.com");
}
void paint (Graphics& g) override
{
g.fillAll (Colours::grey);
}
void resized() override
{
webView->setBounds (10, 45, getWidth() - 20, getHeight() - 55);
goButton.setBounds (getWidth() - 45, 10, 35, 25);
addressTextBox.setBounds (100, 10, getWidth() - 155, 25);
backButton.setBounds (10, 10, 35, 25);
forwardButton.setBounds (55, 10, 35, 25);
}
private:
ScopedPointer<DemoBrowserComponent> webView;
TextEditor addressTextBox;
TextButton goButton, backButton, forwardButton;
void textEditorTextChanged (TextEditor&) override {}
void textEditorEscapeKeyPressed (TextEditor&) override {}
void textEditorFocusLost (TextEditor&) override {}
void textEditorReturnKeyPressed (TextEditor&) override
{
webView->goToURL (addressTextBox.getText());
}
void buttonClicked (Button* b) override
{
if (b == &backButton)
webView->goBack();
else if (b == &forwardButton)
webView->goForward();
else if (b == &goButton)
webView->goToURL (addressTextBox.getText());
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebBrowserDemo);
};
// This static object will register this demo type in a global list of demos..
static JuceDemoType<WebBrowserDemo> demo ("10 Components: Web Browser");
#endif // JUCE_WEB_BROWSER
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,331 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-12 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../JuceDemoHeader.h"
//==============================================================================
/** Just a simple window that deletes itself when closed. */
class BasicWindow : public DocumentWindow
{
public:
BasicWindow (const String& name, Colour backgroundColour, int buttonsNeeded)
: DocumentWindow (name, backgroundColour, buttonsNeeded)
{
}
void closeButtonPressed()
{
delete this;
}
private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BasicWindow)
};
//==============================================================================
/** This window contains a ColourSelector which can be used to change the window's colour. */
class ColourSelectorWindow : public DocumentWindow,
private ChangeListener
{
public:
ColourSelectorWindow (const String& name, Colour backgroundColour, int buttonsNeeded)
: DocumentWindow (name, backgroundColour, buttonsNeeded),
selector (ColourSelector::showColourAtTop | ColourSelector::showSliders | ColourSelector::showColourspace)
{
selector.setCurrentColour (backgroundColour);
selector.setColour (ColourSelector::backgroundColourId, Colours::transparentWhite);
selector.addChangeListener (this);
setContentOwned (&selector, false);
}
~ColourSelectorWindow()
{
selector.removeChangeListener (this);
}
void closeButtonPressed()
{
delete this;
}
private:
ColourSelector selector;
void changeListenerCallback (ChangeBroadcaster* source)
{
if (source == &selector)
setBackgroundColour (selector.getCurrentColour());
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ColourSelectorWindow)
};
//==============================================================================
class BouncingBallComponent : public Component,
public Timer
{
public:
BouncingBallComponent()
{
setInterceptsMouseClicks (false, false);
Random random;
const float size = 10.0f + random.nextInt (30);
ballBounds.setBounds (random.nextFloat() * 100.0f,
random.nextFloat() * 100.0f,
size, size);
direction.x = random.nextFloat() * 8.0f - 4.0f;
direction.y = random.nextFloat() * 8.0f - 4.0f;
colour = Colour ((juce::uint32) random.nextInt())
.withAlpha (0.5f)
.withBrightness (0.7f);
startTimer (60);
}
void paint (Graphics& g)
{
g.setColour (colour);
g.fillEllipse (ballBounds - getPosition().toFloat());
}
void timerCallback()
{
ballBounds += direction;
if (ballBounds.getX() < 0) direction.x = std::abs (direction.x);
if (ballBounds.getY() < 0) direction.y = std::abs (direction.y);
if (ballBounds.getRight() > getParentWidth()) direction.x = -std::abs (direction.x);
if (ballBounds.getBottom() > getParentHeight()) direction.y = -std::abs (direction.y);
setBounds (ballBounds.getSmallestIntegerContainer());
}
private:
Colour colour;
Rectangle<float> ballBounds;
Point<float> direction;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BouncingBallComponent)
};
//==============================================================================
class BouncingBallsContainer : public Component
{
public:
BouncingBallsContainer (int numBalls)
{
for (int i = 0; i < numBalls; ++i)
{
BouncingBallComponent* newBall = new BouncingBallComponent();
balls.add (newBall);
addAndMakeVisible (newBall);
}
}
void mouseDown (const MouseEvent& e)
{
dragger.startDraggingComponent (this, e);
}
void mouseDrag (const MouseEvent& e)
{
// as there's no titlebar we have to manage the dragging ourselves
dragger.dragComponent (this, e, 0);
}
void paint (Graphics& g)
{
if (isOpaque())
g.fillAll (Colours::white);
else
g.fillAll (Colours::blue.withAlpha (0.2f));
g.setFont (16.0f);
g.setColour (Colours::black);
g.drawFittedText ("This window has no titlebar and a transparent background.",
getLocalBounds().reduced (8, 0),
Justification::centred, 5);
g.drawRect (getLocalBounds());
}
private:
ComponentDragger dragger;
OwnedArray<BouncingBallComponent> balls;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BouncingBallsContainer)
};
//==============================================================================
class WindowsDemo : public Component,
private Button::Listener
{
public:
enum Windows
{
dialog,
document,
alert,
numWindows
};
WindowsDemo()
{
setOpaque (true);
showWindowsButton.setButtonText ("Show Windows");
addAndMakeVisible (showWindowsButton);
showWindowsButton.addListener (this);
closeWindowsButton.setButtonText ("Close Windows");
addAndMakeVisible (closeWindowsButton);
closeWindowsButton.addListener (this);
}
~WindowsDemo()
{
closeAllWindows();
closeWindowsButton.removeListener (this);
showWindowsButton.removeListener (this);
}
void paint (Graphics& g) override
{
g.fillAll (Colours::grey);
}
void resized() override
{
const Rectangle<int> buttonSize (0, 0, 108, 28);
Rectangle<int> area ((getWidth() / 2) - (buttonSize.getWidth() / 2),
(getHeight() / 2) - buttonSize.getHeight(),
buttonSize.getWidth(), buttonSize.getHeight());
showWindowsButton.setBounds (area.reduced (2));
closeWindowsButton.setBounds (area.translated (0, buttonSize.getHeight()).reduced (2));
}
private:
// Because in this demo the windows delete themselves, we'll use the
// Component::SafePointer class to point to them, which automatically becomes
// null when the component that it points to is deleted.
Array< Component::SafePointer<Component> > windows;
TextButton showWindowsButton, closeWindowsButton;
void showAllWindows()
{
closeAllWindows();
showDocumentWindow (false);
showDocumentWindow (true);
showTransparentWindow();
showDialogWindow();
}
void closeAllWindows()
{
for (int i = 0; i < windows.size(); ++i)
windows.getReference(i).deleteAndZero();
windows.clear();
}
void showDialogWindow()
{
String m;
m << "Dialog Windows can be used to quickly show a component, usually blocking mouse input to other windows." << newLine
<< newLine
<< "They can also be quickly closed with the escape key, try it now.";
DialogWindow::LaunchOptions options;
Label* label = new Label();
label->setText (m, dontSendNotification);
label->setColour (Label::textColourId, Colours::whitesmoke);
options.content.setOwned (label);
Rectangle<int> area (0, 0, 300, 200);
options.content->setSize (area.getWidth(), area.getHeight());
options.dialogTitle = "Dialog Window";
options.dialogBackgroundColour = Colour (0xff0e345a);
options.escapeKeyTriggersCloseButton = true;
options.useNativeTitleBar = false;
options.resizable = true;
const RectanglePlacement placement (RectanglePlacement::xRight + RectanglePlacement::yBottom + RectanglePlacement::doNotResize);
DialogWindow* dw = options.launchAsync();
dw->centreWithSize (300, 200);
}
void showDocumentWindow (bool native)
{
DocumentWindow* dw = new ColourSelectorWindow ("Document Window", getRandomBrightColour(), DocumentWindow::allButtons);
windows.add (dw);
Rectangle<int> area (0, 0, 300, 400);
const RectanglePlacement placement ((native ? RectanglePlacement::xLeft : RectanglePlacement::xRight)
+ RectanglePlacement::yTop + RectanglePlacement::doNotResize);
Rectangle<int> result (placement.appliedTo (area, Desktop::getInstance().getDisplays().getMainDisplay().userArea.reduced (20)));
dw->setBounds (result);
dw->setResizable (true, ! native);
dw->setUsingNativeTitleBar (native);
dw->setVisible (true);
}
void showTransparentWindow()
{
BouncingBallsContainer* balls = new BouncingBallsContainer (3);
balls->addToDesktop (ComponentPeer::windowIsTemporary);
windows.add (balls);
Rectangle<int> area (0, 0, 200, 200);
const RectanglePlacement placement (RectanglePlacement::xLeft + RectanglePlacement::yBottom + RectanglePlacement::doNotResize);
Rectangle<int> result (placement.appliedTo (area, Desktop::getInstance().getDisplays().getMainDisplay().userArea.reduced (20)));
balls->setBounds (result);
balls->setVisible (true);
}
void buttonClicked (Button* button) override
{
if (button == &showWindowsButton)
showAllWindows();
else if (button == &closeWindowsButton)
closeAllWindows();
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsDemo);
};
// This static object will register this demo type in a global list of demos..
static JuceDemoType<WindowsDemo> demo ("10 Components: Windows");
@@ -0,0 +1,385 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-12 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../JuceDemoHeader.h"
//==============================================================================
class XmlTreeItem : public TreeViewItem
{
public:
XmlTreeItem (XmlElement& x) : xml (x)
{
}
String getUniqueName() const override
{
if (xml.getTagName().isEmpty())
return "unknown";
return xml.getTagName();
}
bool mightContainSubItems() override
{
return xml.getFirstChildElement() != nullptr;
}
void paintItem (Graphics& g, int width, int height) override
{
// if this item is selected, fill it with a background colour..
if (isSelected())
g.fillAll (Colours::blue.withAlpha (0.3f));
// use a "colour" attribute in the xml tag for this node to set the text colour..
g.setColour (Colour::fromString (xml.getStringAttribute ("colour", "ff000000")));
g.setFont (height * 0.7f);
// draw the xml element's tag name..
g.drawText (xml.getTagName(),
4, 0, width - 4, height,
Justification::centredLeft, true);
}
void itemOpennessChanged (bool isNowOpen) override
{
if (isNowOpen)
{
// if we've not already done so, we'll now add the tree's sub-items. You could
// also choose to delete the existing ones and refresh them if that's more suitable
// in your app.
if (getNumSubItems() == 0)
{
// create and add sub-items to this node of the tree, corresponding to
// each sub-element in the XML..
forEachXmlChildElement (xml, child)
{
jassert (child != nullptr);
addSubItem (new XmlTreeItem (*child));
}
}
}
else
{
// in this case, we'll leave any sub-items in the tree when the node gets closed,
// though you could choose to delete them if that's more appropriate for
// your application.
}
}
private:
XmlElement& xml;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (XmlTreeItem)
};
//==============================================================================
class JsonTreeItem : public TreeViewItem
{
public:
JsonTreeItem (Identifier i, var value) : identifier (i), json (value)
{
}
String getUniqueName() const override
{
return identifier.toString() + "_id";
}
bool mightContainSubItems() override
{
if (DynamicObject* obj = json.getDynamicObject())
return obj->getProperties().size() > 0;
return json.isArray();
}
void paintItem (Graphics& g, int width, int height) override
{
// if this item is selected, fill it with a background colour..
if (isSelected())
g.fillAll (Colours::blue.withAlpha (0.3f));
g.setColour (Colours::black);
g.setFont (height * 0.7f);
// draw the element's tag name..
g.drawText (getText(),
4, 0, width - 4, height,
Justification::centredLeft, true);
}
void itemOpennessChanged (bool isNowOpen) override
{
if (isNowOpen)
{
// if we've not already done so, we'll now add the tree's sub-items. You could
// also choose to delete the existing ones and refresh them if that's more suitable
// in your app.
if (getNumSubItems() == 0)
{
// create and add sub-items to this node of the tree, corresponding to
// the type of object this var represents
if (json.isArray())
{
for (int i = 0; i < json.size(); ++i)
{
var& child (json[i]);
jassert (! child.isVoid());
addSubItem (new JsonTreeItem (Identifier(), child));
}
}
else if (DynamicObject* obj = json.getDynamicObject())
{
NamedValueSet& props (obj->getProperties());
for (int i = 0; i < props.size(); ++i)
{
const Identifier id (props.getName (i));
var child (props[id]);
jassert (! child.isVoid());
addSubItem (new JsonTreeItem (id, child));
}
}
}
}
else
{
// in this case, we'll leave any sub-items in the tree when the node gets closed,
// though you could choose to delete them if that's more appropriate for
// your application.
}
}
private:
Identifier identifier;
var json;
/** Returns the text to display in the tree.
This is a little more complex for JSON than XML as nodes can be strings, objects or arrays.
*/
String getText() const
{
String text;
if (identifier.isValid())
text << identifier.toString();
if (! json.isVoid())
{
if (text.isNotEmpty() && (! json.isArray()))
text << ": ";
if (json.isObject() && (! identifier.isValid()))
text << "[Array]";
else if (! json.isArray())
text << json.toString();
}
return text;
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JsonTreeItem)
};
//==============================================================================
class StringsDemo : public Component,
private ComboBox::Listener,
private CodeDocument::Listener
{
public:
/** The type of database to parse. */
enum Type
{
xml,
json
};
StringsDemo()
: codeDocumentComponent (codeDocument, nullptr)
{
setOpaque (true);
addAndMakeVisible (typeBox);
typeBox.addListener (this);
typeBox.addItem ("XML", 1);
typeBox.addItem ("JSON", 2);
comboBoxLabel.setText ("Database Type:", dontSendNotification);
comboBoxLabel.attachToComponent (&typeBox, true);
addAndMakeVisible (codeDocumentComponent);
codeDocument.addListener (this);
addAndMakeVisible (resultsTree);
resultsTree.setColour (TreeView::backgroundColourId, Colours::white);
resultsTree.setDefaultOpenness (true);
addAndMakeVisible (errorMessage);
errorMessage.setReadOnly (true);
errorMessage.setMultiLine (true);
errorMessage.setCaretVisible (false);
errorMessage.setColour (TextEditor::outlineColourId, Colours::transparentWhite);
errorMessage.setColour (TextEditor::shadowColourId, Colours::transparentWhite);
typeBox.setSelectedId (1);
}
~StringsDemo()
{
resultsTree.setRootItem (nullptr);
}
void paint (Graphics& g) override
{
fillTiledBackground (g);
}
void resized() override
{
Rectangle<int> area (getLocalBounds());
typeBox.setBounds (area.removeFromTop (36).removeFromRight (150).reduced (8));
codeDocumentComponent.setBounds (area.removeFromTop(area.getHeight() / 2).reduced (8));
resultsTree.setBounds (area.reduced (8));
errorMessage.setBounds (resultsTree.getBounds());
}
private:
ComboBox typeBox;
Label comboBoxLabel;
CodeDocument codeDocument;
CodeEditorComponent codeDocumentComponent;
TreeView resultsTree;
ScopedPointer<TreeViewItem> rootItem;
ScopedPointer<XmlElement> parsedXml;
TextEditor errorMessage;
void rebuildTree()
{
ScopedPointer<XmlElement> openness;
if (rootItem != nullptr)
openness = rootItem->getOpennessState();
createNewRootNode();
if (openness != nullptr && rootItem != nullptr)
rootItem->restoreOpennessState (*openness);
}
void createNewRootNode()
{
// clear the current tree
resultsTree.setRootItem (nullptr);
rootItem = nullptr;
// try and parse the editor's contents
switch (typeBox.getSelectedItemIndex())
{
case xml: rootItem = rebuildXml(); break;
case json: rootItem = rebuildJson(); break;
default: rootItem = nullptr; break;
}
// if we have a valid TreeViewItem hide any old error messages and set our TreeView to use it
if (rootItem != nullptr)
errorMessage.clear();
errorMessage.setVisible (! errorMessage.isEmpty());
resultsTree.setRootItem (rootItem);
}
/** Parses the editors contects as XML. */
TreeViewItem* rebuildXml()
{
parsedXml = nullptr;
XmlDocument doc (codeDocument.getAllContent());
parsedXml = doc.getDocumentElement();
if (parsedXml == nullptr)
{
String error (doc.getLastParseError());
if (error.isEmpty())
error = "Unknown error";
errorMessage.setText ("Error parsing XML: " + error, dontSendNotification);
return nullptr;
}
return new XmlTreeItem (*parsedXml);
}
/** Parses the editors contects as JSON. */
TreeViewItem* rebuildJson()
{
var parsedJson;
Result result = JSON::parse (codeDocument.getAllContent(), parsedJson);
if (! result.wasOk())
{
errorMessage.setText ("Error parsing JSON: " + result.getErrorMessage());
return nullptr;
}
return new JsonTreeItem (Identifier(), parsedJson);
}
/** Clears the editor and loads some default text. */
void reset (Type type)
{
switch (type)
{
case xml: codeDocument.replaceAllContent (BinaryData::treedemo_xml); break;
case json: codeDocument.replaceAllContent (BinaryData::juce_module_info); break;
default: codeDocument.replaceAllContent (String::empty); break;
}
}
void comboBoxChanged (ComboBox* box) override
{
if (box == &typeBox)
{
if (typeBox.getSelectedId() == 1)
reset (xml);
else
reset (json);
}
}
void codeDocumentTextInserted (const String&, int) override { rebuildTree(); }
void codeDocumentTextDeleted (int, int) override { rebuildTree(); }
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StringsDemo)
};
// This static object will register this demo type in a global list of demos..
static JuceDemoType<StringsDemo> demo ("40 XML & JSON");