- clean working copy for leaving SVN
This commit is contained in:
@@ -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");
|
||||
Reference in New Issue
Block a user