00001 #include "System.h"
00002 #include "Damp.h"
00003 #include "LinearConstraint.h"
00004 #include "PointConstraint.h"
00005
00006 #include <limits>
00007 #include <future>
00008 #include <QDebug>
00009
00010 namespace Elve {
00011
00012 using namespace std;
00013
00014 System::System() : mGravity(75e4), mDamp(2)
00015 {
00016
00017 }
00018
00019 void System::tick(float dt, bool update)
00020 {
00021 static int count = 0;
00022
00023 mGravity.updateQuadTree();
00024
00025 #ifndef NO_THREADS
00026 int nthreads = 8;
00027 vector<future<void>> futures(nthreads);
00028 int splitsize = mPoints.size() / nthreads;
00029 size_t end = 0;
00030
00031 for(int i = 0; i < nthreads-1; i++) {
00032 end = (i+1)*splitsize;
00033 futures[i] = async(bind(&System::computeForces,this,i*splitsize,end));
00034 }
00035 futures[nthreads-1] = async(bind(&System::computeForces,this,end,mPoints.size()));
00036
00037 for(future<void>& f : futures) {
00038 f.get();
00039 }
00040 #else
00041 computeForces(0,mPoints.size());
00042 #endif
00043
00044 for(Point* m : mPoints) {
00045 m->tick(dt,update);
00046 }
00047 }
00048
00049 void System::debug(QPainter* p) const {
00050 mGravity.debug(p);
00051 }
00052
00053 void System::computeForces(size_t from,size_t until)
00054 {
00055 for(size_t i = from; i < until; i++) {
00056 mPoints[i]->resetForce();
00057 mPoints[i]->computeForce();
00058 }
00059 }
00060
00061 const QRectF& System::sizeHint() const {
00062 return mSizeHint;
00063 }
00064
00065 Point* System::point(const NodeID& id) {
00066 auto it = mPointsById.find(id);
00067 if(it != mPointsById.end()) {
00068 return it->second;
00069 } else {
00070 return addPoint(1,id,{0,0},3,FULL);
00071 }
00072 }
00073
00074 void System::setRepulsionForce(float f) {
00075 mGravity.setK(f);
00076 }
00077
00078 const PointsByID& System::pinnedPoints() const {
00079 return mPinnedPoints;
00080 }
00081
00082 const PointsByID& System::pointsByID() const {
00083 return mPointsById;
00084 }
00085
00086 NodePositions System::positions() const
00087 {
00088 NodePositions poss;
00089 for(const Point* p : mPoints) {
00090 poss[p->boundID()] = p->pos();
00091 }
00092 return poss;
00093 }
00094
00095 void System::clearMovables() {
00096 for(Point* p : mPoints) {
00097 p->clearMovables();
00098 }
00099 }
00100
00101 Point* System::addPoint(qreal mass, const NodeID &id, QVector2D pos, qreal damp, GravityMode g)
00102 {
00103 mDamp.setB(damp);
00104 Point* nm = nullptr;
00105 auto it = mPointsById.find(id);
00106 if(it != mPointsById.end()) {
00107 nm = it->second;
00108 nm->setMass(mass);
00109 }else {
00110 nm = new Point(mass,id,*this);
00111 mPoints.push_back(nm);
00112 mPointsById[id] = nm;
00113 }
00114 nm->setPos(pos);
00115
00116 if(g != NONE) {
00117 nm->addForce(&mGravity);
00118 if(g == FULL) {
00119 mGravity.addPoint(nm);
00120 }
00121 }
00122 nm->addForce(&mDamp);
00123 nm->addConstraint(&mBox);
00124 return nm;
00125 }
00126
00127 void System::pin(const NodeID& id, const QVector2D& pnt) {
00128 Point* p = point(id);
00129 mPinnedPoints[p->boundID()] = p;
00130 p->removePConstraints();
00131 addPConstrain(p,pnt);
00132 }
00133
00134 void System::unpin(const NodeID& id) {
00135 Point* p = point(id);
00136 p->removePConstraints();
00137 p->addConstraint(&mBox);
00138 mPinnedPoints.erase(id);
00139 }
00140
00141 void System::addSpring(unsigned i, unsigned j, qreal k, qreal l0)
00142 {
00143 Point* mi = mPoints.at(i);
00144 Point* mj = mPoints.at(j);
00145 addSpring(mi,mj,k,l0);
00146 }
00147
00148 void System::addSpring(Point* mi, Point* mj, qreal k, qreal l0)
00149 {
00150 Spring* force = new Spring(*mi,*mj,l0,k);
00151 mi->addForce(force);
00152 mj->addForce(force);
00153 mForces.push_back(force);
00154 }
00155 void System::clear()
00156 {
00157 for(Point* m : mPoints) {
00158 delete m;
00159 }
00160
00161 mPoints.clear();
00162 for(Force* f : mForces) {
00163 delete f;
00164 }
00165 mForces.clear();
00166 mGravity.clear();
00167 mPointsById.clear();
00168 mPinnedPoints.clear();
00169 }
00170
00171 const Point* System::nearest(const QVector2D& p) const
00172 {
00173 qreal dist = std::numeric_limits<qreal>::infinity();
00174 const Point* ml = mPoints.back();
00175 for(const Point* m : mPoints) {
00176 qreal r = (m->pos() - p).lengthSquared();
00177 if(r < dist) {
00178 dist = r;
00179 ml = m;
00180 }
00181 }
00182 return ml;
00183 }
00184
00185 void System::setSizeHint(const QRectF& rect) {
00186 mGSizeHint = rect.adjusted(-512,-512,512,512);
00187 switch(orientationHint()) {
00188 case LEFTRIGHT:
00189 case RIGHTLEFT:
00190 mSizeHint = QRectF(mGSizeHint.top(),mGSizeHint.left(),mGSizeHint.height(),mGSizeHint.width());
00191 break;
00192 case TOPDOWN:
00193 default:
00194 mSizeHint = mGSizeHint;
00195 break;
00196 }
00197
00198 mGravity.setQuadTreeBounds(mSizeHint);
00199 mBox.setBounds(mSizeHint);
00200 }
00201
00202 LinearConstraint::Dir _adaptDir(OrientationHint hint, LinearConstraint::Dir dir) {
00203 switch(hint) {
00204 case RIGHTLEFT:
00205 case LEFTRIGHT:
00206 return (LinearConstraint::Dir)((dir+1)%2);
00207 case TOPDOWN:
00208 default:
00209 return dir;
00210 }
00211 }
00212
00213 qreal _adaptPos(QVector2D transformPoint,LinearConstraint::Dir dir) {
00214 return dir == LinearConstraint::VERTICAL ? transformPoint.y() : transformPoint.x();
00215 }
00216
00217 void System::addVConstraint(Point* m, qreal height)
00218 {
00219 qreal hardn = 0.2;
00220 LinearConstraint::Dir newDir = _adaptDir(orientationHint(),LinearConstraint::VERTICAL);
00221 qreal tpos = _adaptPos(transformPoint({0,height}),newDir);
00222 Constraint* c = new LinearConstraint(tpos,hardn,newDir);
00223 m->addConstraint(c);
00224 mConstraints.push_back(c);
00225 }
00226
00227 void System::addHConstraint(Point* m, qreal pos) {
00228 qreal hardn = 0.2;
00229 Constraint* c = new LinearConstraint(transformPoint({pos,0}).x(),hardn,
00230 _adaptDir(orientationHint(),LinearConstraint::HORIZONTAL));
00231 m->addConstraint(c);
00232 mConstraints.push_back(c);
00233 }
00234
00235 QVector2D System::transformPoint(const QVector2D& p) const {
00236 switch(orientationHint()) {
00237 case TOPDOWN:
00238 return {p.x(), mGSizeHint.bottom() - (p.y() - mGSizeHint.top())};
00239 case LEFTRIGHT:
00240 return {mGSizeHint.bottom() - (p.y() - mGSizeHint.top()),p.x()};
00241 case RIGHTLEFT:
00242 return {p.y(),p.x()};
00243 default:
00244 return p;
00245 }
00246 }
00247
00248 void System::addForce(Point* m, Force* f) {
00249 mForces.push_back(f);
00250 m->addForce(f);
00251 }
00252
00253 void System::addPConstrain(Point* m, const QVector2D& p)
00254 {
00255 Constraint* pc = new PointConstraint(transformPoint(p));
00256 m->addConstraint(pc);
00257 mConstraints.push_back(pc);
00258 }
00259
00260 void System::setOrientationHint(OrientationHint hint) {
00261 mOrHint = hint;
00262 }
00263
00264 OrientationHint System::orientationHint() const {
00265 return mOrHint;
00266 }
00267
00268 size_t System::massCount() const
00269 {
00270 return mPoints.size();
00271 }
00272
00273 size_t System::forceCount() const
00274 {
00275 return mForces.size();
00276 }
00277
00278 System::~System()
00279 {
00280 clear();
00281 }
00282
00283 }