Browse Source

copied source from Gravity Beats

master
Joshua Moerman 12 years ago
parent
commit
61c889b0c6
  1. 86
      Beat.h
  2. 99
      Math.h
  3. 70
      Scales.h
  4. 211
      Simulation.h
  5. 25
      UserInformation.h

86
Beat.h

@ -0,0 +1,86 @@
//
// Beat.h
// GravityBeats
//
// Created by Joshua Moerman on 1/9/13.
// Copyright (c) 2013 Vadovas. All rights reserved.
//
#ifndef GravityBeats_Beat_h
#define GravityBeats_Beat_h
#include <vector>
#include <cmath>
#include "UserInformation.h"
template <typename Information>
struct Note : public UserInformation<Information>{
// see http://en.wikipedia.org/wiki/Note_value
enum Speed {
kWholeNote =1, // 1 in one measure
kHalfNote =2, // 2 in one measure
kTriplet =3, // 3 in one measure
kQuarterNote =4, // 4
kHalfTriplet =6, // 6
kEighthNote =8 // 8
} speed;
int progress = -2;
template <typename... S>
Note(Speed speed, S... args)
: UserInformation<Information>(args...)
, speed(speed)
{}
// we could return how much it is off, to give the balls the right initial position and speed.
bool update(float time_in_measure){
int new_progress = std::floor(time_in_measure*speed);
if(new_progress != progress){
if(progress == -2){
progress = new_progress;
return false;
} else {
progress = new_progress;
return true;
}
}
return false;
}
void reset(){
progress = -1;
}
};
template <typename NoteInformation>
struct Beat {
float total_length{4.0}; // 60 bpm
float time{-0.1};
std::vector<Note<NoteInformation>> notes;
std::vector<NoteInformation> update(float dt){
std::vector<NoteInformation> ret;
time += dt;
// not needed, but keeps the floats small
// also usefull when we want to change bpm
if(time > total_length){
time -= total_length;
for(auto& n : notes){
n.reset();
}
}
for(auto& n : notes){
if(n.update(time / total_length)){
ret.push_back(n.information);
}
}
return ret;
}
};
#endif

99
Math.h

@ -0,0 +1,99 @@
//
// Math.h
// GravityBeats
//
// Created by Joshua Moerman on 1/12/13.
// Copyright (c) 2013 Vadovas. All rights reserved.
//
#ifndef GravityBeats_Math_h
#define GravityBeats_Math_h
#include <cmath>
namespace math {
struct Vec2 {
float x, y;
Vec2& operator *= (float rh){
x *= rh;
y *= rh;
return *this;
}
Vec2& operator+= (Vec2 const & rh){
x += rh.x;
y += rh.y;
return *this;
}
float sqr_length() const {
return x*x + y*y;
}
float length() const {
return std::sqrt(sqr_length());
}
Vec2& normalize(){
const float l = length();
if(l == 0.0f) { return *this; }
x /= l;
y /= l;
return *this;
}
};
inline Vec2 operator*(float lh, Vec2 rh){
rh *= lh;
return rh;
}
inline Vec2 operator+(Vec2 lh, Vec2 const & rh){
return lh += rh;
}
inline Vec2 operator-(Vec2 lh, Vec2 const & rh){
return lh += -1.0*rh;
}
inline float dot(Vec2 const & lh, Vec2 const & rh){
return lh.x*rh.x + lh.y*rh.y;
}
inline Vec2 rotate_ccw(Vec2 v){
std::swap(v.x, v.y);
v.x *= -1.0;
return v;
}
inline Vec2 normalize(Vec2 v){
return v.normalize();
}
inline float distance_point_point(Vec2 p1, Vec2 p2){
return (p2 - p1).length();
}
// p point, [v,w] line segment
inline float distance_point_line(Vec2 p, Vec2 v, Vec2 w){
// http://stackoverflow.com/questions/849211/shortest-distance-between-a-point-and-a-line-segment
const float l2 = (v-w).sqr_length();
if (l2 == 0.0) return distance_point_point(p, v);
const float t = dot(p - v, w - v) / l2;
if (t < 0.0) return distance_point_point(p, v);
else if (t > 1.0) return distance_point_point(p, w);
auto projection = v + t * (w - v);
return distance_point_point(p, projection);
}
template <typename T>
constexpr T clamp(T input, T min, T max){
return input > max ? max :
input < min ? min : input;
}
}
#endif

