Browse Source

copied source from Gravity Beats

master
Joshua Moerman 8 years ago
parent
commit
61c889b0c6
5 changed files with 491 additions and 0 deletions
  1. +86
    -0
      Beat.h
  2. +99
    -0
      Math.h
  3. +70
    -0
      Scales.h
  4. +211
    -0
      Simulation.h
  5. +25
    -0
      UserInformation.h

+ 86
- 0
Beat.h View File

@ -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
- 0
Math.h View File

@ -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
- 0
Scales.h View File

@ -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
- 0
Simulation.h View File

@ -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
- 0
UserInformation.h View File

@ -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