<commit_before>/*
This source file is part of pairOculus, a student project aiming at creating a
simple 3D multiplayer game for the Oculus Rift.

Repository can be found here : https://github.com/Target6/pairOculus

Copyright (c) 2013 Zykino

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#include "LocalMap.hpp"

LocalMap::LocalMap(
	OgreBulletDynamics::DynamicsWorld *world,
	OgrePlayerList *playerList,
	BombManager *bombManager,
	unsigned int height,
	unsigned int width,
	time_t seed
):
	Map(height, width, seed),
	mWorld(world),
	mBodies(0),
	mPlayerList(playerList),
	mBombManager(bombManager)
{
	mBombManager->setExplosionListener(this);
}

LocalMap::~LocalMap(){
	mCrumblingBlocks.clear();
}

void LocalMap::generate() {

	using namespace OgreBulletCollisions;

	Ogre::SceneManager *sceneMgr = mWorld->getSceneManager();

	FloorPanel* fp = new FloorPanel(
		sceneMgr->createManualObject("floor"),
		mScale*mHeight,
		mScale*mWidth
	);
	BlockFactory* bf = new BlockFactory(sceneMgr);

	CollisionShape *Shape;
	Shape = new StaticPlaneCollisionShape(Ogre::Vector3(0,1,0), 0);
	OgreBulletDynamics::RigidBody *planeBody;
	planeBody = new OgreBulletDynamics::RigidBody("MapFloor", mWorld);
	planeBody->setStaticShape(Shape, 0.6f, 0.6f);

	SceneNode *floorNode = sceneMgr->getRootSceneNode()->createChildSceneNode("floorNode");
	floorNode->attachObject(fp->GetManualFloor());

	Ogre::ManualObject *object;

	Ogre::Vector3 size(mScale/2, mScale/6, mScale/2);
	Ogre::Vector3 pos;
	Ogre::SceneNode *node;

	BoxCollisionShape *boxShape;

	stringstream genName;

	mBodies = new OgreBulletDynamics::RigidBody **[mHeight];
	for (unsigned int i = 0; i < mHeight; i++) {

		mBodies[i] = new OgreBulletDynamics::RigidBody *[mWidth];
		for (unsigned int j = 0; j < mWidth; j++) {

			object = bf->createBlock(mMap[i][j], i, j, mScale);
			mBodies[i][j] = 0;

			if (object != NULL){

				genName << i << "_" << j;
				node = sceneMgr->getRootSceneNode()->createChildSceneNode("node_" + genName.str());
				node->attachObject(object);

				boxShape = new BoxCollisionShape(size);
				mBodies[i][j] = new OgreBulletDynamics::RigidBody("box_" + genName.str(), mWorld);
				genName.str("");
				genName.clear();

				pos = Ogre::Vector3(
					i*mScale + mScale/2,
					mScale/6,
					j*mScale + mScale/2
				);

				mBodies[i][j]->setStaticShape(
					node,
					boxShape,
					0.6f,					// dynamic body restitution
					0.6f,					// dynamic body friction
					pos
				);

			}

		}

	}

	Entity *entity = mWorld->getSceneManager()->createEntity("bomb.mesh");
	node = sceneMgr->getRootSceneNode()->createChildSceneNode();
	node->setPosition(Ogre::Vector3(0, mScale/2, 0));
	node->attachObject(entity);
	node->scale(0.1, 0.1, 0.1);

	Ogre::Light* light = sceneMgr->createLight("light");
	light->setPosition(16, 80, 16);

}

Ogre::Vector3 LocalMap::getMapCenter(){
	return Ogre::Vector3(mHeight * (mScale+0.1f), 0, mWidth * (mScale+0.1f));
}

bool LocalMap::bombExploded(Ogre::Vector3 position, int range){
	createExplosion(position, range);
	return true;
}

void LocalMap::frameRenderingQueued(const Ogre::FrameEvent &evt){

	Ogre::Vector3 currentVelocity(0, -4 * mScale * evt.timeSinceLastFrame, 0);

	for(unsigned int i = 0; i < mCrumblingBlocks.size(); i++)
		mCrumblingBlocks[i]->translate(currentVelocity);

	while(
		mCrumblingBlocks.size() > 0 &&
		mCrumblingBlocks.front()->getPosition().y < -mScale/2
	){
			mWorld->getSceneManager()->destroyManualObject(
				mCrumblingBlocks.front()->getAttachedObject(0)->getName()
			);
			mCrumblingBlocks.pop_front();
	}

}

int LocalMap::getRow(Ogre::Vector3 pos){

	if(pos.x < 0 || pos.x >= mHeight * mScale)
		return -1;

	return pos.x / mScale;

}

int LocalMap::getCol(Ogre::Vector3 pos){

	if(pos.z < 0 || pos.x >= mWidth * mScale)
		return -1;

	return pos.z / mScale;

}

void LocalMap::createExplosion(Ogre::Vector3 pos, int range){

	if(pos.y < 0 || pos.y >= mScale/3) //above or below the map
		return;

	int row = getRow(pos), col = getCol(pos), i;

	if(row == -1 || col == -1) //outside the map
		return;

	if(!isUnbreakable(row, col)){	//if not in an unbreakable block

		mMap[row][col] = EMPTY;
		searchAndDestroyObjects(row, col);

		for(i = 1; range - i > 0 && !isUnbreakable(row + i, col); i++){

			if(mMap[row + i][col] == EMPTY)
				searchAndDestroyObjects(row + i, col);

			if(isBreakable(row + i, col)){
				destroyBlock(row + i, col);
				i = range;
			}
			else
				destroyBlock(row + i, col);
		}

		for(i = -1; range + i > 0 && !isUnbreakable(row + i, col); i--){

			if(mMap[row + i][col] == EMPTY)
				searchAndDestroyObjects(row + i, col);

			if(isBreakable(row + i, col)){
				destroyBlock(row + i, col);
				i = -range;
			}
			else
				destroyBlock(row + i, col);
		}

		for(i = 1; range - i > 0 && !isUnbreakable(row, col + i); i++){

			if(mMap[row][col + i] == EMPTY)
				searchAndDestroyObjects(row, col + i);

			if(isBreakable(row, col + i)){
				destroyBlock(row, col + i);
				i = range;
			}
			else
				destroyBlock(row, col + i);
		}

		for(i = -1; range + i > 0 && !isUnbreakable(row, col + i); i--){

			if(mMap[row][col + i] == EMPTY)
				searchAndDestroyObjects(row, col + i);

			if(isBreakable(row, col + i)){
				destroyBlock(row, col + i);
				i = -range;
			}
			else
				destroyBlock(row, col + i);
		}

	}

}

void LocalMap::destroyBlock(unsigned int i, unsigned int j){

	stringstream genName;

	mMap[i][j] = EMPTY;

	if(mBodies[i][j] != 0){

		mCrumblingBlocks.push_back(mBodies[i][j]->getSceneNode());
		delete mBodies[i][j];
		mBodies[i][j] = 0;

	}

}

void LocalMap::searchAndDestroyObjects(int row, int col){

	double topBound = (row+1) * mScale;
	double bottomBound = row * mScale;
	double leftBound = col * mScale;
	double rightBound = (col+1) * mScale;
	double upperBound = mScale;
	double lowerBound = 0;

	for(unsigned int i = 0; i < mPlayerList->size(); i++)
		if(
			(*mPlayerList)[i]->getX() >= bottomBound &&
			(*mPlayerList)[i]->getX() < topBound &&
			(*mPlayerList)[i]->getZ() >= leftBound &&
			(*mPlayerList)[i]->getZ() < rightBound &&
			(*mPlayerList)[i]->getY() >= lowerBound &&
			(*mPlayerList)[i]->getY() < upperBound
		)
			(*mPlayerList)[i]->die();

	for(unsigned int i = 0; i < mBombManager->size(); i++)
		if(
			(*mBombManager)[i]->getPosition().x >= bottomBound &&
			(*mBombManager)[i]->getPosition().x < topBound &&
			(*mBombManager)[i]->getPosition().z >= leftBound &&
			(*mBombManager)[i]->getPosition().z < rightBound &&
			(*mBombManager)[i]->getPosition().y >= lowerBound &&
			(*mBombManager)[i]->getPosition().y < upperBound
		)
			(*mBombManager)[i]->detonate();

}
<commit_msg>Removed the bomb in the corner of the map<commit_after>/*
This source file is part of pairOculus, a student project aiming at creating a
simple 3D multiplayer game for the Oculus Rift.

Repository can be found here : https://github.com/Target6/pairOculus

Copyright (c) 2013 Zykino

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#include "LocalMap.hpp"

LocalMap::LocalMap(
	OgreBulletDynamics::DynamicsWorld *world,
	OgrePlayerList *playerList,
	BombManager *bombManager,
	unsigned int height,
	unsigned int width,
	time_t seed
):
	Map(height, width, seed),
	mWorld(world),
	mBodies(0),
	mPlayerList(playerList),
	mBombManager(bombManager)
{
	mBombManager->setExplosionListener(this);
}

LocalMap::~LocalMap(){
	mCrumblingBlocks.clear();
}

void LocalMap::generate() {

	using namespace OgreBulletCollisions;

	Ogre::SceneManager *sceneMgr = mWorld->getSceneManager();

	FloorPanel* fp = new FloorPanel(
		sceneMgr->createManualObject("floor"),
		mScale*mHeight,
		mScale*mWidth
	);
	BlockFactory* bf = new BlockFactory(sceneMgr);

	CollisionShape *Shape;
	Shape = new StaticPlaneCollisionShape(Ogre::Vector3(0,1,0), 0);
	OgreBulletDynamics::RigidBody *planeBody;
	planeBody = new OgreBulletDynamics::RigidBody("MapFloor", mWorld);
	planeBody->setStaticShape(Shape, 0.6f, 0.6f);

	SceneNode *floorNode = sceneMgr->getRootSceneNode()->createChildSceneNode("floorNode");
	floorNode->attachObject(fp->GetManualFloor());

	Ogre::ManualObject *object;

	Ogre::Vector3 size(mScale/2, mScale/6, mScale/2);
	Ogre::Vector3 pos;
	Ogre::SceneNode *node;

	BoxCollisionShape *boxShape;

	stringstream genName;

	mBodies = new OgreBulletDynamics::RigidBody **[mHeight];
	for (unsigned int i = 0; i < mHeight; i++) {

		mBodies[i] = new OgreBulletDynamics::RigidBody *[mWidth];
		for (unsigned int j = 0; j < mWidth; j++) {

			object = bf->createBlock(mMap[i][j], i, j, mScale);
			mBodies[i][j] = 0;

			if (object != NULL){

				genName << i << "_" << j;
				node = sceneMgr->getRootSceneNode()->createChildSceneNode("node_" + genName.str());
				node->attachObject(object);

				boxShape = new BoxCollisionShape(size);
				mBodies[i][j] = new OgreBulletDynamics::RigidBody("box_" + genName.str(), mWorld);
				genName.str("");
				genName.clear();

				pos = Ogre::Vector3(
					i*mScale + mScale/2,
					mScale/6,
					j*mScale + mScale/2
				);

				mBodies[i][j]->setStaticShape(
					node,
					boxShape,
					0.6f,					// dynamic body restitution
					0.6f,					// dynamic body friction
					pos
				);

			}

		}

	}

	Ogre::Light* light = sceneMgr->createLight("light");
	light->setPosition(16, 80, 16);

}

Ogre::Vector3 LocalMap::getMapCenter(){
	return Ogre::Vector3(mHeight * (mScale+0.1f), 0, mWidth * (mScale+0.1f));
}

bool LocalMap::bombExploded(Ogre::Vector3 position, int range){
	createExplosion(position, range);
	return true;
}

void LocalMap::frameRenderingQueued(const Ogre::FrameEvent &evt){

	Ogre::Vector3 currentVelocity(0, -4 * mScale * evt.timeSinceLastFrame, 0);

	for(unsigned int i = 0; i < mCrumblingBlocks.size(); i++)
		mCrumblingBlocks[i]->translate(currentVelocity);

	while(
		mCrumblingBlocks.size() > 0 &&
		mCrumblingBlocks.front()->getPosition().y < -mScale/2
	){
			mWorld->getSceneManager()->destroyManualObject(
				mCrumblingBlocks.front()->getAttachedObject(0)->getName()
			);
			mCrumblingBlocks.pop_front();
	}

}

int LocalMap::getRow(Ogre::Vector3 pos){

	if(pos.x < 0 || pos.x >= mHeight * mScale)
		return -1;

	return pos.x / mScale;

}

int LocalMap::getCol(Ogre::Vector3 pos){

	if(pos.z < 0 || pos.x >= mWidth * mScale)
		return -1;

	return pos.z / mScale;

}

void LocalMap::createExplosion(Ogre::Vector3 pos, int range){

	if(pos.y < 0 || pos.y >= mScale/3) //above or below the map
		return;

	int row = getRow(pos), col = getCol(pos), i;

	if(row == -1 || col == -1) //outside the map
		return;

	if(!isUnbreakable(row, col)){	//if not in an unbreakable block

		mMap[row][col] = EMPTY;
		searchAndDestroyObjects(row, col);

		for(i = 1; range - i > 0 && !isUnbreakable(row + i, col); i++){

			if(mMap[row + i][col] == EMPTY)
				searchAndDestroyObjects(row + i, col);

			if(isBreakable(row + i, col)){
				destroyBlock(row + i, col);
				i = range;
			}
			else
				destroyBlock(row + i, col);
		}

		for(i = -1; range + i > 0 && !isUnbreakable(row + i, col); i--){

			if(mMap[row + i][col] == EMPTY)
				searchAndDestroyObjects(row + i, col);

			if(isBreakable(row + i, col)){
				destroyBlock(row + i, col);
				i = -range;
			}
			else
				destroyBlock(row + i, col);
		}

		for(i = 1; range - i > 0 && !isUnbreakable(row, col + i); i++){

			if(mMap[row][col + i] == EMPTY)
				searchAndDestroyObjects(row, col + i);

			if(isBreakable(row, col + i)){
				destroyBlock(row, col + i);
				i = range;
			}
			else
				destroyBlock(row, col + i);
		}

		for(i = -1; range + i > 0 && !isUnbreakable(row, col + i); i--){

			if(mMap[row][col + i] == EMPTY)
				searchAndDestroyObjects(row, col + i);

			if(isBreakable(row, col + i)){
				destroyBlock(row, col + i);
				i = -range;
			}
			else
				destroyBlock(row, col + i);
		}

	}

}

void LocalMap::destroyBlock(unsigned int i, unsigned int j){

	stringstream genName;

	mMap[i][j] = EMPTY;

	if(mBodies[i][j] != 0){

		mCrumblingBlocks.push_back(mBodies[i][j]->getSceneNode());
		delete mBodies[i][j];
		mBodies[i][j] = 0;

	}

}

void LocalMap::searchAndDestroyObjects(int row, int col){

	double topBound = (row+1) * mScale;
	double bottomBound = row * mScale;
	double leftBound = col * mScale;
	double rightBound = (col+1) * mScale;
	double upperBound = mScale;
	double lowerBound = 0;

	for(unsigned int i = 0; i < mPlayerList->size(); i++)
		if(
			(*mPlayerList)[i]->getX() >= bottomBound &&
			(*mPlayerList)[i]->getX() < topBound &&
			(*mPlayerList)[i]->getZ() >= leftBound &&
			(*mPlayerList)[i]->getZ() < rightBound &&
			(*mPlayerList)[i]->getY() >= lowerBound &&
			(*mPlayerList)[i]->getY() < upperBound
		)
			(*mPlayerList)[i]->die();

	for(unsigned int i = 0; i < mBombManager->size(); i++)
		if(
			(*mBombManager)[i]->getPosition().x >= bottomBound &&
			(*mBombManager)[i]->getPosition().x < topBound &&
			(*mBombManager)[i]->getPosition().z >= leftBound &&
			(*mBombManager)[i]->getPosition().z < rightBound &&
			(*mBombManager)[i]->getPosition().y >= lowerBound &&
			(*mBombManager)[i]->getPosition().y < upperBound
		)
			(*mBombManager)[i]->detonate();

}
<|endoftext|>


<commit_before>#include <unistd.h>
#include <getopt.h>
#include <BGL.hh>
#include "Defaults.hh"
#include "Stopwatch.hh"
#include "SlicingContext.hh"
#include "Operation.hh"
#include "OpQueue.hh"
#include "CarveOp.hh"
#include "InfillOp.hh"
#include "InsetOp.hh"
#include "RaftOp.hh"
#include "SvgDumpOp.hh"
#include "PathFinderOp.hh"
#include "GCodeExportOp.hh"

static string inFileName  = "";
static string material    = "ABS";
static double scaling      = 1.0f;
static double rotation     = 0.0f;
static bool   doCenter     = true;
static bool   doDumpSVG    = false;
static int    threadcount  = DEFAULT_WORKER_THREADS;

enum ExportTypes {
    NONE,
    GCODE,
    S3G
} exportType = GCODE;

void usage(const char* arg0, SlicingContext& ctx)
{
    fprintf(stderr, "Usage: %s [OPTIONS] FILE\n", arg0);
    fprintf(stderr, "Or   : %s -m MATERIAL [OPTIONS] FILE\n", arg0);
    fprintf(stderr, "\t[-m STRING]   Extruded material. (default ABS)\n");
    fprintf(stderr, "\t[-f FLOAT]    Filament diameter. (default %.1f mm)\n", ctx.filamentDiameter);
    fprintf(stderr, "\t[-F FLOAT]    Filament feedrate. (default %.3f mm/s)\n", ctx.filamentFeedRate);
    fprintf(stderr, "\t[-i FLOAT]    Infill density. (default %.2f)\n", ctx.infillDensity);
    fprintf(stderr, "\t[-l FLOAT]    Slicing layer thickness. (default %.2f mm)\n", ctx.layerThickness);
    fprintf(stderr, "\t[-p INT]      Number of perimeter shell layers. (default %d)\n", ctx.perimeterShells);
    fprintf(stderr, "\t[-r INT]      Number of Raft layers. (default %d)\n", ctx.raftLayers);
    fprintf(stderr, "\t[-w FLOAT]    Extrusion width over thickness ratio. (default %.2f)\n", ctx.widthOverHeightRatio);
    fprintf(stderr, "\t[-c]          DON'T center model on platform before slicing.\n");
    fprintf(stderr, "\t[-s FLOAT]    Scale model.  (default %.4gx)\n", scaling);
    fprintf(stderr, "\t[-R FLOAT]    Rotate model about Z.  (default %.4g deg)\n", rotation);
    fprintf(stderr, "\t[-z FLOAT]    Slice model only at the given Z level.\n");
    fprintf(stderr, "\t[-d PREFIX]   Dump layers to SVG files with names like PREFIX-12.34.svg.\n");
    fprintf(stderr, "\t[-t INT]      Number of threads to slice with. (default %d)\n", threadcount);
    exit(-1);
}

int main (int argc, char * const argv[])
{
    Stopwatch stopwatch;
    stopwatch.start();

    SlicingContext ctx;
    char buf[512];

    int ch;
    const char *progName = argv[0];
    const char * shortopts = "?cd:f:F:hi:l:m:o:p:r:t:w:R:S:";
    static struct option longopts[] = {
	{"material", required_argument, NULL, 'm'},
	{"diameter", required_argument, NULL, 'f'},
	{"feedrate", required_argument, NULL, 'F'},
	{"infill", required_argument, NULL, 'i'},
	{"layer", required_argument, NULL, 'l'},
	{"shells", required_argument, NULL, 'p'},
	{"ratio", required_argument, NULL, 'w'},
	{"raftlayers", required_argument, NULL, 'R'},
	{"nocenter", required_argument, NULL, 'c'},
	{"scale", required_argument, NULL, 'S'},
	{"rotatex", required_argument, NULL, 'R'},
	{"dumpprefix", required_argument, NULL, 'd'},
	{"threads", required_argument, NULL, 't'},
	{0, 0, 0, 0}
    };

    // We need to get the command-line argument for Material first.
    while ((ch = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
        if (ch == 'm') {
	    material = optarg;
	}
    }

    // Load saved settings for the given material, if any.
    snprintf(buf, sizeof(buf), "~/.mandoline/materials/%.128s.conf", material.c_str());
    ctx.loadDefaultsFromFile(buf);

    // Process remainder of command-line arguments.
    opterr = optind = 1;

#if HAVE_DECL_OPTRESET
    optreset = 1;
#endif

    while ((ch = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
        switch (ch) {
        case 'c':
            doCenter = false;
            break;
        case 'd':
            doDumpSVG = true;
            ctx.dumpPrefix = optarg;
            break;
        case 'f':
            ctx.filamentDiameter = atof(optarg);
            break;
        case 'F':
            ctx.filamentFeedRate = atof(optarg);
            break;
        case 'i':
            ctx.infillDensity = atof(optarg);
            break;
        case 'l':
            ctx.layerThickness = atof(optarg);
            break;
        case 'p':
            ctx.perimeterShells = atoi(optarg);
            break;
        case 'w':
            ctx.widthOverHeightRatio = atof(optarg);
            break;
        case 'r':
            ctx.raftLayers = atoi(optarg);
            break;
        case 'R':
            rotation = atof(optarg);
            break;
        case 'S':
            scaling = atof(optarg);
            break;
        case 't':
	    {
		int cnt = atoi(optarg);
		if (cnt < 1) {
		    fprintf(stderr, "Error: Thread count cannot be less than 1.\n");
		}
		threadcount = cnt;
	    }
            break;
        case 'm':
            // We already parsed this one out.  Ignore.
            break;
        case '?':
        case 'h':
        default:
            usage(progName, ctx);
        }
    }
    argc -= optind;
    argv += optind;
    if (argc > 0) {
        inFileName = string(argv[0]);
    }
    if (inFileName.length() < 1) {
        usage(progName, ctx);
    }

    // Load the model from the file.
    BGL::Mesh3d &mesh = ctx.mesh;
    mesh.loadFromSTLFile(inFileName.c_str());
    printf("Found %d faces.\n", mesh.size());
    stopwatch.checkpoint("Model loaded from file");
    printf("Model Bounds = (%.2f, %.2f, %.2f) to (%.2f, %.2f, %.2f)\n", mesh.minX, mesh.minY, mesh.minZ, mesh.maxX, mesh.maxY, mesh.maxZ);

    // Scale the model if requested.
    if (scaling != 1.0f) {
        printf("Scaling model by %.4gx\n", scaling);
        mesh.scale(scaling);
    }

    // Rotate the model if requested.
    if (rotation != 0.0f) {
        printf("Rotating model by %.4g degrees\n", rotation);
        mesh.rotateZ(rotation*M_PI/180.0f);
    }

    // Center the model.
    if (doCenter) {
        printf("Centering model on X=0, Y=0  Placing bottom at Z=0.\n");
        mesh.translateToCenterOfPlatform();
    }

    if (scaling != 1.0f || rotation != 0.0f || doCenter) {
	printf("New Bounds = (%.2f, %.2f, %.2f) to (%.2f, %.2f, %.2f)\n", mesh.minX, mesh.minY, mesh.minZ, mesh.maxX, mesh.maxY, mesh.maxZ);
        stopwatch.checkpoint("Transformed");
    }

    // Recalculate SVG offsets.
    ctx.calculateSvgOffsets();

    // Calculate first and last layer Zs
    printf("Layer Thickness=%.4g\n", ctx.layerThickness);
    double halfLayer = ctx.layerThickness/2.0;
    double z = halfLayer;
    double topZ = mesh.maxZ;

    printf("Extrusion Width=%.4g\n", ctx.standardExtrusionWidth());

    // Set up Operations Queue and threadpool.
    OpQueue opQ;
    opQ.setMaxConcurrentOperationCount(threadcount);

    // Carve model to find layer outlines
    while (z < topZ) {
        CarveOp* op = new CarveOp(&ctx, ctx.allocSlice(z+halfLayer), z);
	opQ.addOperation(op);
        z += ctx.layerThickness;
    }
    opQ.waitUntilAllOperationsAreFinished();
    stopwatch.checkpoint("Carved");

    list<CarvedSlice>::iterator it;

    // Inset each level's carved region
    for (it = ctx.slices.begin(); it != ctx.slices.end(); it++) {
        InsetOp* op = new InsetOp(&ctx, &(*it));
	opQ.addOperation(op);
    }
    opQ.waitUntilAllOperationsAreFinished();
    stopwatch.checkpoint("Inset");

    // Infill each level's carved region
    for (it = ctx.slices.begin(); it != ctx.slices.end(); it++) {
        InfillOp* op = new InfillOp(&ctx, &(*it));
	opQ.addOperation(op);
    }
    opQ.waitUntilAllOperationsAreFinished();
    stopwatch.checkpoint("Infilled");

    // Create the raft. This isn't easily parallelizable.
    if (ctx.raftLayers > 0) {
	RaftOp* raftOp = new RaftOp(&ctx);
	opQ.addOperation(raftOp);
	opQ.waitUntilAllOperationsAreFinished();
	delete raftOp;
	stopwatch.checkpoint("Rafted");
    }

    // Find optimized path.
    PathFinderOp* pathOp = new PathFinderOp(&ctx);
    opQ.addOperation(pathOp);
    opQ.waitUntilAllOperationsAreFinished();
    delete pathOp;
    stopwatch.checkpoint("Path Optimized");

    // Optionally dump to SVG
    if (doDumpSVG) {
	for (it = ctx.slices.begin(); it != ctx.slices.end(); it++) {
	    SvgDumpOp* op = new SvgDumpOp(&ctx, &(*it));
	    opQ.addOperation(op);
        }
	opQ.waitUntilAllOperationsAreFinished();
        stopwatch.checkpoint("Dumped to SVG");
    }

    // Export toolpaths to apropriate output format.
    Operation* expOp = NULL;
    switch (exportType) {
    case GCODE:
        expOp = new GCodeExportOp(&ctx);
        break;
    default:
        break;
    }
    if (expOp) {
	opQ.addOperation(expOp);
	opQ.waitUntilAllOperationsAreFinished();
	stopwatch.checkpoint("Exported");
    }

    // All done!
    stopwatch.finish();

    return 0;
}

<commit_msg>Fixed typo in help.<commit_after>#include <unistd.h>
#include <getopt.h>
#include <BGL.hh>
#include "Defaults.hh"
#include "Stopwatch.hh"
#include "SlicingContext.hh"
#include "Operation.hh"
#include "OpQueue.hh"
#include "CarveOp.hh"
#include "InfillOp.hh"
#include "InsetOp.hh"
#include "RaftOp.hh"
#include "SvgDumpOp.hh"
#include "PathFinderOp.hh"
#include "GCodeExportOp.hh"

static string inFileName  = "";
static string material    = "ABS";
static double scaling      = 1.0f;
static double rotation     = 0.0f;
static bool   doCenter     = true;
static bool   doDumpSVG    = false;
static int    threadcount  = DEFAULT_WORKER_THREADS;

enum ExportTypes {
    NONE,
    GCODE,
    S3G
} exportType = GCODE;

void usage(const char* arg0, SlicingContext& ctx)
{
    fprintf(stderr, "Usage: %s [OPTIONS] FILE\n", arg0);
    fprintf(stderr, "Or   : %s -m MATERIAL [OPTIONS] FILE\n", arg0);
    fprintf(stderr, "\t[-m STRING]   Extruded material. (default ABS)\n");
    fprintf(stderr, "\t[-f FLOAT]    Filament diameter. (default %.1f mm)\n", ctx.filamentDiameter);
    fprintf(stderr, "\t[-F FLOAT]    Filament feedrate. (default %.3f mm/s)\n", ctx.filamentFeedRate);
    fprintf(stderr, "\t[-i FLOAT]    Infill density. (default %.2f)\n", ctx.infillDensity);
    fprintf(stderr, "\t[-l FLOAT]    Slicing layer thickness. (default %.2f mm)\n", ctx.layerThickness);
    fprintf(stderr, "\t[-p INT]      Number of perimeter shell layers. (default %d)\n", ctx.perimeterShells);
    fprintf(stderr, "\t[-r INT]      Number of Raft layers. (default %d)\n", ctx.raftLayers);
    fprintf(stderr, "\t[-w FLOAT]    Extrusion width over thickness ratio. (default %.2f)\n", ctx.widthOverHeightRatio);
    fprintf(stderr, "\t[-c]          DON'T center model on platform before slicing.\n");
    fprintf(stderr, "\t[-S FLOAT]    Scale model.  (default %.4gx)\n", scaling);
    fprintf(stderr, "\t[-R FLOAT]    Rotate model about Z.  (default %.4g deg)\n", rotation);
    fprintf(stderr, "\t[-d PREFIX]   Dump layers to SVG files with names like PREFIX-12.34.svg.\n");
    fprintf(stderr, "\t[-t INT]      Number of threads to slice with. (default %d)\n", threadcount);
    exit(-1);
}

int main (int argc, char * const argv[])
{
    Stopwatch stopwatch;
    stopwatch.start();

    SlicingContext ctx;
    char buf[512];

    int ch;
    const char *progName = argv[0];
    const char * shortopts = "?cd:f:F:hi:l:m:o:p:r:t:w:R:S:";
    static struct option longopts[] = {
	{"material", required_argument, NULL, 'm'},
	{"diameter", required_argument, NULL, 'f'},
	{"feedrate", required_argument, NULL, 'F'},
	{"infill", required_argument, NULL, 'i'},
	{"layer", required_argument, NULL, 'l'},
	{"shells", required_argument, NULL, 'p'},
	{"ratio", required_argument, NULL, 'w'},
	{"raftlayers", required_argument, NULL, 'r'},
	{"nocenter", required_argument, NULL, 'c'},
	{"scale", required_argument, NULL, 'S'},
	{"rotatex", required_argument, NULL, 'R'},
	{"dumpprefix", required_argument, NULL, 'd'},
	{"threads", required_argument, NULL, 't'},
	{0, 0, 0, 0}
    };

    // We need to get the command-line argument for Material first.
    while ((ch = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
        if (ch == 'm') {
	    material = optarg;
	}
    }

    // Load saved settings for the given material, if any.
    snprintf(buf, sizeof(buf), "~/.mandoline/materials/%.128s.conf", material.c_str());
    ctx.loadDefaultsFromFile(buf);

    // Process remainder of command-line arguments.
    opterr = optind = 1;

#if HAVE_DECL_OPTRESET
    optreset = 1;
#endif

    while ((ch = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
        switch (ch) {
        case 'c':
            doCenter = false;
            break;
        case 'd':
            doDumpSVG = true;
            ctx.dumpPrefix = optarg;
            break;
        case 'f':
            ctx.filamentDiameter = atof(optarg);
            break;
        case 'F':
            ctx.filamentFeedRate = atof(optarg);
            break;
        case 'i':
            ctx.infillDensity = atof(optarg);
            break;
        case 'l':
            ctx.layerThickness = atof(optarg);
            break;
        case 'p':
            ctx.perimeterShells = atoi(optarg);
            break;
        case 'w':
            ctx.widthOverHeightRatio = atof(optarg);
            break;
        case 'r':
            ctx.raftLayers = atoi(optarg);
            break;
        case 'R':
            rotation = atof(optarg);
            break;
        case 'S':
            scaling = atof(optarg);
            break;
        case 't':
	    {
		int cnt = atoi(optarg);
		if (cnt < 1) {
		    fprintf(stderr, "Error: Thread count cannot be less than 1.\n");
		}
		threadcount = cnt;
	    }
            break;
        case 'm':
            // We already parsed this one out.  Ignore.
            break;
        case '?':
        case 'h':
        default:
            usage(progName, ctx);
        }
    }
    argc -= optind;
    argv += optind;
    if (argc > 0) {
        inFileName = string(argv[0]);
    }
    if (inFileName.length() < 1) {
        usage(progName, ctx);
    }

    // Load the model from the file.
    BGL::Mesh3d &mesh = ctx.mesh;
    mesh.loadFromSTLFile(inFileName.c_str());
    printf("Found %d faces.\n", mesh.size());
    stopwatch.checkpoint("Model loaded from file");
    printf("Model Bounds = (%.2f, %.2f, %.2f) to (%.2f, %.2f, %.2f)\n", mesh.minX, mesh.minY, mesh.minZ, mesh.maxX, mesh.maxY, mesh.maxZ);

    // Scale the model if requested.
    if (scaling != 1.0f) {
        printf("Scaling model by %.4gx\n", scaling);
        mesh.scale(scaling);
    }

    // Rotate the model if requested.
    if (rotation != 0.0f) {
        printf("Rotating model by %.4g degrees\n", rotation);
        mesh.rotateZ(rotation*M_PI/180.0f);
    }

    // Center the model.
    if (doCenter) {
        printf("Centering model on X=0, Y=0  Placing bottom at Z=0.\n");
        mesh.translateToCenterOfPlatform();
    }

    if (scaling != 1.0f || rotation != 0.0f || doCenter) {
	printf("New Bounds = (%.2f, %.2f, %.2f) to (%.2f, %.2f, %.2f)\n", mesh.minX, mesh.minY, mesh.minZ, mesh.maxX, mesh.maxY, mesh.maxZ);
        stopwatch.checkpoint("Transformed");
    }

    // Recalculate SVG offsets.
    ctx.calculateSvgOffsets();

    // Calculate first and last layer Zs
    printf("Layer Thickness=%.4g\n", ctx.layerThickness);
    double halfLayer = ctx.layerThickness/2.0;
    double z = halfLayer;
    double topZ = mesh.maxZ;

    printf("Extrusion Width=%.4g\n", ctx.standardExtrusionWidth());

    // Set up Operations Queue and threadpool.
    OpQueue opQ;
    opQ.setMaxConcurrentOperationCount(threadcount);

    // Carve model to find layer outlines
    while (z < topZ) {
        CarveOp* op = new CarveOp(&ctx, ctx.allocSlice(z+halfLayer), z);
	opQ.addOperation(op);
        z += ctx.layerThickness;
    }
    opQ.waitUntilAllOperationsAreFinished();
    stopwatch.checkpoint("Carved");

    list<CarvedSlice>::iterator it;

    // Inset each level's carved region
    for (it = ctx.slices.begin(); it != ctx.slices.end(); it++) {
        InsetOp* op = new InsetOp(&ctx, &(*it));
	opQ.addOperation(op);
    }
    opQ.waitUntilAllOperationsAreFinished();
    stopwatch.checkpoint("Inset");

    // Infill each level's carved region
    for (it = ctx.slices.begin(); it != ctx.slices.end(); it++) {
        InfillOp* op = new InfillOp(&ctx, &(*it));
	opQ.addOperation(op);
    }
    opQ.waitUntilAllOperationsAreFinished();
    stopwatch.checkpoint("Infilled");

    // Create the raft. This isn't easily parallelizable.
    if (ctx.raftLayers > 0) {
	RaftOp* raftOp = new RaftOp(&ctx);
	opQ.addOperation(raftOp);
	opQ.waitUntilAllOperationsAreFinished();
	delete raftOp;
	stopwatch.checkpoint("Rafted");
    }

    // Find optimized path.
    PathFinderOp* pathOp = new PathFinderOp(&ctx);
    opQ.addOperation(pathOp);
    opQ.waitUntilAllOperationsAreFinished();
    delete pathOp;
    stopwatch.checkpoint("Path Optimized");

    // Optionally dump to SVG
    if (doDumpSVG) {
	for (it = ctx.slices.begin(); it != ctx.slices.end(); it++) {
	    SvgDumpOp* op = new SvgDumpOp(&ctx, &(*it));
	    opQ.addOperation(op);
        }
	opQ.waitUntilAllOperationsAreFinished();
        stopwatch.checkpoint("Dumped to SVG");
    }

    // Export toolpaths to apropriate output format.
    Operation* expOp = NULL;
    switch (exportType) {
    case GCODE:
        expOp = new GCodeExportOp(&ctx);
        break;
    default:
        break;
    }
    if (expOp) {
	opQ.addOperation(expOp);
	opQ.waitUntilAllOperationsAreFinished();
	stopwatch.checkpoint("Exported");
    }

    // All done!
    stopwatch.finish();

    return 0;
}

<|endoftext|>


<commit_before>// Copyright (c) 2011 - 2015, GS Group, https://github.com/GSGroup
// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted,
// provided that the above copyright notice and this permission notice appear in all copies.
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

#include <stingraykit/io/BithreadCircularBuffer.h>

namespace stingray
{

	struct BithreadCircularBuffer::Impl : public self_counter<BithreadCircularBuffer::Impl>
	{
		// TODO: finish padding support
		static const size_t PaddingSize = 8;

	private:
		BytesOwner		_storage;
		size_t			_writeOffset, _readOffset;
		bool			_dataIsContiguous;

	public:
		Impl(const BytesOwner& storage) :
			_storage(storage),
			_writeOffset(0), _readOffset(0),
			_dataIsContiguous(true)
		{ }

		size_t GetDataSize() const
		{ return _dataIsContiguous ? (_writeOffset - _readOffset) : (GetStorageSize() - _readOffset + _writeOffset); }

		size_t GetFreeSize() const
		{ return GetStorageSize() - GetDataSize() - 1; }

		size_t GetStorageSize() const
		{ return _storage.size(); }

		ConstByteData GetStorage() const
		{ return _storage; }

		ByteData Write()
		{
			size_t size = _dataIsContiguous ? (GetStorageSize() - _writeOffset) : (_readOffset - _writeOffset);
			return ByteData(_storage, _writeOffset, size);
		}

		void Push(size_t pushSize)
		{
			STINGRAYKIT_CHECK(_writeOffset + pushSize <= GetStorageSize(), IndexOutOfRangeException(pushSize, _writeOffset, GetStorageSize()));
			_writeOffset += pushSize;

			if (_writeOffset == GetStorageSize())
			{
				_writeOffset = 0;
				_dataIsContiguous = !_dataIsContiguous;
			}
		}

		ConstByteData Read()
		{
			size_t size = (_dataIsContiguous) ? (_writeOffset - _readOffset) : (GetStorageSize() - _readOffset);
			return ByteData(_storage, _readOffset, size);
		}

		void Pop(size_t popSize)
		{
			STINGRAYKIT_CHECK(_readOffset + popSize <= GetStorageSize(), IndexOutOfRangeException(popSize, _readOffset, GetStorageSize()));
			_readOffset += popSize;

			if (_readOffset == GetStorageSize())
			{
				_readOffset = 0;
				_dataIsContiguous = !_dataIsContiguous;
			}
		}

		void Clear()
		{
			_writeOffset = 0;
			_readOffset = 0;
			_dataIsContiguous = true;
		}
	};

	BithreadCircularBuffer::BithreadCircularBuffer(size_t size) : _impl(new Impl(BytesOwner::Create(size)))
	{ }

	BithreadCircularBuffer::BithreadCircularBuffer(const BytesOwner& storage) : _impl(new Impl(storage))
	{ }

	BithreadCircularBuffer::~BithreadCircularBuffer()
	{ }

	size_t BithreadCircularBuffer::GetDataSize() const
	{ return _impl->GetDataSize(); }

	size_t BithreadCircularBuffer::GetFreeSize() const
	{ return _impl->GetFreeSize(); }

	size_t BithreadCircularBuffer::GetTotalSize() const
	{ return _impl->GetStorageSize(); }

	BithreadCircularBuffer::Reader BithreadCircularBuffer::Read()
	{ return Reader(_impl); }

	BithreadCircularBuffer::Writer BithreadCircularBuffer::Write()
	{ return Writer(_impl); }

	void BithreadCircularBuffer::Clear()
	{ _impl->Clear(); }

	BithreadCircularBuffer::Reader::Reader(const ImplSelfCountPtr& impl) :
		_impl(impl),
		_data(impl->Read())
	{ }

	BithreadCircularBuffer::Reader::Reader(const Reader& other) : _impl(other._impl), _data(other._data)
	{ }

	BithreadCircularBuffer::Reader::~Reader()
	{ }

	BithreadCircularBuffer::Reader& BithreadCircularBuffer::Reader::operator =(const Reader& other)
	{
		_impl = other._impl;
		_data = other._data;
		return *this;
	}

	const u8* BithreadCircularBuffer::Reader::data() const
	{ return _data.data(); }

	size_t BithreadCircularBuffer::Reader::size() const
	{ return _data.size(); }

	ConstByteData::iterator BithreadCircularBuffer::Reader::begin()
	{ return _data.begin(); }

	ConstByteData::iterator BithreadCircularBuffer::Reader::end()
	{ return _data.end(); }

	ConstByteData BithreadCircularBuffer::Reader::GetData() const
	{ return _data; }

	bool BithreadCircularBuffer::Reader::IsBufferEnd() const
	{
		ConstByteData storage(_impl->GetStorage());
		return (_data.data() + _data.size()) == (storage.data() + storage.size());
	}

	void BithreadCircularBuffer::Reader::Pop(size_t bytes)
	{
		_impl->Pop(bytes);
		_data = ConstByteData(NULL, 0);
	}

	BithreadCircularBuffer::Writer::Writer(const ImplSelfCountPtr& impl) :
		_impl(impl),
		_data(impl->Write())
	{ }

	BithreadCircularBuffer::Writer::Writer(const Writer& other) : _impl(other._impl), _data(other._data)
	{ }

	BithreadCircularBuffer::Writer::~Writer()
	{ }

	BithreadCircularBuffer::Writer& BithreadCircularBuffer::Writer::operator =(const Writer& other)
	{
		_impl = other._impl;
		_data = other._data;
		return *this;
	}

	u8* BithreadCircularBuffer::Writer::data() const
	{ return _data.data(); }

	size_t BithreadCircularBuffer::Writer::size() const
	{ return _data.size(); }

	ByteData::iterator BithreadCircularBuffer::Writer::begin()
	{ return _data.begin(); }

	ByteData::iterator BithreadCircularBuffer::Writer::end()
	{ return _data.end(); }

	ByteData BithreadCircularBuffer::Writer::GetData() const
	{ return _data; }

	bool BithreadCircularBuffer::Writer::IsBufferEnd() const
	{
		ConstByteData storage(_impl->GetStorage());
		return (_data.data() + _data.size()) == (storage.data() + storage.size());
	}

	void BithreadCircularBuffer::Writer::Push(size_t bytes)
	{
		_impl->Push(bytes);
		_data = ByteData(null, 0);
	}

}
<commit_msg>BithreadCircularBuffer: fix free size calculation<commit_after>// Copyright (c) 2011 - 2015, GS Group, https://github.com/GSGroup
// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted,
// provided that the above copyright notice and this permission notice appear in all copies.
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

#include <stingraykit/io/BithreadCircularBuffer.h>

namespace stingray
{

	struct BithreadCircularBuffer::Impl : public self_counter<BithreadCircularBuffer::Impl>
	{
		// TODO: finish padding support
		static const size_t PaddingSize = 8;

	private:
		BytesOwner		_storage;
		size_t			_writeOffset, _readOffset;
		bool			_dataIsContiguous;

	public:
		Impl(const BytesOwner& storage) :
			_storage(storage),
			_writeOffset(0), _readOffset(0),
			_dataIsContiguous(true)
		{ }

		size_t GetDataSize() const
		{ return _dataIsContiguous ? (_writeOffset - _readOffset) : (GetStorageSize() - _readOffset + _writeOffset); }

		size_t GetFreeSize() const
		{ return GetStorageSize() - GetDataSize(); }

		size_t GetStorageSize() const
		{ return _storage.size(); }

		ConstByteData GetStorage() const
		{ return _storage; }

		ByteData Write()
		{
			size_t size = _dataIsContiguous ? (GetStorageSize() - _writeOffset) : (_readOffset - _writeOffset);
			return ByteData(_storage, _writeOffset, size);
		}

		void Push(size_t pushSize)
		{
			STINGRAYKIT_CHECK(_writeOffset + pushSize <= GetStorageSize(), IndexOutOfRangeException(pushSize, _writeOffset, GetStorageSize()));
			_writeOffset += pushSize;

			if (_writeOffset == GetStorageSize())
			{
				_writeOffset = 0;
				_dataIsContiguous = !_dataIsContiguous;
			}
		}

		ConstByteData Read()
		{
			size_t size = (_dataIsContiguous) ? (_writeOffset - _readOffset) : (GetStorageSize() - _readOffset);
			return ByteData(_storage, _readOffset, size);
		}

		void Pop(size_t popSize)
		{
			STINGRAYKIT_CHECK(_readOffset + popSize <= GetStorageSize(), IndexOutOfRangeException(popSize, _readOffset, GetStorageSize()));
			_readOffset += popSize;

			if (_readOffset == GetStorageSize())
			{
				_readOffset = 0;
				_dataIsContiguous = !_dataIsContiguous;
			}
		}

		void Clear()
		{
			_writeOffset = 0;
			_readOffset = 0;
			_dataIsContiguous = true;
		}
	};

	BithreadCircularBuffer::BithreadCircularBuffer(size_t size) : _impl(new Impl(BytesOwner::Create(size)))
	{ }

	BithreadCircularBuffer::BithreadCircularBuffer(const BytesOwner& storage) : _impl(new Impl(storage))
	{ }

	BithreadCircularBuffer::~BithreadCircularBuffer()
	{ }

	size_t BithreadCircularBuffer::GetDataSize() const
	{ return _impl->GetDataSize(); }

	size_t BithreadCircularBuffer::GetFreeSize() const
	{ return _impl->GetFreeSize(); }

	size_t BithreadCircularBuffer::GetTotalSize() const
	{ return _impl->GetStorageSize(); }

	BithreadCircularBuffer::Reader BithreadCircularBuffer::Read()
	{ return Reader(_impl); }

	BithreadCircularBuffer::Writer BithreadCircularBuffer::Write()
	{ return Writer(_impl); }

	void BithreadCircularBuffer::Clear()
	{ _impl->Clear(); }

	BithreadCircularBuffer::Reader::Reader(const ImplSelfCountPtr& impl) :
		_impl(impl),
		_data(impl->Read())
	{ }

	BithreadCircularBuffer::Reader::Reader(const Reader& other) : _impl(other._impl), _data(other._data)
	{ }

	BithreadCircularBuffer::Reader::~Reader()
	{ }

	BithreadCircularBuffer::Reader& BithreadCircularBuffer::Reader::operator =(const Reader& other)
	{
		_impl = other._impl;
		_data = other._data;
		return *this;
	}

	const u8* BithreadCircularBuffer::Reader::data() const
	{ return _data.data(); }

	size_t BithreadCircularBuffer::Reader::size() const
	{ return _data.size(); }

	ConstByteData::iterator BithreadCircularBuffer::Reader::begin()
	{ return _data.begin(); }

	ConstByteData::iterator BithreadCircularBuffer::Reader::end()
	{ return _data.end(); }

	ConstByteData BithreadCircularBuffer::Reader::GetData() const
	{ return _data; }

	bool BithreadCircularBuffer::Reader::IsBufferEnd() const
	{
		ConstByteData storage(_impl->GetStorage());
		return (_data.data() + _data.size()) == (storage.data() + storage.size());
	}

	void BithreadCircularBuffer::Reader::Pop(size_t bytes)
	{
		_impl->Pop(bytes);
		_data = ConstByteData(NULL, 0);
	}

	BithreadCircularBuffer::Writer::Writer(const ImplSelfCountPtr& impl) :
		_impl(impl),
		_data(impl->Write())
	{ }

	BithreadCircularBuffer::Writer::Writer(const Writer& other) : _impl(other._impl), _data(other._data)
	{ }

	BithreadCircularBuffer::Writer::~Writer()
	{ }

	BithreadCircularBuffer::Writer& BithreadCircularBuffer::Writer::operator =(const Writer& other)
	{
		_impl = other._impl;
		_data = other._data;
		return *this;
	}

	u8* BithreadCircularBuffer::Writer::data() const
	{ return _data.data(); }

	size_t BithreadCircularBuffer::Writer::size() const
	{ return _data.size(); }

	ByteData::iterator BithreadCircularBuffer::Writer::begin()
	{ return _data.begin(); }

	ByteData::iterator BithreadCircularBuffer::Writer::end()
	{ return _data.end(); }

	ByteData BithreadCircularBuffer::Writer::GetData() const
	{ return _data; }

	bool BithreadCircularBuffer::Writer::IsBufferEnd() const
	{
		ConstByteData storage(_impl->GetStorage());
		return (_data.data() + _data.size()) == (storage.data() + storage.size());
	}

	void BithreadCircularBuffer::Writer::Push(size_t bytes)
	{
		_impl->Push(bytes);
		_data = ByteData(null, 0);
	}

}
<|endoftext|>


<commit_before>/* Copyright (c) 2017 Hans-Kristian Arntzen
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include "application.hpp"
#include "application_events.hpp"
#include "vulkan_symbol_wrapper.h"
#include "vulkan.hpp"
#include <thread>
#include <mutex>
#include <condition_variable>

using namespace std;
using namespace Vulkan;

namespace Granite
{
class FrameWorker
{
public:
	FrameWorker();
	~FrameWorker();

	void wait();
	void set_work(function<void ()> work);

private:
	thread thr;
	condition_variable cond;
	mutex cond_lock;
	function<void ()> func;
	bool working = false;
	bool dead = false;

	void thread_loop();
};

FrameWorker::FrameWorker()
{
	thr = thread(&FrameWorker::thread_loop, this);
}

void FrameWorker::wait()
{
	unique_lock<mutex> u{cond_lock};
	cond.wait(u, [this]() -> bool {
		return !working;
	});
}

void FrameWorker::set_work(function<void()> work)
{
	wait();
	func = move(work);
	unique_lock<mutex> u{cond_lock};
	working = true;
	cond.notify_one();
}

void FrameWorker::thread_loop()
{
	for (;;)
	{
		{
			unique_lock<mutex> u{cond_lock};
			cond.wait(u, [this]() -> bool {
				return working || dead;
			});

			if (dead)
				return;
		}

		if (func)
			func();

		lock_guard<mutex> holder{cond_lock};
		working = false;
		cond.notify_one();
	}
}

FrameWorker::~FrameWorker()
{
	{
		lock_guard<mutex> holder{cond_lock};
		dead = true;
		cond.notify_one();
	}

	if (thr.joinable())
		thr.join();
}

struct WSIPlatformHeadless : Vulkan::WSIPlatform
{
public:
	WSIPlatformHeadless(unsigned width, unsigned height)
		: width(width), height(height)
	{
		if (!Context::init_loader(nullptr))
			throw runtime_error("Failed to initialize Vulkan loader.");

		EventManager::get_global().dequeue_all_latched(ApplicationLifecycleEvent::get_type_id());
		EventManager::get_global().enqueue_latched<ApplicationLifecycleEvent>(ApplicationLifecycle::Stopped);
		EventManager::get_global().dequeue_all_latched(ApplicationLifecycleEvent::get_type_id());
		EventManager::get_global().enqueue_latched<ApplicationLifecycleEvent>(ApplicationLifecycle::Paused);
		EventManager::get_global().dequeue_all_latched(ApplicationLifecycleEvent::get_type_id());
		EventManager::get_global().enqueue_latched<ApplicationLifecycleEvent>(ApplicationLifecycle::Running);
	}

	~WSIPlatformHeadless() override
	{
		EventManager::get_global().dequeue_all_latched(ApplicationLifecycleEvent::get_type_id());
		EventManager::get_global().enqueue_latched<ApplicationLifecycleEvent>(ApplicationLifecycle::Paused);
		EventManager::get_global().dequeue_all_latched(ApplicationLifecycleEvent::get_type_id());
		EventManager::get_global().enqueue_latched<ApplicationLifecycleEvent>(ApplicationLifecycle::Stopped);
	}

	bool alive(Vulkan::WSI &) override
	{
		return frames <= max_frames;
	}

	void poll_input() override
	{
		get_input_tracker().dispatch_current_state(get_frame_timer().get_frame_time());
	}

	vector<const char *> get_instance_extensions() override
	{
		return {};
	}

	VkSurfaceKHR create_surface(VkInstance, VkPhysicalDevice) override
	{
		return VK_NULL_HANDLE;
	}

	uint32_t get_surface_width() override
	{
		return width;
	}

	uint32_t get_surface_height() override
	{
		return height;
	}

	void notify_resize(unsigned width, unsigned height)
	{
		resize = true;
		this->width = width;
		this->height = height;
	}

	void set_max_frames(unsigned max_frames)
	{
		this->max_frames = max_frames;
	}

	bool has_external_swapchain() override
	{
		return true;
	}

	void init(Application *app)
	{
		this->app = app;

		set_max_frames(16);
		auto &wsi = app->get_wsi();
		wsi.init_external_context(make_unique<Context>(nullptr, 0, nullptr, 0));

		auto &device = wsi.get_device();

		auto info = ImageCreateInfo::render_target(width, height, VK_FORMAT_R8G8B8A8_SRGB);
		info.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
		info.initial_layout = VK_IMAGE_LAYOUT_UNDEFINED;
		info.misc = IMAGE_MISC_CONCURRENT_QUEUE_BIT;

		BufferCreateInfo readback = {};
		readback.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
		readback.domain = BufferDomain::CachedHost;
		readback.size = width * height * sizeof(uint32_t);

		for (unsigned i = 0; i < SwapchainImages; i++)
		{
			swapchain_images.push_back(device.create_image(info, nullptr));
			readback_buffers.push_back(device.create_buffer(readback, nullptr));
			acquire_semaphore.push_back(nullptr);
			worker_threads.push_back(make_unique<FrameWorker>());
			readback_fence.push_back({});
		}

		for (auto &swap : swapchain_images)
			swap->set_swapchain_layout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);

		wsi.init_external_swapchain(swapchain_images);
	}

	void begin_frame()
	{
		auto &wsi = app->get_wsi();
		wsi.set_external_frame(index, acquire_semaphore[index], 0.01);
		acquire_semaphore[index].reset();
		worker_threads[index]->wait();
	}

	void end_frame()
	{
		auto &wsi = app->get_wsi();
		auto &device = wsi.get_device();
		auto release_semaphore = wsi.get_external_release_semaphore();

		if (release_semaphore && release_semaphore->get_semaphore() != VK_NULL_HANDLE)
		{
			device.add_wait_semaphore(CommandBuffer::Type::Transfer, release_semaphore, VK_PIPELINE_STAGE_TRANSFER_BIT);

			auto cmd = device.request_command_buffer(CommandBuffer::Type::Transfer);
			swapchain_images[index]->set_layout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);

			cmd->copy_image_to_buffer(*readback_buffers[index], *swapchain_images[index],
			                          0, {}, {width, height, 1},
			                          0, 0, {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1});

			cmd->barrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT,
			             VK_PIPELINE_STAGE_HOST_BIT, VK_ACCESS_HOST_READ_BIT);

			device.submit(cmd, &readback_fence[index], &acquire_semaphore[index]);

			unsigned index_copy = index;
			unsigned frame_copy = frames;
			worker_threads[index]->set_work([this, index_copy, frame_copy]() {
				dump_frame(frame_copy, index_copy);
			});
		}
		release_semaphore.reset();
		index = (index + 1) % SwapchainImages;
		frames++;
	}

private:
	unsigned width = 0;
	unsigned height = 0;
	unsigned frames = 0;
	unsigned max_frames = UINT_MAX;
	unsigned index = 0;
	enum { SwapchainImages = 4 };

	vector<ImageHandle> swapchain_images;
	vector<BufferHandle> readback_buffers;
	vector<Semaphore> acquire_semaphore;
	vector<Fence> readback_fence;
	vector<unique_ptr<FrameWorker>> worker_threads;

	void dump_frame(unsigned frame, unsigned index)
	{
		auto &wsi = app->get_wsi();
		auto &device = wsi.get_device();
		device.wait_for_fence(readback_fence[index]);
		readback_fence[index].reset();

		LOGI("Dumping frame: %u (index: %u)\n", frame, index);
	}

	Application *app = nullptr;
};

void application_dummy()
{
}
}

int main(int argc, char *argv[])
{
	auto app = unique_ptr<Granite::Application>(Granite::application_create(argc, argv));
	if (app)
	{
		auto platform = make_unique<Granite::WSIPlatformHeadless>(1280, 720);
		auto *p = platform.get();
		if (!app->init_wsi(move(platform)))
			return 1;

		p->init(app.get());

		while (app->poll())
		{
			p->begin_frame();
			app->run_frame();
			p->end_frame();
		}
		return 0;
	}
	else
		return 1;
}
<commit_msg>Readback works!<commit_after>/* Copyright (c) 2017 Hans-Kristian Arntzen
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include "application.hpp"
#include "application_events.hpp"
#include "vulkan_symbol_wrapper.h"
#include "vulkan.hpp"
#include <thread>
#include <mutex>
#include <condition_variable>
#include "stb_image_write.h"

using namespace std;
using namespace Vulkan;

namespace Granite
{
class FrameWorker
{
public:
	FrameWorker();
	~FrameWorker();

	void wait();
	void set_work(function<void ()> work);

private:
	thread thr;
	condition_variable cond;
	mutex cond_lock;
	function<void ()> func;
	bool working = false;
	bool dead = false;

	void thread_loop();
};

FrameWorker::FrameWorker()
{
	thr = thread(&FrameWorker::thread_loop, this);
}

void FrameWorker::wait()
{
	unique_lock<mutex> u{cond_lock};
	cond.wait(u, [this]() -> bool {
		return !working;
	});
}

void FrameWorker::set_work(function<void()> work)
{
	wait();
	func = move(work);
	unique_lock<mutex> u{cond_lock};
	working = true;
	cond.notify_one();
}

void FrameWorker::thread_loop()
{
	for (;;)
	{
		{
			unique_lock<mutex> u{cond_lock};
			cond.wait(u, [this]() -> bool {
				return working || dead;
			});

			if (dead)
				return;
		}

		if (func)
			func();

		lock_guard<mutex> holder{cond_lock};
		working = false;
		cond.notify_one();
	}
}

FrameWorker::~FrameWorker()
{
	{
		lock_guard<mutex> holder{cond_lock};
		dead = true;
		cond.notify_one();
	}

	if (thr.joinable())
		thr.join();
}

struct WSIPlatformHeadless : Vulkan::WSIPlatform
{
public:
	WSIPlatformHeadless(unsigned width, unsigned height)
		: width(width), height(height)
	{
		if (!Context::init_loader(nullptr))
			throw runtime_error("Failed to initialize Vulkan loader.");

		EventManager::get_global().dequeue_all_latched(ApplicationLifecycleEvent::get_type_id());
		EventManager::get_global().enqueue_latched<ApplicationLifecycleEvent>(ApplicationLifecycle::Stopped);
		EventManager::get_global().dequeue_all_latched(ApplicationLifecycleEvent::get_type_id());
		EventManager::get_global().enqueue_latched<ApplicationLifecycleEvent>(ApplicationLifecycle::Paused);
		EventManager::get_global().dequeue_all_latched(ApplicationLifecycleEvent::get_type_id());
		EventManager::get_global().enqueue_latched<ApplicationLifecycleEvent>(ApplicationLifecycle::Running);
	}

	~WSIPlatformHeadless() override
	{
		app->get_wsi().get_device().wait_idle();

		EventManager::get_global().dequeue_all_latched(ApplicationLifecycleEvent::get_type_id());
		EventManager::get_global().enqueue_latched<ApplicationLifecycleEvent>(ApplicationLifecycle::Paused);
		EventManager::get_global().dequeue_all_latched(ApplicationLifecycleEvent::get_type_id());
		EventManager::get_global().enqueue_latched<ApplicationLifecycleEvent>(ApplicationLifecycle::Stopped);
	}

	bool alive(Vulkan::WSI &) override
	{
		return frames <= max_frames;
	}

	void poll_input() override
	{
		get_input_tracker().dispatch_current_state(get_frame_timer().get_frame_time());
	}

	void enable_png_readback(string base_path)
	{
		png_readback = base_path;
	}

	vector<const char *> get_instance_extensions() override
	{
		return {};
	}

	VkSurfaceKHR create_surface(VkInstance, VkPhysicalDevice) override
	{
		return VK_NULL_HANDLE;
	}

	uint32_t get_surface_width() override
	{
		return width;
	}

	uint32_t get_surface_height() override
	{
		return height;
	}

	void notify_resize(unsigned width, unsigned height)
	{
		resize = true;
		this->width = width;
		this->height = height;
	}

	void set_max_frames(unsigned max_frames)
	{
		this->max_frames = max_frames;
	}

	bool has_external_swapchain() override
	{
		return true;
	}

	void init(Application *app)
	{
		this->app = app;

		auto &wsi = app->get_wsi();
		wsi.init_external_context(make_unique<Context>(nullptr, 0, nullptr, 0));

		auto &device = wsi.get_device();

		auto info = ImageCreateInfo::render_target(width, height, VK_FORMAT_R8G8B8A8_SRGB);
		info.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
		info.initial_layout = VK_IMAGE_LAYOUT_UNDEFINED;
		info.misc = IMAGE_MISC_CONCURRENT_QUEUE_BIT;

		BufferCreateInfo readback = {};
		readback.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
		readback.domain = BufferDomain::CachedHost;
		readback.size = width * height * sizeof(uint32_t);

		for (unsigned i = 0; i < SwapchainImages; i++)
		{
			swapchain_images.push_back(device.create_image(info, nullptr));
			readback_buffers.push_back(device.create_buffer(readback, nullptr));
			acquire_semaphore.push_back(nullptr);
			worker_threads.push_back(make_unique<FrameWorker>());
			readback_fence.push_back({});
		}

		for (auto &swap : swapchain_images)
			swap->set_swapchain_layout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);

		wsi.init_external_swapchain(swapchain_images);
	}

	void begin_frame()
	{
		auto &wsi = app->get_wsi();
		wsi.set_external_frame(index, acquire_semaphore[index], 0.01);
		acquire_semaphore[index].reset();
		worker_threads[index]->wait();
	}

	void end_frame()
	{
		auto &wsi = app->get_wsi();
		auto &device = wsi.get_device();
		auto release_semaphore = wsi.get_external_release_semaphore();

		if (release_semaphore && release_semaphore->get_semaphore() != VK_NULL_HANDLE)
		{
			if (!png_readback.empty())
			{
				device.add_wait_semaphore(CommandBuffer::Type::Transfer, release_semaphore,
				                          VK_PIPELINE_STAGE_TRANSFER_BIT);

				auto cmd = device.request_command_buffer(CommandBuffer::Type::Transfer);
				swapchain_images[index]->set_layout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);

				cmd->copy_image_to_buffer(*readback_buffers[index], *swapchain_images[index],
				                          0, {}, {width, height, 1},
				                          0, 0, {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1});

				cmd->barrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT,
				             VK_PIPELINE_STAGE_HOST_BIT, VK_ACCESS_HOST_READ_BIT);

				device.submit(cmd, &readback_fence[index], &acquire_semaphore[index]);

				unsigned index_copy = index;
				unsigned frame_copy = frames;
				worker_threads[index]->set_work([this, index_copy, frame_copy]() {
					dump_frame(frame_copy, index_copy);
				});
			}
			else
			{
				acquire_semaphore[index] = release_semaphore;
			}
		}
		release_semaphore.reset();
		index = (index + 1) % SwapchainImages;
		frames++;
	}

private:
	unsigned width = 0;
	unsigned height = 0;
	unsigned frames = 0;
	unsigned max_frames = UINT_MAX;
	unsigned index = 0;
	string png_readback;
	enum { SwapchainImages = 4 };

	vector<ImageHandle> swapchain_images;
	vector<BufferHandle> readback_buffers;
	vector<Semaphore> acquire_semaphore;
	vector<Fence> readback_fence;
	vector<unique_ptr<FrameWorker>> worker_threads;

	void dump_frame(unsigned frame, unsigned index)
	{
		auto &wsi = app->get_wsi();
		auto &device = wsi.get_device();
		device.wait_for_fence(readback_fence[index]);
		readback_fence[index].reset();

		LOGI("Dumping frame: %u (index: %u)\n", frame, index);

		auto *ptr = static_cast<uint32_t *>(device.map_host_buffer(*readback_buffers[index], MEMORY_ACCESS_READ_WRITE));
		for (unsigned i = 0; i < width * height; i++)
			ptr[i] |= 0xff000000u;

		char buffer[64];
		sprintf(buffer, "_%05u.png", frame);
		auto path = png_readback + buffer;
		if (!stbi_write_png(path.c_str(), width, height, 4, ptr, width * 4))
			LOGE("Failed to write PNG to disk.\n");
		device.unmap_host_buffer(*readback_buffers[index]);
	}

	Application *app = nullptr;
};

void application_dummy()
{
}
}

int main(int argc, char *argv[])
{
	auto app = unique_ptr<Granite::Application>(Granite::application_create(argc, argv));
	if (app)
	{
		auto platform = make_unique<Granite::WSIPlatformHeadless>(1280, 720);
		auto *p = platform.get();
		if (!app->init_wsi(move(platform)))
			return 1;

		p->enable_png_readback("/tmp/test");
		p->set_max_frames(16);
		p->init(app.get());

		while (app->poll())
		{
			p->begin_frame();
			app->run_frame();
			p->end_frame();
		}
		return 0;
	}
	else
		return 1;
}
<|endoftext|>


<commit_before>#ifndef MJOLNIR_OMP_SYSTEM_HPP
#define MJOLNIR_OMP_SYSTEM_HPP
#include <mjolnir/util/aligned_allocator.hpp>
#include <mjolnir/omp/OpenMPSimulatorTraits.hpp>
#include <mjolnir/omp/RandomNumberGenerator.hpp>
#include <mjolnir/core/System.hpp>

namespace mjolnir
{

template<typename realT, template<typename, typename> class boundaryT>
class System<OpenMPSimulatorTraits<realT, boundaryT>>
{
  public:
    using traits_type     = OpenMPSimulatorTraits<realT, boundaryT>;
    using real_type       = typename traits_type::real_type;
    using coordinate_type = typename traits_type::coordinate_type;
    using matrix33_type   = typename traits_type::matrix33_type;
    using boundary_type   = typename traits_type::boundary_type;
    using topology_type   = Topology;
    using attribute_type  = std::map<std::string, real_type>;
    using rng_type        = RandomNumberGenerator<traits_type>;

    using string_type              = std::string;
    using particle_type            = Particle<real_type, coordinate_type>;
    using particle_view_type       = ParticleView<real_type, coordinate_type>;
    using particle_const_view_type = ParticleConstView<real_type, coordinate_type>;

    using real_container_type          = std::vector<real_type>;
    using coordinate_container_type    = std::vector<coordinate_type>;
    using string_container_type        = std::vector<std::string>;

    static constexpr std::size_t cache_alignment = 64;

    template<typename T>
    using cache_aligned_allocator = aligned_allocator<T, cache_alignment>;

  public:

    System(const std::size_t num_particles, const boundary_type& bound)
        : velocity_initialized_(false), force_initialized_(false),
          boundary_(bound), attributes_(),
          virial_(0,0,0, 0,0,0, 0,0,0),
          virial_threads_(omp_get_max_threads(),
                          matrix33_type(0,0,0, 0,0,0, 0,0,0)),
          num_particles_(num_particles),
          masses_   (num_particles), rmasses_   (num_particles),
          positions_(num_particles), velocities_(num_particles),
          forces_main_(num_particles),
          forces_threads_(omp_get_max_threads(),
              coordinate_container_type(num_particles,
                  math::make_coordinate<coordinate_type>(0,0,0)),
              cache_aligned_allocator<coordinate_container_type>{}),
          names_(num_particles), groups_(num_particles)
    {}
    ~System() = default;

    void initialize(rng_type& rng)
    {
        MJOLNIR_GET_DEFAULT_LOGGER();
        MJOLNIR_LOG_FUNCTION();

        // make all the particles inside the boundary
        for(auto& p : this->positions_)
        {
            p = this->boundary_.adjust_position(p);
        }

        if(this->velocity_initialized_)
        {
            MJOLNIR_LOG_NOTICE(
                "velocity is already given, nothing to initialize in System");
            return ;
        }
        assert(this->has_attribute("temperature"));

        const real_type kB    = physics::constants<real_type>::kB();
        const real_type T_ref = this->attribute("temperature");

        MJOLNIR_LOG_NOTICE("generating velocity with T = ", T_ref, "...");

        // generate Maxwell-Boltzmann distribution
        const real_type kBT = kB * T_ref;
        for(std::size_t i=0; i<this->size(); ++i)
        {
            const auto vel_coef = std::sqrt(kBT / this->mass(i));
            math::X(this->velocity(i)) = rng.gaussian(0, vel_coef);
            math::Y(this->velocity(i)) = rng.gaussian(0, vel_coef);
            math::Z(this->velocity(i)) = rng.gaussian(0, vel_coef);
        }
        MJOLNIR_LOG_NOTICE("done.");
        return;
    }

    coordinate_type adjust_direction(coordinate_type from, coordinate_type to) const noexcept
    {
        return boundary_.adjust_direction(from, to);
    }
    coordinate_type  adjust_position(coordinate_type dr) const noexcept
    {
        return boundary_.adjust_position(dr);
    }
    coordinate_type transpose(coordinate_type tgt, const coordinate_type& ref) const noexcept
    {
        return boundary_.transpose(tgt, ref);
    }

    std::size_t size() const noexcept {return num_particles_;}

    particle_view_type operator[](std::size_t i) noexcept
    {
        return particle_view_type{
            masses_[i],    rmasses_[i],
            positions_[i], velocities_[i], forces_main_[i],
            names_[i],     groups_[i]
        };
    }
    particle_const_view_type operator[](std::size_t i) const noexcept
    {
        return particle_const_view_type{
            masses_[i],    rmasses_[i],
            positions_[i], velocities_[i], forces_main_[i],
            names_[i],     groups_[i]
        };
    }
    particle_view_type at(std::size_t i)
    {
        return particle_view_type{
            masses_.at(i),    rmasses_.at(i),
            positions_.at(i), velocities_.at(i), forces_main_.at(i),
            names_.at(i),     groups_.at(i)
        };
    }
    particle_const_view_type at(std::size_t i) const
    {
        return particle_const_view_type{
            masses_.at(i),    rmasses_.at(i),
            positions_.at(i), velocities_.at(i), forces_main_.at(i),
            names_.at(i),     groups_.at(i)
        };
    }

    real_type  mass (std::size_t i) const noexcept {return masses_[i];}
    real_type& mass (std::size_t i)       noexcept {return masses_[i];}
    real_type  rmass(std::size_t i) const noexcept {return rmasses_[i];}
    real_type& rmass(std::size_t i)       noexcept {return rmasses_[i];}

    coordinate_type const& position(std::size_t i) const noexcept {return positions_[i];}
    coordinate_type&       position(std::size_t i)       noexcept {return positions_[i];}
    coordinate_type const& velocity(std::size_t i) const noexcept {return velocities_[i];}
    coordinate_type&       velocity(std::size_t i)       noexcept {return velocities_[i];}
    coordinate_type const& force   (std::size_t i) const noexcept {return forces_main_[i];}
    coordinate_type&       force   (std::size_t i)       noexcept {return forces_main_[i];}

    coordinate_type const&
    force_thread(std::size_t thread_num, std::size_t particle_id) const noexcept
    {
        return forces_threads_[thread_num][particle_id];
    }
    coordinate_type&
    force_thread(std::size_t thread_num, std::size_t particle_id)       noexcept
    {
        return forces_threads_[thread_num][particle_id];
    }

    matrix33_type&       virial()       noexcept {return virial_;}
    matrix33_type const& virial() const noexcept {return virial_;}

    matrix33_type&       virial_thread(std::size_t thread_num)       noexcept
    {
        return virial_threads_[thread_num];
    }
    matrix33_type const& virial_thread(std::size_t thread_num) const noexcept
    {
        return virial_threads_[thread_num];
    }

    void preprocess_forces()  noexcept
    {
        // Do nothing. We already allocated the thread local forces and virials
        // with zero values. Also, in the end of each step, postprocess_forces
        // zero-clears everything.
    }

    // Since all the forces will be calculated in different cores, we need to
    // merge those thread-local forces by summing up those for each particle.
    void postprocess_forces() noexcept
    {
        // sumup virial and zero-clear the thread local virials
        virial_ = matrix33_type(0,0,0, 0,0,0, 0,0,0);
        for(std::size_t thread_id=0, max_threads=omp_get_max_threads();
                thread_id < max_threads; ++thread_id)
        {
            virial_ += virial_threads_[thread_id];
            virial_threads_[thread_id] = matrix33_type(0,0,0, 0,0,0, 0,0,0);
        }

#pragma omp parallel for
        for(std::size_t i=0; i<this->size(); ++i)
        {
            // sumup all the thread-local forces into the main force.
            // XXX: keep the values in the main force! Some pre-omp interactions
            //      may write its force in it. Clearing main force is a task
            //      for Integrators.
            for(std::size_t thread_id=0, max_threads=omp_get_max_threads();
                    thread_id < max_threads; ++thread_id)
            {
                this->force(i) += this->force_thread(thread_id, i);
                this->force_thread(thread_id, i) =
                    math::make_coordinate<coordinate_type>(0, 0, 0);
            }
        }
        return;
    }

    string_type const& name (std::size_t i) const noexcept {return names_[i];}
    string_type&       name (std::size_t i)       noexcept {return names_[i];}
    string_type const& group(std::size_t i) const noexcept {return groups_[i];}
    string_type&       group(std::size_t i)       noexcept {return groups_[i];}

    boundary_type&       boundary()       noexcept {return boundary_;}
    boundary_type const& boundary() const noexcept {return boundary_;}

    // system attributes like `reference temperature`, `ionic strength`, ...
    // assuming it will not be called so often.
    real_type  attribute(const std::string& key) const {return attributes_.at(key);}
    real_type& attribute(const std::string& key)       {return attributes_[key];}
    bool   has_attribute(const std::string& key) const {return attributes_.count(key) == 1;}
    attribute_type const& attributes() const noexcept {return attributes_;}

    bool  velocity_initialized() const noexcept {return velocity_initialized_;}
    bool& velocity_initialized()       noexcept {return velocity_initialized_;}

    // force_main is initialized or not. force_thread does not matter.
    bool  force_initialized() const noexcept {return force_initialized_;}
    bool& force_initialized()       noexcept {return force_initialized_;}

    coordinate_container_type const& forces() const noexcept {return forces_main_;}
    coordinate_container_type&       forces()       noexcept {return forces_main_;}

  private:

    bool           velocity_initialized_, force_initialized_;
    boundary_type  boundary_;
    attribute_type attributes_;

    matrix33_type  virial_;
    std::vector<matrix33_type, cache_aligned_allocator<matrix33_type>> virial_threads_;

    std::size_t                  num_particles_;
    real_container_type          masses_;
    real_container_type          rmasses_; // r for reciprocal
    coordinate_container_type    positions_;
    coordinate_container_type    velocities_;
    coordinate_container_type    forces_main_;
    // thread-local forces
    std::vector<coordinate_container_type,
                cache_aligned_allocator<coordinate_container_type>
        > forces_threads_;
    string_container_type        names_;
    string_container_type        groups_;

    // names and groups are in Topology class
};

#ifdef MJOLNIR_SEPARATE_BUILD
extern template class System<OpenMPSimulatorTraits<double, UnlimitedBoundary>>;
extern template class System<OpenMPSimulatorTraits<float,  UnlimitedBoundary>>;
extern template class System<OpenMPSimulatorTraits<double, CuboidalPeriodicBoundary>>;
extern template class System<OpenMPSimulatorTraits<float,  CuboidalPeriodicBoundary>>;
#endif

} // mjolnir
#endif// MJOLNIR_SYSTEM_HPP
<commit_msg>fix: allow to have non-omp implementation<commit_after>#ifndef MJOLNIR_OMP_SYSTEM_HPP
#define MJOLNIR_OMP_SYSTEM_HPP
#include <mjolnir/util/aligned_allocator.hpp>
#include <mjolnir/omp/OpenMPSimulatorTraits.hpp>
#include <mjolnir/omp/RandomNumberGenerator.hpp>
#include <mjolnir/core/System.hpp>

namespace mjolnir
{

template<typename realT, template<typename, typename> class boundaryT>
class System<OpenMPSimulatorTraits<realT, boundaryT>>
{
  public:
    using traits_type     = OpenMPSimulatorTraits<realT, boundaryT>;
    using real_type       = typename traits_type::real_type;
    using coordinate_type = typename traits_type::coordinate_type;
    using matrix33_type   = typename traits_type::matrix33_type;
    using boundary_type   = typename traits_type::boundary_type;
    using topology_type   = Topology;
    using attribute_type  = std::map<std::string, real_type>;
    using rng_type        = RandomNumberGenerator<traits_type>;

    using string_type              = std::string;
    using particle_type            = Particle<real_type, coordinate_type>;
    using particle_view_type       = ParticleView<real_type, coordinate_type>;
    using particle_const_view_type = ParticleConstView<real_type, coordinate_type>;

    using real_container_type          = std::vector<real_type>;
    using coordinate_container_type    = std::vector<coordinate_type>;
    using string_container_type        = std::vector<std::string>;

    static constexpr std::size_t cache_alignment = 64;

    template<typename T>
    using cache_aligned_allocator = aligned_allocator<T, cache_alignment>;

  public:

    System(const std::size_t num_particles, const boundary_type& bound)
        : velocity_initialized_(false), force_initialized_(false),
          boundary_(bound), attributes_(),
          virial_(0,0,0, 0,0,0, 0,0,0),
          virial_threads_(omp_get_max_threads(),
                          matrix33_type(0,0,0, 0,0,0, 0,0,0)),
          num_particles_(num_particles),
          masses_   (num_particles), rmasses_   (num_particles),
          positions_(num_particles), velocities_(num_particles),
          forces_main_(num_particles),
          forces_threads_(omp_get_max_threads(),
              coordinate_container_type(num_particles,
                  math::make_coordinate<coordinate_type>(0,0,0)),
              cache_aligned_allocator<coordinate_container_type>{}),
          names_(num_particles), groups_(num_particles)
    {}
    ~System() = default;

    void initialize(rng_type& rng)
    {
        MJOLNIR_GET_DEFAULT_LOGGER();
        MJOLNIR_LOG_FUNCTION();

        // make all the particles inside the boundary
        for(auto& p : this->positions_)
        {
            p = this->boundary_.adjust_position(p);
        }

        if(this->velocity_initialized_)
        {
            MJOLNIR_LOG_NOTICE(
                "velocity is already given, nothing to initialize in System");
            return ;
        }
        assert(this->has_attribute("temperature"));

        const real_type kB    = physics::constants<real_type>::kB();
        const real_type T_ref = this->attribute("temperature");

        MJOLNIR_LOG_NOTICE("generating velocity with T = ", T_ref, "...");

        // generate Maxwell-Boltzmann distribution
        const real_type kBT = kB * T_ref;
        for(std::size_t i=0; i<this->size(); ++i)
        {
            const auto vel_coef = std::sqrt(kBT / this->mass(i));
            math::X(this->velocity(i)) = rng.gaussian(0, vel_coef);
            math::Y(this->velocity(i)) = rng.gaussian(0, vel_coef);
            math::Z(this->velocity(i)) = rng.gaussian(0, vel_coef);
        }
        MJOLNIR_LOG_NOTICE("done.");
        return;
    }

    coordinate_type adjust_direction(coordinate_type from, coordinate_type to) const noexcept
    {
        return boundary_.adjust_direction(from, to);
    }
    coordinate_type  adjust_position(coordinate_type dr) const noexcept
    {
        return boundary_.adjust_position(dr);
    }
    coordinate_type transpose(coordinate_type tgt, const coordinate_type& ref) const noexcept
    {
        return boundary_.transpose(tgt, ref);
    }

    std::size_t size() const noexcept {return num_particles_;}

    particle_view_type operator[](std::size_t i) noexcept
    {
        return particle_view_type{
            masses_[i],    rmasses_[i],
            positions_[i], velocities_[i], forces_main_[i],
            names_[i],     groups_[i]
        };
    }
    particle_const_view_type operator[](std::size_t i) const noexcept
    {
        return particle_const_view_type{
            masses_[i],    rmasses_[i],
            positions_[i], velocities_[i], forces_main_[i],
            names_[i],     groups_[i]
        };
    }
    particle_view_type at(std::size_t i)
    {
        return particle_view_type{
            masses_.at(i),    rmasses_.at(i),
            positions_.at(i), velocities_.at(i), forces_main_.at(i),
            names_.at(i),     groups_.at(i)
        };
    }
    particle_const_view_type at(std::size_t i) const
    {
        return particle_const_view_type{
            masses_.at(i),    rmasses_.at(i),
            positions_.at(i), velocities_.at(i), forces_main_.at(i),
            names_.at(i),     groups_.at(i)
        };
    }

    real_type  mass (std::size_t i) const noexcept {return masses_[i];}
    real_type& mass (std::size_t i)       noexcept {return masses_[i];}
    real_type  rmass(std::size_t i) const noexcept {return rmasses_[i];}
    real_type& rmass(std::size_t i)       noexcept {return rmasses_[i];}

    coordinate_type const& position(std::size_t i) const noexcept {return positions_[i];}
    coordinate_type&       position(std::size_t i)       noexcept {return positions_[i];}
    coordinate_type const& velocity(std::size_t i) const noexcept {return velocities_[i];}
    coordinate_type&       velocity(std::size_t i)       noexcept {return velocities_[i];}
    coordinate_type const& force   (std::size_t i) const noexcept {return forces_main_[i];}
    coordinate_type&       force   (std::size_t i)       noexcept {return forces_main_[i];}

    coordinate_type const&
    force_thread(std::size_t thread_num, std::size_t particle_id) const noexcept
    {
        return forces_threads_[thread_num][particle_id];
    }
    coordinate_type&
    force_thread(std::size_t thread_num, std::size_t particle_id)       noexcept
    {
        return forces_threads_[thread_num][particle_id];
    }

    matrix33_type&       virial()       noexcept {return virial_;}
    matrix33_type const& virial() const noexcept {return virial_;}

    matrix33_type&       virial_thread(std::size_t thread_num)       noexcept
    {
        return virial_threads_[thread_num];
    }
    matrix33_type const& virial_thread(std::size_t thread_num) const noexcept
    {
        return virial_threads_[thread_num];
    }

    void preprocess_forces()  noexcept
    {
        // Do nothing. We already allocated the thread local forces and virials
        // with zero values. Also, in the end of each step, postprocess_forces
        // zero-clears everything.
    }

    // Since all the forces will be calculated in different cores, we need to
    // merge those thread-local forces by summing up those for each particle.
    void postprocess_forces() noexcept
    {
        // sumup virial and zero-clear the thread local virials
//         virial_ = matrix33_type(0,0,0, 0,0,0, 0,0,0); // allow non-parallelized stuff
        for(std::size_t thread_id=0, max_threads=omp_get_max_threads();
                thread_id < max_threads; ++thread_id)
        {
            virial_ += virial_threads_[thread_id];
            virial_threads_[thread_id] = matrix33_type(0,0,0, 0,0,0, 0,0,0);
        }

#pragma omp parallel for
        for(std::size_t i=0; i<this->size(); ++i)
        {
            // sumup all the thread-local forces into the main force.
            // XXX: keep the values in the main force! Some pre-omp interactions
            //      may write its force in it. Clearing main force is a task
            //      for Integrators.
            for(std::size_t thread_id=0, max_threads=omp_get_max_threads();
                    thread_id < max_threads; ++thread_id)
            {
                this->force(i) += this->force_thread(thread_id, i);
                this->force_thread(thread_id, i) =
                    math::make_coordinate<coordinate_type>(0, 0, 0);
            }
        }
        return;
    }

    string_type const& name (std::size_t i) const noexcept {return names_[i];}
    string_type&       name (std::size_t i)       noexcept {return names_[i];}
    string_type const& group(std::size_t i) const noexcept {return groups_[i];}
    string_type&       group(std::size_t i)       noexcept {return groups_[i];}

    boundary_type&       boundary()       noexcept {return boundary_;}
    boundary_type const& boundary() const noexcept {return boundary_;}

    // system attributes like `reference temperature`, `ionic strength`, ...
    // assuming it will not be called so often.
    real_type  attribute(const std::string& key) const {return attributes_.at(key);}
    real_type& attribute(const std::string& key)       {return attributes_[key];}
    bool   has_attribute(const std::string& key) const {return attributes_.count(key) == 1;}
    attribute_type const& attributes() const noexcept {return attributes_;}

    bool  velocity_initialized() const noexcept {return velocity_initialized_;}
    bool& velocity_initialized()       noexcept {return velocity_initialized_;}

    // force_main is initialized or not. force_thread does not matter.
    bool  force_initialized() const noexcept {return force_initialized_;}
    bool& force_initialized()       noexcept {return force_initialized_;}

    coordinate_container_type const& forces() const noexcept {return forces_main_;}
    coordinate_container_type&       forces()       noexcept {return forces_main_;}

  private:

    bool           velocity_initialized_, force_initialized_;
    boundary_type  boundary_;
    attribute_type attributes_;

    matrix33_type  virial_;
    std::vector<matrix33_type, cache_aligned_allocator<matrix33_type>> virial_threads_;

    std::size_t                  num_particles_;
    real_container_type          masses_;
    real_container_type          rmasses_; // r for reciprocal
    coordinate_container_type    positions_;
    coordinate_container_type    velocities_;
    coordinate_container_type    forces_main_;
    // thread-local forces
    std::vector<coordinate_container_type,
                cache_aligned_allocator<coordinate_container_type>
        > forces_threads_;
    string_container_type        names_;
    string_container_type        groups_;

    // names and groups are in Topology class
};

#ifdef MJOLNIR_SEPARATE_BUILD
extern template class System<OpenMPSimulatorTraits<double, UnlimitedBoundary>>;
extern template class System<OpenMPSimulatorTraits<float,  UnlimitedBoundary>>;
extern template class System<OpenMPSimulatorTraits<double, CuboidalPeriodicBoundary>>;
extern template class System<OpenMPSimulatorTraits<float,  CuboidalPeriodicBoundary>>;
#endif

} // mjolnir
#endif// MJOLNIR_SYSTEM_HPP
<|endoftext|>