70
Scales.h

@ -0,0 +1,70 @@
//
// Scales.h
// GravityBeats
//
// Created by Joshua Moerman on 1/25/13.
// Copyright (c) 2013 Vadovas. All rights reserved.
//
#ifndef GravityBeats_Scales_h
#define GravityBeats_Scales_h
#include <vector>
#include <cmath>
#include <iostream>
#include <fstream>
#include <stdexcept>
// http://www.phys.unsw.edu.au/jw/notes.html
inline float pitch_for_midi_note(int note){
// we say 440hz is 1 in our units
float exponent = (note - 69) / 12.0f;
return std::pow(2.0f, exponent);
}
struct Scale {
std::vector<int> notes;
int note_for_length(float length){
// determine note
length /= 200.0;
float note = -std::log(length) / std::log(2.0f) * 12.0f + 69.0f;
// determine note in scale
auto it = notes.begin();
while(*it < note && it != notes.end()){
++it;
}
// determine closest note in scale
if(it == notes.begin()){
return *it;
} else {
auto it2 = it - 1;
if(std::abs(*it - note) > std::abs(*it2 - note)){
return *it2;
} else {
return *it;
}
}
}
static Scale load_from_file(std::string filename){
std::ifstream file(filename);
if(!file) throw std::runtime_error("Couldn't open file " + filename);
Scale scale;
int note = 0;
while (file >> note) {
scale.notes.push_back(note);
}
std::sort(scale.notes.begin(), scale.notes.end());
return scale;
}
};
#endif

211
Simulation.h

@ -0,0 +1,211 @@
//
// Simulation.h
// GravityBeats
//
// Created by Joshua Moerman on 1/5/13.
// Copyright (c) 2013 Vadovas. All rights reserved.
//
#ifndef GravityBeats_Simulation_h
#define GravityBeats_Simulation_h
#include <vector>
#include <cmath>
#include "Math.h"
#include "UserInformation.h"
// TODO: if not needed, remove forcefield
namespace simulation {
template <typename Info>
struct Ball : public UserInformation<Info> {
math::Vec2 position{0.0, 0.0};
math::Vec2 speed{0.0, 0.0};
template <typename... S>
Ball(float x, float y, float dx, float dy, S... args)
: UserInformation<Info>(args...)
, position{x, y}
, speed{dx, dy}
{}
};
enum LineKind {
kFallThrough,
kOneWay
};
template <typename Info>
struct Line : public UserInformation<Info> {
math::Vec2 starting_point;
math::Vec2 end_point;
math::Vec2 normal;
float sqr_length;
LineKind line_kind;
template <typename... S>
Line(math::Vec2 starting_point, math::Vec2 end_point, LineKind line_kind, S... args)
: UserInformation<Info>(args...)
, starting_point(starting_point)
, end_point(end_point)
, line_kind(line_kind)
{
update_normal_and_length();
}
void update_normal_and_length(){
auto dir = end_point - starting_point;
normal = normalize(rotate_ccw(dir));
sqr_length = dir.sqr_length();
}
};
template <typename BallInfo, typename LineInfo>
struct CollisionData {
Ball<BallInfo> ball;
Line<LineInfo> line;
// For this trick I need inheriting ctors... Which we dont have yet...
//struct : public UserInformation<BallInfo> {} ball_information;
//struct : public UserInformation<LineInfo> {} line_information;
CollisionData(Ball<BallInfo> const & b, Line<LineInfo> const & l)
: ball(b), line(l)
{}
};
struct Bounds{
float xmin{0};
float xmax{1280};
float ymin{0};
float ymax{800};
};
template <typename BallInfo, typename LineInfo>
struct Simulation {
typedef Ball<BallInfo> ball_type;
typedef Line<LineInfo> line_type;
typedef CollisionData<BallInfo, LineInfo> collision_type;
std::vector<ball_type> balls;
std::vector<line_type> lines;
std::vector<collision_type> collisions_in_update;
Bounds bounds;
float collision_timer{0.0};
int total_collisions{0};
int collisions{0};
int collisions_per_second{0};
Simulation(){
balls.reserve(500);
lines.reserve(10);
collisions_in_update.reserve(1000);
}
std::vector<collision_type> update(float dt){
collisions_in_update.clear();
// move balls
for(auto& b : balls){
b.speed += dt * math::Vec2{0.0f, 50.0f};
float rest_time = collide(b, lines, dt);
b.position += rest_time*b.speed;
}
// count collisions per second (per half second)
collision_timer += dt;
if(collision_timer > 0.5){
collision_timer -= 0.5;
collisions_per_second = collisions * 2;
collisions = 0.0;
}
// remove out-of-scene balls
for(auto it = balls.begin(); it != balls.end();){
ball_type & b = *it;
if(b.position.y > bounds.ymax || b.position.x < bounds.xmin || b.position.x > bounds.xmax){
it = balls.erase(it);
} else {
++it;
}
}
return collisions_in_update;
}
/*
Part about the collisions
*/
float collision_time(ball_type const & b, line_type const & l){
// -dot(b.pos - l.start) / ...
return dot(l.starting_point - b.position, l.normal) / dot(b.speed, l.normal);
}
std::pair<bool, math::Vec2> check_collision(ball_type const & b, line_type const & l, float dt){
auto t = collision_time(b, l);
// does it collide within the given time? AND does it go the right way?
if(0 <= t && t <= dt){
if(l.line_kind == kOneWay && dot(b.speed, l.normal) > 0.0){
return {false, {0,0}};
}
// does it collide with the finite line?
auto collision = b.position + t*b.speed;
auto on_line = dot(collision - l.starting_point, l.end_point - l.starting_point);
if(0 <= on_line && on_line <= l.sqr_length){
return {true, collision};
} else {
return {false, {0,0}};
}
} else {
return {false, {0,0}};
}
}
float collide(ball_type& b, std::vector<line_type> const & lines, float dt, line_type const * ignore = nullptr){
line_type const * closest_line = nullptr;
math::Vec2 collision{0.0, 0.0};
float closeness = 100.0f;
for(auto& l : lines){
if(&l == ignore) continue;
auto ret = check_collision(b, l, dt);
if(ret.first){
auto t = collision_time(b, l);
if(0 <= t && t < closeness){
closest_line = &l;
closeness = t;
collision = ret.second;
}
}
}
if(closest_line == nullptr){
return dt;
} else {
++total_collisions;
++collisions;
b.position = collision;
auto const & l = *closest_line;
collisions_in_update.emplace_back(b, l);
if(l.line_kind != kFallThrough){
auto b1 = -dot(b.speed, l.normal) * l.normal;
b.speed = 2.0*b1 + b.speed;
}
return collide(b, lines, dt - closeness, closest_line);
}
}
};
}
#endif

25
UserInformation.h

@ -0,0 +1,25 @@
//
// UserInformation.h
// GravityBeats
//
// Created by Joshua Moerman on 2/19/13.
// Copyright (c) 2013 Vadovas. All rights reserved.
//
#ifndef GravityBeats_UserInformation_h
#define GravityBeats_UserInformation_h
template <typename T>
struct UserInformation {
template <typename... S>
UserInformation(S... args)
: information(args...)
{}
T information;
};
template <>
struct UserInformation<void> {};
#endif