<commit_before>#include <opencv2/features2d.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/opencv.hpp>
#include <vector>
#include <iostream>
#include <iomanip>

#include "stats.h" // Stats structure definition
#include "utils.h" // Drawing and printing functions

using namespace std;
using namespace cv;

const double akaze_thresh = 3e-4; // AKAZE detection threshold set to locate about 1000 keypoints
const double ransac_thresh = 2.5f; // RANSAC inlier threshold
const double nn_match_ratio = 0.8f; // Nearest-neighbour matching ratio
const int bb_min_inliers = 100; // Minimal number of inliers to draw bounding box
const int stats_update_period = 10; // On-screen statistics are updated every 10 frames

class Tracker
{
public:
    Tracker(Ptr<Feature2D> _detector, Ptr<DescriptorMatcher> _matcher) :
        detector(_detector),
        matcher(_matcher)
    {}

    void setFirstFrame(const Mat frame, vector<Point2f> bb, string title, Stats& stats);
    Mat process(const Mat frame, Stats& stats);
    Ptr<Feature2D> getDetector() {
        return detector;
    }
protected:
    Ptr<Feature2D> detector;
    Ptr<DescriptorMatcher> matcher;
    Mat first_frame, first_desc;
    vector<KeyPoint> first_kp;
    vector<Point2f> object_bb;
};

void Tracker::setFirstFrame(const Mat frame, vector<Point2f> bb, string title, Stats& stats)
{
    first_frame = frame.clone();
    detector->detectAndCompute(first_frame, noArray(), first_kp, first_desc);
    stats.keypoints = (int)first_kp.size();
    drawBoundingBox(first_frame, bb);
    putText(first_frame, title, Point(0, 60), FONT_HERSHEY_PLAIN, 5, Scalar::all(0), 4);
    object_bb = bb;
}

Mat Tracker::process(const Mat frame, Stats& stats)
{
    vector<KeyPoint> kp;
    Mat desc;
    detector->detectAndCompute(frame, noArray(), kp, desc);
    stats.keypoints = (int)kp.size();

    vector< vector<DMatch> > matches;
    vector<KeyPoint> matched1, matched2;
    matcher->knnMatch(first_desc, desc, matches, 2);
    for(unsigned i = 0; i < matches.size(); i++) {
        if(matches[i][0].distance < nn_match_ratio * matches[i][1].distance) {
            matched1.push_back(first_kp[matches[i][0].queryIdx]);
            matched2.push_back(      kp[matches[i][0].trainIdx]);
        }
    }
    stats.matches = (int)matched1.size();

    Mat inlier_mask, homography;
    vector<KeyPoint> inliers1, inliers2;
    vector<DMatch> inlier_matches;
    if(matched1.size() >= 4) {
        homography = findHomography(Points(matched1), Points(matched2),
                                    RANSAC, ransac_thresh, inlier_mask);
    }

    if(matched1.size() < 4 || homography.empty()) {
        Mat res;
        hconcat(first_frame, frame, res);
        stats.inliers = 0;
        stats.ratio = 0;
        return res;
    }
    for(unsigned i = 0; i < matched1.size(); i++) {
        if(inlier_mask.at<uchar>(i)) {
            int new_i = static_cast<int>(inliers1.size());
            inliers1.push_back(matched1[i]);
            inliers2.push_back(matched2[i]);
            inlier_matches.push_back(DMatch(new_i, new_i, 0));
        }
    }
    stats.inliers = (int)inliers1.size();
    stats.ratio = stats.inliers * 1.0 / stats.matches;

    vector<Point2f> new_bb;
    perspectiveTransform(object_bb, new_bb, homography);
    Mat frame_with_bb = frame.clone();
    if(stats.inliers >= bb_min_inliers) {
        drawBoundingBox(frame_with_bb, new_bb);
    }
    Mat res;
    drawMatches(first_frame, inliers1, frame_with_bb, inliers2,
                inlier_matches, res,
                Scalar(255, 0, 0), Scalar(255, 0, 0));
    return res;
}

int main(int argc, char **argv)
{
    if(argc < 4) {
        cerr << "Usage: " << endl <<
                "akaze_track input_path output_path bounding_box" << endl;
        return 1;
    }
    VideoCapture video_in(argv[1]);
    VideoWriter  video_out(argv[2],
                           (int)video_in.get(CAP_PROP_FOURCC),
                           (int)video_in.get(CAP_PROP_FPS),
                           Size(2 * (int)video_in.get(CAP_PROP_FRAME_WIDTH),
                                2 * (int)video_in.get(CAP_PROP_FRAME_HEIGHT)));

    if(!video_in.isOpened()) {
        cerr << "Couldn't open " << argv[1] << endl;
        return 1;
    }
    if(!video_out.isOpened()) {
        cerr << "Couldn't open " << argv[2] << endl;
        return 1;
    }

    vector<Point2f> bb;
    FileStorage fs(argv[3], FileStorage::READ);
    if(fs["bounding_box"].empty()) {
        cerr << "Couldn't read bounding_box from " << argv[3] << endl;
        return 1;
    }
    fs["bounding_box"] >> bb;

    Stats stats, akaze_stats, orb_stats;
    Ptr<AKAZE> akaze = AKAZE::create();
    akaze->setThreshold(akaze_thresh);
    Ptr<ORB> orb = ORB::create();
    orb->setMaxFeatures(stats.keypoints);
    Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create("BruteForce-Hamming");
    Tracker akaze_tracker(akaze, matcher);
    Tracker orb_tracker(orb, matcher);

    Mat frame;
    video_in >> frame;
    akaze_tracker.setFirstFrame(frame, bb, "AKAZE", stats);
    orb_tracker.setFirstFrame(frame, bb, "ORB", stats);

    Stats akaze_draw_stats, orb_draw_stats;
    int frame_count = (int)video_in.get(CAP_PROP_FRAME_COUNT);
    Mat akaze_res, orb_res, res_frame;
    for(int i = 1; i < frame_count; i++) {
        bool update_stats = (i % stats_update_period == 0);
        video_in >> frame;

        akaze_res = akaze_tracker.process(frame, stats);
        akaze_stats += stats;
        if(update_stats) {
            akaze_draw_stats = stats;
        }

        orb->setMaxFeatures(stats.keypoints);
        orb_res = orb_tracker.process(frame, stats);
        orb_stats += stats;
        if(update_stats) {
            orb_draw_stats = stats;
        }

        drawStatistics(akaze_res, akaze_draw_stats);
        drawStatistics(orb_res, orb_draw_stats);
        vconcat(akaze_res, orb_res, res_frame);
        video_out << res_frame;
        cout << i << "/" << frame_count - 1 << endl;
    }
    akaze_stats /= frame_count - 1;
    orb_stats /= frame_count - 1;
    printStatistics("AKAZE", akaze_stats);
    printStatistics("ORB", orb_stats);
    return 0;
}
<commit_msg>- remove zero threshold that prevents ORB from finding any local features<commit_after>#include <opencv2/features2d.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/opencv.hpp>
#include <vector>
#include <iostream>
#include <iomanip>

#include "stats.h" // Stats structure definition
#include "utils.h" // Drawing and printing functions

using namespace std;
using namespace cv;

const double akaze_thresh = 3e-4; // AKAZE detection threshold set to locate about 1000 keypoints
const double ransac_thresh = 2.5f; // RANSAC inlier threshold
const double nn_match_ratio = 0.8f; // Nearest-neighbour matching ratio
const int bb_min_inliers = 100; // Minimal number of inliers to draw bounding box
const int stats_update_period = 10; // On-screen statistics are updated every 10 frames

class Tracker
{
public:
    Tracker(Ptr<Feature2D> _detector, Ptr<DescriptorMatcher> _matcher) :
        detector(_detector),
        matcher(_matcher)
    {}

    void setFirstFrame(const Mat frame, vector<Point2f> bb, string title, Stats& stats);
    Mat process(const Mat frame, Stats& stats);
    Ptr<Feature2D> getDetector() {
        return detector;
    }
protected:
    Ptr<Feature2D> detector;
    Ptr<DescriptorMatcher> matcher;
    Mat first_frame, first_desc;
    vector<KeyPoint> first_kp;
    vector<Point2f> object_bb;
};

void Tracker::setFirstFrame(const Mat frame, vector<Point2f> bb, string title, Stats& stats)
{
    first_frame = frame.clone();
    detector->detectAndCompute(first_frame, noArray(), first_kp, first_desc);
    stats.keypoints = (int)first_kp.size();
    drawBoundingBox(first_frame, bb);
    putText(first_frame, title, Point(0, 60), FONT_HERSHEY_PLAIN, 5, Scalar::all(0), 4);
    object_bb = bb;
}

Mat Tracker::process(const Mat frame, Stats& stats)
{
    vector<KeyPoint> kp;
    Mat desc;
    detector->detectAndCompute(frame, noArray(), kp, desc);
    stats.keypoints = (int)kp.size();

    vector< vector<DMatch> > matches;
    vector<KeyPoint> matched1, matched2;
    matcher->knnMatch(first_desc, desc, matches, 2);
    for(unsigned i = 0; i < matches.size(); i++) {
        if(matches[i][0].distance < nn_match_ratio * matches[i][1].distance) {
            matched1.push_back(first_kp[matches[i][0].queryIdx]);
            matched2.push_back(      kp[matches[i][0].trainIdx]);
        }
    }
    stats.matches = (int)matched1.size();

    Mat inlier_mask, homography;
    vector<KeyPoint> inliers1, inliers2;
    vector<DMatch> inlier_matches;
    if(matched1.size() >= 4) {
        homography = findHomography(Points(matched1), Points(matched2),
                                    RANSAC, ransac_thresh, inlier_mask);
    }

    if(matched1.size() < 4 || homography.empty()) {
        Mat res;
        hconcat(first_frame, frame, res);
        stats.inliers = 0;
        stats.ratio = 0;
        return res;
    }
    for(unsigned i = 0; i < matched1.size(); i++) {
        if(inlier_mask.at<uchar>(i)) {
            int new_i = static_cast<int>(inliers1.size());
            inliers1.push_back(matched1[i]);
            inliers2.push_back(matched2[i]);
            inlier_matches.push_back(DMatch(new_i, new_i, 0));
        }
    }
    stats.inliers = (int)inliers1.size();
    stats.ratio = stats.inliers * 1.0 / stats.matches;

    vector<Point2f> new_bb;
    perspectiveTransform(object_bb, new_bb, homography);
    Mat frame_with_bb = frame.clone();
    if(stats.inliers >= bb_min_inliers) {
        drawBoundingBox(frame_with_bb, new_bb);
    }
    Mat res;
    drawMatches(first_frame, inliers1, frame_with_bb, inliers2,
                inlier_matches, res,
                Scalar(255, 0, 0), Scalar(255, 0, 0));
    return res;
}

int main(int argc, char **argv)
{
    if(argc < 4) {
        cerr << "Usage: " << endl <<
                "akaze_track input_path output_path bounding_box" << endl;
        return 1;
    }
    VideoCapture video_in(argv[1]);
    VideoWriter  video_out(argv[2],
                           (int)video_in.get(CAP_PROP_FOURCC),
                           (int)video_in.get(CAP_PROP_FPS),
                           Size(2 * (int)video_in.get(CAP_PROP_FRAME_WIDTH),
                                2 * (int)video_in.get(CAP_PROP_FRAME_HEIGHT)));

    if(!video_in.isOpened()) {
        cerr << "Couldn't open " << argv[1] << endl;
        return 1;
    }
    if(!video_out.isOpened()) {
        cerr << "Couldn't open " << argv[2] << endl;
        return 1;
    }

    vector<Point2f> bb;
    FileStorage fs(argv[3], FileStorage::READ);
    if(fs["bounding_box"].empty()) {
        cerr << "Couldn't read bounding_box from " << argv[3] << endl;
        return 1;
    }
    fs["bounding_box"] >> bb;

    Stats stats, akaze_stats, orb_stats;
    Ptr<AKAZE> akaze = AKAZE::create();
    akaze->setThreshold(akaze_thresh);
    Ptr<ORB> orb = ORB::create();
    Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create("BruteForce-Hamming");
    Tracker akaze_tracker(akaze, matcher);
    Tracker orb_tracker(orb, matcher);

    Mat frame;
    video_in >> frame;
    akaze_tracker.setFirstFrame(frame, bb, "AKAZE", stats);
    orb_tracker.setFirstFrame(frame, bb, "ORB", stats);

    Stats akaze_draw_stats, orb_draw_stats;
    int frame_count = (int)video_in.get(CAP_PROP_FRAME_COUNT);
    Mat akaze_res, orb_res, res_frame;
    for(int i = 1; i < frame_count; i++) {
        bool update_stats = (i % stats_update_period == 0);
        video_in >> frame;

        akaze_res = akaze_tracker.process(frame, stats);
        akaze_stats += stats;
        if(update_stats) {
            akaze_draw_stats = stats;
        }

        orb->setMaxFeatures(stats.keypoints);
        orb_res = orb_tracker.process(frame, stats);
        orb_stats += stats;
        if(update_stats) {
            orb_draw_stats = stats;
        }

        drawStatistics(akaze_res, akaze_draw_stats);
        drawStatistics(orb_res, orb_draw_stats);
        vconcat(akaze_res, orb_res, res_frame);
        video_out << res_frame;
        cout << i << "/" << frame_count - 1 << endl;
    }
    akaze_stats /= frame_count - 1;
    orb_stats /= frame_count - 1;
    printStatistics("AKAZE", akaze_stats);
    printStatistics("ORB", orb_stats);
    return 0;
}
<|endoftext|>


<commit_before>#include "axisstepper.h"

namespace drv {

Event AxisStepper::getEvent() const {
	return Event::StepperEvent(this->time, this->index(), this->direction);
}

void AxisStepper::_nextStep() {
	//should be implemented in derivatives.
}

}
<commit_msg>Attempt to detect a lack of overriding of AxisStepper::_nextStep()<commit_after>#include "axisstepper.h"

namespace drv {

Event AxisStepper::getEvent() const {
	return Event::StepperEvent(this->time, this->index(), this->direction);
}

void AxisStepper::_nextStep() {
	//should be implemented in derivatives.
	throw std::runtime_error("AxisStepper::_nextStep() must be overriden in any child classes");
}

}
<|endoftext|>


<commit_before>#include <cstdarg>
#include <cstdio>
#include <ctime>
#include <cerrno>
#include <cstring>
#include <sys/utsname.h>
#include <unistd.h>
#include <sys/stat.h>
#include "../utils/utils.hpp"

enum { Bufsz = 256 };

static const char *start4 = "#start data file format 4\n";
static const char *end4 = "#end data file format 4\n";

static void machineid(FILE*);
static void tryprocstatus(FILE*);

void dfpair(FILE *f, const char *key, const char *fmt, ...) {
	char buf[Bufsz];

	int n = snprintf(buf, Bufsz, "#pair  \"%s\"\t\"", key);

	va_list ap;
	va_start(ap, fmt);
	n += vsnprintf(buf+n, Bufsz-n, fmt, ap);
	va_end(ap);

	fprintf(f, "%s\"\n", buf);
}

void dfrowhdr(FILE *f, const char *name, int ncols, ...) {
	char buf[Bufsz];
	int n = snprintf(buf, Bufsz, "#altcols  \"%s\"", name);

	va_list ap;
	va_start(ap, ncols);
	for (int i = 0; i < ncols; i++) {
		char *col = va_arg(ap, char*);
		n += snprintf(buf+n, Bufsz-n, "\t\"%s\"", col);
	}
	va_end(ap);

	fprintf(f, "%s\n", buf);
}

void dfrow(FILE *f, const char *name, const char *colfmt, ...) {
	char buf[Bufsz];
	int n = snprintf(buf, Bufsz, "#altrow  \"%s\"", name);

	va_list ap;
	va_start(ap, colfmt);
	for (unsigned int i = 0; i < strlen(colfmt); i++) {
		double g = 0;
		long d = 0;
		unsigned long u = 0;
		switch (colfmt[i]) {
		case 'g':
			g = va_arg(ap, double);
			n += snprintf(buf+n, Bufsz-n, "\t%g", g);
			break;
		case 'f':
			g = va_arg(ap, double);
			n += snprintf(buf+n, Bufsz-n, "\t%lf", g);
			break;
		case 'd':
			d = va_arg(ap, long);
			n += snprintf(buf+n, Bufsz-n, "\t%ld", d);
			break;
		case 'u':
			u = va_arg(ap, unsigned long);
			n += snprintf(buf+n, Bufsz-n, "\t%lu", u);
			break;
		}
	}
	va_end(ap);

	fprintf(f, "%s\n", buf);
}

void dfheader(FILE *f) {
	fputs(start4, f);

	time_t tm;
	time(&tm);
	char *tstr = ctime(&tm);
	tstr[strlen(tstr)-1] = '\0';
	dfpair(f, "wall start date", "%s", tstr);

	dfpair(f, "wall start time", "%g", walltime());
	machineid(f);
}

void dffooter(FILE *f) {
	dfpair(f, "wall finish time", "%g", walltime());

	time_t tm;
	time(&tm);
	char *tstr = ctime(&tm);
	tstr[strlen(tstr)-1] = '\0';
	dfpair(f, "wall finish date", "%s", tstr);
	tryprocstatus(f);
	fputs(end4, f);
}

void dfprocstatus(FILE *f) {
	tryprocstatus(f);
}

static void machineid(FILE *f) {
	char hname[Bufsz];
	memset(hname, '\0', Bufsz);
	if (gethostname(hname, Bufsz) == -1) {
		warnx(errno, "gethostname failed, unable to print machine id\n");
		return;
	}

	struct utsname u;
	if (uname(&u) == -1) {
		warnx(errno, "uname failed\n");
		dfpair(f, "machine id", "%s", hname);
	}

	dfpair(f, "machine id", "%s-%s-%s-%s", hname, u.sysname, u.release, u.machine);
}

static void tryprocstatus(FILE *out)
{
	static const char *field = "VmPeak:";
	static const char *key = "max virtual kilobytes";
	char buf[Bufsz];

	int n = snprintf(buf, Bufsz, "/proc/%d/status", getpid());
	if (n <= 0 || n > Bufsz)
		return;

	struct stat sb;
	if (stat(buf, &sb) < 0)
		return;

	FILE *in = fopen(buf, "r");

	for (;;) {
		if (!fgets(buf, Bufsz, in))
			break;
		if (!strstr(buf, field))
			continue;
		size_t skip = strspn(buf + strlen(field), " \t");
		char *strt = buf + strlen(field) + skip;
		char *end = strchr(strt, ' ');
		*end = '\0';
		dfpair(out, key, "%s", strt);
		break;
	}

	fclose(in);
}
<commit_msg>Fatal when datafile functions overflow the buffer.  dfpair has a dynamic buffer.<commit_after>#include <cstdarg>
#include <cstdio>
#include <ctime>
#include <cstdlib>
#include <cerrno>
#include <cstring>
#include <cassert>
#include <sys/utsname.h>
#include <unistd.h>
#include <sys/stat.h>
#include "../utils/utils.hpp"

enum { Bufsz = 256 };

static const char *start4 = "#start data file format 4\n";
static const char *end4 = "#end data file format 4\n";

static void dfpair_sz(FILE*, unsigned int, const char*, const char*, va_list);
static void machineid(FILE*);
static void tryprocstatus(FILE*);

void dfpair(FILE *f, const char *key, const char *fmt, ...) {
	char buf[Bufsz];
	int n = snprintf(buf, Bufsz, "#pair  \"%s\"\t\"", key);
	if (n > Bufsz)
		fatal("dfrowhdr: buffer is too small\n");

	va_list ap;
	va_start(ap, fmt);
	unsigned int m = vsnprintf(buf+n, Bufsz-n, fmt, ap);
	va_end(ap);

	if (m > (unsigned int) Bufsz-n) {	// Err, overflow
		va_start(ap, fmt);
		dfpair_sz(f, m + n + 1, key, fmt, ap);
		va_end(ap);
		return;
	}

	fprintf(f, "%s\"\n", buf);
}

static void dfpair_sz(FILE *f, unsigned int sz, const char *key, const char *fmt, va_list ap) {
	char *buf = (char*) malloc(sz * sizeof(*buf));

	unsigned int n = snprintf(buf, sz, "#pair  \"%s\"\t\"", key);
	assert (n <= sz);
	vsnprintf(buf+n, sz-n, fmt, ap);

	fprintf(f, "%s\"\n", buf);
	free(buf);
}

void dfrowhdr(FILE *f, const char *name, int ncols, ...) {
	char buf[Bufsz];
	int n = snprintf(buf, Bufsz, "#altcols  \"%s\"", name);
	if (n > Bufsz)
		fatal("dfrowhdr: buffer is too small\n");

	va_list ap;
	va_start(ap, ncols);
	for (int i = 0; i < ncols; i++) {
		char *col = va_arg(ap, char*);
		unsigned int m = snprintf(buf+n, Bufsz-n, "\t\"%s\"", col);
		if (m > (unsigned int) Bufsz - n)
			fatal("dfrowhdr: buffer is too small\n");
		n += m;
	}
	va_end(ap);

	fprintf(f, "%s\n", buf);
}

void dfrow(FILE *f, const char *name, const char *colfmt, ...) {
	char buf[Bufsz];
	int n = snprintf(buf, Bufsz, "#altrow  \"%s\"", name);
	if (n > Bufsz)
		fatal("dfrowhdr: buffer is too small\n");

	va_list ap;
	va_start(ap, colfmt);
	for (unsigned int i = 0; i < strlen(colfmt); i++) {
		double g = 0;
		long d = 0;
		unsigned long u = 0;
		unsigned int m = 0;
		switch (colfmt[i]) {
		case 'g':
			g = va_arg(ap, double);
			m = snprintf(buf+n, Bufsz-n, "\t%g", g);
			break;
		case 'f':
			g = va_arg(ap, double);
			m = snprintf(buf+n, Bufsz-n, "\t%lf", g);
			break;
		case 'd':
			d = va_arg(ap, long);
			m = snprintf(buf+n, Bufsz-n, "\t%ld", d);
			break;
		case 'u':
			u = va_arg(ap, unsigned long);
			m = snprintf(buf+n, Bufsz-n, "\t%lu", u);
			break;
		}
		if (m > (unsigned int) Bufsz-n)
			fatal("dfrow: buffer is too small\n");
		n += m;
	}
	va_end(ap);

	fprintf(f, "%s\n", buf);
}

void dfheader(FILE *f) {
	fputs(start4, f);

	time_t tm;
	time(&tm);
	char *tstr = ctime(&tm);
	tstr[strlen(tstr)-1] = '\0';
	dfpair(f, "wall start date", "%s", tstr);

	dfpair(f, "wall start time", "%g", walltime());
	machineid(f);
}

void dffooter(FILE *f) {
	dfpair(f, "wall finish time", "%g", walltime());

	time_t tm;
	time(&tm);
	char *tstr = ctime(&tm);
	tstr[strlen(tstr)-1] = '\0';
	dfpair(f, "wall finish date", "%s", tstr);
	tryprocstatus(f);
	fputs(end4, f);
}

void dfprocstatus(FILE *f) {
	tryprocstatus(f);
}

static void machineid(FILE *f) {
	char hname[Bufsz];
	memset(hname, '\0', Bufsz);
	if (gethostname(hname, Bufsz) == -1) {
		warnx(errno, "gethostname failed, unable to print machine id\n");
		return;
	}

	struct utsname u;
	if (uname(&u) == -1) {
		warnx(errno, "uname failed\n");
		dfpair(f, "machine id", "%s", hname);
	}

	dfpair(f, "machine id", "%s-%s-%s-%s", hname, u.sysname, u.release, u.machine);
}

static void tryprocstatus(FILE *out)
{
	static const char *field = "VmPeak:";
	static const char *key = "max virtual kilobytes";
	char buf[Bufsz];

	int n = snprintf(buf, Bufsz, "/proc/%d/status", getpid());
	if (n <= 0 || n > Bufsz)
		return;

	struct stat sb;
	if (stat(buf, &sb) < 0)
		return;

	FILE *in = fopen(buf, "r");

	for (;;) {
		if (!fgets(buf, Bufsz, in))
			break;
		if (!strstr(buf, field))
			continue;
		size_t skip = strspn(buf + strlen(field), " \t");
		char *strt = buf + strlen(field) + skip;
		char *end = strchr(strt, ' ');
		*end = '\0';
		dfpair(out, key, "%s", strt);
		break;
	}

	fclose(in);
}
<|endoftext|>


<commit_before>/*=========================================================================

 Program:   ORFEO Toolbox
 Language:  C++
 Date:      $Date$
 Version:   $Revision$

 Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
 See OTBCopyright.txt for details.

 This software is distributed WITHOUT ANY WARRANTY; without even
 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 PURPOSE.  See the above copyright notices for more information.

 =========================================================================*/

#include "otbWrapperApplication.h"
#include "otbWrapperApplicationFactory.h"

#include "otbVectorImage.h"
#include "otbImage.h"
#include "itkEuclideanDistance.h"
#include "itkImageRegionSplitter.h"
#include "otbStreamingTraits.h"
#include "otbKMeansImageClassificationFilter.h"
#include "itkImageRegionConstIterator.h"
#include "itkListSample.h"
#include "itkWeightedCentroidKdTreeGenerator.h"
#include "itkKdTreeBasedKmeansEstimator.h"
#include "itkMersenneTwisterRandomVariateGenerator.h"

namespace otb
{
namespace Wrapper
{

typedef FloatImageType::PixelType PixelType;

typedef itk::FixedArray<PixelType, 108> SampleType;
typedef itk::Statistics::ListSample<SampleType> ListSampleType;
typedef itk::Statistics::WeightedCentroidKdTreeGenerator<ListSampleType> TreeGeneratorType;
typedef TreeGeneratorType::KdTreeType TreeType;
typedef itk::Statistics::KdTreeBasedKmeansEstimator<TreeType> EstimatorType;

typedef otb::StreamingTraits<FloatVectorImageType> StreamingTraitsType;
typedef itk::ImageRegionSplitter<2> SplitterType;
typedef FloatImageType::RegionType RegionType;

typedef itk::ImageRegionConstIterator<FloatVectorImageType> IteratorType;
typedef itk::ImageRegionConstIterator<UInt8ImageType> LabeledIteratorType;

typedef otb::KMeansImageClassificationFilter<FloatVectorImageType, UInt8ImageType, 108> ClassificationFilterType;

class KMeansClassification: public Application
{
public:
  /** Standard class typedefs. */
  typedef KMeansClassification Self;
  typedef Application Superclass;
  typedef itk::SmartPointer<Self> Pointer;
  typedef itk::SmartPointer<const Self> ConstPointer;

  /** Standard macro */
  itkNewMacro(Self);

  itkTypeMacro(KMeansClassification, otb::Application);

private:
  KMeansClassification()
  {
    SetName("KMeansClassification");
    SetDescription("Unsupervised KMeans image classification.");
  }

  virtual ~KMeansClassification()
  {
  }

  void DoCreateParameters()
  {

    AddParameter(ParameterType_InputImage, "in", "Input Image");
    AddParameter(ParameterType_OutputImage, "out", "Output Image");
    AddParameter(ParameterType_InputImage, "vm", "Validity Mask");
    AddParameter(ParameterType_Int, "ts", "Size of the training set");
    SetParameterInt("ts", 100);
    AddParameter(ParameterType_Float, "tp", "Probability for a sample to be selected in the training set");
    SetParameterFloat("tp", 0.5);
    AddParameter(ParameterType_Int, "nc", "Number of classes");
    SetParameterInt("nc", 3);
    AddParameter(ParameterType_Float, "cp", "Probability for a pixel to be selected as an initial class centroid");
    SetParameterFloat("cp", 0.8);
    AddParameter(ParameterType_Int, "sl", "Number of lines for each streaming block");
    SetParameterInt("sl", 1000);
  }

  void DoUpdateParameters()
  {
    // Nothing to do here : all parameters are independent
  }

  void DoExecute()
  {
    GetLogger()->Debug("Entering DoExecute\n");

    // initiating random number generation
    itk::Statistics::MersenneTwisterRandomVariateGenerator::Pointer
        randomGen = itk::Statistics::MersenneTwisterRandomVariateGenerator::New();
    m_InImage = GetParameterImage("in");
    std::cout<<"mask in progress"<<std::endl;

    UInt8ImageType::Pointer maskImage = GetParameterUInt8Image("vm");

    std::cout<<"mask in progress done"<<std::endl;
    std::ostringstream message("");

    const unsigned int nbsamples = GetParameterInt("ts");
    const double trainingProb = GetParameterFloat("tp");
    const double initProb = GetParameterFloat("cp");
    const unsigned int nb_classes = GetParameterInt("nc");
    const unsigned int nbLinesForStreaming = GetParameterInt("sl");

    /*******************************************/
    /*           Sampling data                 */
    /*******************************************/
    GetLogger()->Info("-- SAMPLING DATA --");

    // Update input images information
    m_InImage->UpdateOutputInformation();
    maskImage->UpdateOutputInformation();

    if (m_InImage->GetLargestPossibleRegion() != maskImage->GetLargestPossibleRegion())
      {
      GetLogger()->Error("Mask image and input image have different sizes.");
      }

    RegionType largestRegion = m_InImage->GetLargestPossibleRegion();

    // Setting up local streaming capabilities
    SplitterType::Pointer splitter = SplitterType::New();
    unsigned int
        numberOfStreamDivisions = StreamingTraitsType::CalculateNumberOfStreamDivisions(
                                                                                m_InImage,
                                                                                largestRegion,
                                                                                splitter,
                                                                                otb::SET_BUFFER_NUMBER_OF_LINES,
                                                                                0, 0, nbLinesForStreaming);

    message.clear();
    message << "The images will be streamed into " << numberOfStreamDivisions << " parts.";
    GetLogger()->Info(message.str());

    // Training sample lists
    ListSampleType::Pointer sampleList = ListSampleType::New();
    EstimatorType::ParametersType initialMeans(108 * nb_classes);
    initialMeans.Fill(0);
    unsigned int init_means_index = 0;

    // Sample dimension and max dimension
    unsigned int maxDimension = SampleType::Dimension;
    unsigned int sampleSize = std::min(m_InImage->GetNumberOfComponentsPerPixel(), maxDimension);
    unsigned int totalSamples = 0;

    message.clear();
    message << "Sample max possible dimension: " << maxDimension << std::endl;
    GetLogger()->Info(message.str());
    message.clear();
    message << "The following sample size will be used: " << sampleSize << std::endl;
    GetLogger()->Info(message.str());
    // local streaming variables
    unsigned int piece = 0;
    RegionType streamingRegion;

    while ((totalSamples < nbsamples) && (init_means_index < 108 * nb_classes))
      {
      double random = randomGen->GetVariateWithClosedRange();
      piece = static_cast<unsigned int> (random * numberOfStreamDivisions);

      streamingRegion = splitter->GetSplit(piece, numberOfStreamDivisions, largestRegion);

      message.clear();
      message << "Processing region: " << streamingRegion << std::endl;
      GetLogger()->Info(message.str());

      m_InImage->SetRequestedRegion(streamingRegion);
      m_InImage->PropagateRequestedRegion();
      m_InImage->UpdateOutputData();

      maskImage->SetRequestedRegion(streamingRegion);
      maskImage->PropagateRequestedRegion();
      maskImage->UpdateOutputData();

      IteratorType it(m_InImage, streamingRegion);
      LabeledIteratorType m_MaskIt(maskImage, streamingRegion);

      it.GoToBegin();
      m_MaskIt.GoToBegin();

      unsigned int localNbSamples = 0;

      // Loop on the image
      while (!it.IsAtEnd() && !m_MaskIt.IsAtEnd() && (totalSamples < nbsamples)
          && (init_means_index < (108 * nb_classes)))
        {
        // If the current pixel is labeled
        if (m_MaskIt.Get() > 0)
          {
          if ((rand() < trainingProb * RAND_MAX))
            {
            SampleType newSample;

            // build the sample
            newSample.Fill(0);
            for (unsigned int i = 0; i < sampleSize; ++i)
              {
              newSample[i] = it.Get()[i];
              }
            // Update the the sample lists
            sampleList->PushBack(newSample);
            ++totalSamples;
            ++localNbSamples;
            }
          else
            if ((init_means_index < 108 * nb_classes) && (rand() < initProb * RAND_MAX))
              {
              for (unsigned int i = 0; i < sampleSize; ++i)
                {
                initialMeans[init_means_index + i] = it.Get()[i];
                }
              init_means_index += 108;
              }
          }
        ++it;
        ++m_MaskIt;
        }

      message.clear();
      message << localNbSamples << " samples added to the training set." << std::endl;
      GetLogger()->Info(message.str());

      }

    message.clear();
    message << "The final training set contains " << totalSamples << " samples." << std::endl;
    GetLogger()->Info(message.str());

    message.clear();
    message << "Data sampling completed." << std::endl;
    GetLogger()->Info(message.str());

    /*******************************************/
    /*           Learning                      */
    /*******************************************/
    message.clear();
    message << "-- LEARNING --" << std::endl;
    message << "Initial centroids are: " << std::endl;
    GetLogger()->Info(message.str());
    message.clear();
    for (unsigned int i = 0; i < nb_classes; ++i)
      {
      message << "Class " << i << ": ";
      for (unsigned int j = 0; j < sampleSize; ++j)
        {
        message << initialMeans[i * 108 + j] << "\t";
        }
      message << std::endl;
      }
    message << std::endl;

    message.clear();
    message << "Starting optimization." << std::endl;
    message << std::endl;
    GetLogger()->Info(message.str());

    EstimatorType::Pointer estimator = EstimatorType::New();

    TreeGeneratorType::Pointer treeGenerator = TreeGeneratorType::New();
    treeGenerator->SetSample(sampleList);
    treeGenerator->SetBucketSize(100);
    treeGenerator->Update();

    estimator->SetParameters(initialMeans);
    estimator->SetKdTree(treeGenerator->GetOutput());
    estimator->SetMaximumIteration(100000000);
    estimator->SetCentroidPositionChangesThreshold(0.001);
    estimator->StartOptimization();

    EstimatorType::ParametersType estimatedMeans = estimator->GetParameters();
    message.clear();
    message << "Optimization completed." << std::endl;
    message << std::endl;
    message << "Estimated centroids are: " << std::endl;

    for (unsigned int i = 0; i < nb_classes; ++i)
      {
      message << "Class " << i << ": ";
      for (unsigned int j = 0; j < sampleSize; ++j)
        {
        message << estimatedMeans[i * 108 + j] << "\t";
        }
      message << std::endl;
      }

    message << std::endl;
    message << "Learning completed." << std::endl;
    message << std::endl;
    GetLogger()->Info(message.str());

    /*******************************************/
    /*           Classification                */
    /*******************************************/
    message.clear();
    message << "-- CLASSIFICATION --" << std::endl;
    message << std::endl;
    GetLogger()->Info(message.str());

    m_Classifier = ClassificationFilterType::New();

    m_Classifier->SetInput(m_InImage);
    m_Classifier->SetInputMask(maskImage);

    m_Classifier->SetCentroids(estimator->GetParameters());

    SetParameterOutputImage<UInt8ImageType> ("out", m_Classifier->GetOutput());

  }

  ClassificationFilterType::Pointer m_Classifier;
  FloatVectorImageType::Pointer m_InImage;

};

}
}

OTB_APPLICATION_EXPORT(otb::Wrapper::KMeansClassification)

<commit_msg>DOC : KMeans Classification Qt application doc update.<commit_after>/*=========================================================================

 Program:   ORFEO Toolbox
 Language:  C++
 Date:      $Date$
 Version:   $Revision$

 Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
 See OTBCopyright.txt for details.

 This software is distributed WITHOUT ANY WARRANTY; without even
 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 PURPOSE.  See the above copyright notices for more information.

 =========================================================================*/

#include "otbWrapperApplication.h"
#include "otbWrapperApplicationFactory.h"

#include "otbVectorImage.h"
#include "otbImage.h"
#include "itkEuclideanDistance.h"
#include "itkImageRegionSplitter.h"
#include "otbStreamingTraits.h"
#include "otbKMeansImageClassificationFilter.h"
#include "itkImageRegionConstIterator.h"
#include "itkListSample.h"
#include "itkWeightedCentroidKdTreeGenerator.h"
#include "itkKdTreeBasedKmeansEstimator.h"
#include "itkMersenneTwisterRandomVariateGenerator.h"

namespace otb
{
namespace Wrapper
{

typedef FloatImageType::PixelType PixelType;

typedef itk::FixedArray<PixelType, 108> SampleType;
typedef itk::Statistics::ListSample<SampleType> ListSampleType;
typedef itk::Statistics::WeightedCentroidKdTreeGenerator<ListSampleType> TreeGeneratorType;
typedef TreeGeneratorType::KdTreeType TreeType;
typedef itk::Statistics::KdTreeBasedKmeansEstimator<TreeType> EstimatorType;

typedef otb::StreamingTraits<FloatVectorImageType> StreamingTraitsType;
typedef itk::ImageRegionSplitter<2> SplitterType;
typedef FloatImageType::RegionType RegionType;

typedef itk::ImageRegionConstIterator<FloatVectorImageType> IteratorType;
typedef itk::ImageRegionConstIterator<UInt8ImageType> LabeledIteratorType;

typedef otb::KMeansImageClassificationFilter<FloatVectorImageType, UInt8ImageType, 108> ClassificationFilterType;

class KMeansClassification: public Application
{
public:
  /** Standard class typedefs. */
  typedef KMeansClassification Self;
  typedef Application Superclass;
  typedef itk::SmartPointer<Self> Pointer;
  typedef itk::SmartPointer<const Self> ConstPointer;

  /** Standard macro */
  itkNewMacro(Self);

  itkTypeMacro(KMeansClassification, otb::Application);

private:
  KMeansClassification()
  {
    SetName("KMeansClassification");
    SetDescription("Unsupervised KMeans image classification");

    SetDocName("Unsupervised KMeans image classification Application");
    SetDocLongDescription("Performs Unsupervised KMeans image classification.");
    SetDocLimitations("None");
    SetDocAuthors("OTB-Team");
    SetDocSeeAlso(" ");
    SetDocCLExample("otbApplicationLauncherCommandLine KMeansClassification "
      "--in ${OTB-Data}/Input/poupees_sub.png --vm ${OTB-Data}/Input/mask_KMeans.png "
      "--ts 100 --tp 0.6 --nc 5 --cp 0.9 --sl 100 --out ClassificationFilterOuptut.tif ");
    AddDocTag("Classification");

  }

  virtual ~KMeansClassification()
  {
  }

  void DoCreateParameters()
  {

    AddParameter(ParameterType_InputImage, "in", "Input Image");
    SetParameterDescription("in","Input image filename.");
    AddParameter(ParameterType_OutputImage, "out", "Output Image");
    SetParameterDescription("out","Output image filename.");
    AddParameter(ParameterType_InputImage, "vm", "Validity Mask");
    SetParameterDescription("vm","Validity mask. Only non-zero pixels will be used to estimate KMeans modes.");
    AddParameter(ParameterType_Int, "ts", "Training set size");
    SetParameterDescription("ts", "Size of the training set.");
    SetParameterInt("ts", 100);
    AddParameter(ParameterType_Float, "tp", "Training set sample selection probability");
    SetParameterDescription("tp", "Probability for a sample to be selected in the training set.");
    SetParameterFloat("tp", 0.5);
    AddParameter(ParameterType_Int, "nc", "Number of classes");
    SetParameterDescription("nc","number of modes, which will be used to generate class membership.");
    SetParameterInt("nc", 3);
    AddParameter(ParameterType_Float, "cp", "Initial class centroid probability");
    SetParameterDescription("cp", "Probability for a pixel to be selected as an initial class centroid");
    SetParameterFloat("cp", 0.8);
    AddParameter(ParameterType_Int, "sl", "Number of lines for each streaming block");
    SetParameterDescription("sl","input image will be divided into sl lines.");
    SetParameterInt("sl", 1000);
  }

  void DoUpdateParameters()
  {
    // Nothing to do here : all parameters are independent
  }

  void DoExecute()
  {
    GetLogger()->Debug("Entering DoExecute\n");

    // initiating random number generation
    itk::Statistics::MersenneTwisterRandomVariateGenerator::Pointer
        randomGen = itk::Statistics::MersenneTwisterRandomVariateGenerator::New();
    m_InImage = GetParameterImage("in");
    std::cout<<"mask in progress"<<std::endl;

    UInt8ImageType::Pointer maskImage = GetParameterUInt8Image("vm");

    std::cout<<"mask in progress done"<<std::endl;
    std::ostringstream message("");

    const unsigned int nbsamples = GetParameterInt("ts");
    const double trainingProb = GetParameterFloat("tp");
    const double initProb = GetParameterFloat("cp");
    const unsigned int nb_classes = GetParameterInt("nc");
    const unsigned int nbLinesForStreaming = GetParameterInt("sl");

    /*******************************************/
    /*           Sampling data                 */
    /*******************************************/
    GetLogger()->Info("-- SAMPLING DATA --");

    // Update input images information
    m_InImage->UpdateOutputInformation();
    maskImage->UpdateOutputInformation();

    if (m_InImage->GetLargestPossibleRegion() != maskImage->GetLargestPossibleRegion())
      {
      GetLogger()->Error("Mask image and input image have different sizes.");
      }

    RegionType largestRegion = m_InImage->GetLargestPossibleRegion();

    // Setting up local streaming capabilities
    SplitterType::Pointer splitter = SplitterType::New();
    unsigned int
        numberOfStreamDivisions = StreamingTraitsType::CalculateNumberOfStreamDivisions(
                                                                                m_InImage,
                                                                                largestRegion,
                                                                                splitter,
                                                                                otb::SET_BUFFER_NUMBER_OF_LINES,
                                                                                0, 0, nbLinesForStreaming);

    message.clear();
    message << "The images will be streamed into " << numberOfStreamDivisions << " parts.";
    GetLogger()->Info(message.str());

    // Training sample lists
    ListSampleType::Pointer sampleList = ListSampleType::New();
    EstimatorType::ParametersType initialMeans(108 * nb_classes);
    initialMeans.Fill(0);
    unsigned int init_means_index = 0;

    // Sample dimension and max dimension
    unsigned int maxDimension = SampleType::Dimension;
    unsigned int sampleSize = std::min(m_InImage->GetNumberOfComponentsPerPixel(), maxDimension);
    unsigned int totalSamples = 0;

    message.clear();
    message << "Sample max possible dimension: " << maxDimension << std::endl;
    GetLogger()->Info(message.str());
    message.clear();
    message << "The following sample size will be used: " << sampleSize << std::endl;
    GetLogger()->Info(message.str());
    // local streaming variables
    unsigned int piece = 0;
    RegionType streamingRegion;

    while ((totalSamples < nbsamples) && (init_means_index < 108 * nb_classes))
      {
      double random = randomGen->GetVariateWithClosedRange();
      piece = static_cast<unsigned int> (random * numberOfStreamDivisions);

      streamingRegion = splitter->GetSplit(piece, numberOfStreamDivisions, largestRegion);

      message.clear();
      message << "Processing region: " << streamingRegion << std::endl;
      GetLogger()->Info(message.str());

      m_InImage->SetRequestedRegion(streamingRegion);
      m_InImage->PropagateRequestedRegion();
      m_InImage->UpdateOutputData();

      maskImage->SetRequestedRegion(streamingRegion);
      maskImage->PropagateRequestedRegion();
      maskImage->UpdateOutputData();

      IteratorType it(m_InImage, streamingRegion);
      LabeledIteratorType m_MaskIt(maskImage, streamingRegion);

      it.GoToBegin();
      m_MaskIt.GoToBegin();

      unsigned int localNbSamples = 0;

      // Loop on the image
      while (!it.IsAtEnd() && !m_MaskIt.IsAtEnd() && (totalSamples < nbsamples)
          && (init_means_index < (108 * nb_classes)))
        {
        // If the current pixel is labeled
        if (m_MaskIt.Get() > 0)
          {
          if ((rand() < trainingProb * RAND_MAX))
            {
            SampleType newSample;

            // build the sample
            newSample.Fill(0);
            for (unsigned int i = 0; i < sampleSize; ++i)
              {
              newSample[i] = it.Get()[i];
              }
            // Update the the sample lists
            sampleList->PushBack(newSample);
            ++totalSamples;
            ++localNbSamples;
            }
          else
            if ((init_means_index < 108 * nb_classes) && (rand() < initProb * RAND_MAX))
              {
              for (unsigned int i = 0; i < sampleSize; ++i)
                {
                initialMeans[init_means_index + i] = it.Get()[i];
                }
              init_means_index += 108;
              }
          }
        ++it;
        ++m_MaskIt;
        }

      message.clear();
      message << localNbSamples << " samples added to the training set." << std::endl;
      GetLogger()->Info(message.str());

      }

    message.clear();
    message << "The final training set contains " << totalSamples << " samples." << std::endl;
    GetLogger()->Info(message.str());

    message.clear();
    message << "Data sampling completed." << std::endl;
    GetLogger()->Info(message.str());

    /*******************************************/
    /*           Learning                      */
    /*******************************************/
    message.clear();
    message << "-- LEARNING --" << std::endl;
    message << "Initial centroids are: " << std::endl;
    GetLogger()->Info(message.str());
    message.clear();
    for (unsigned int i = 0; i < nb_classes; ++i)
      {
      message << "Class " << i << ": ";
      for (unsigned int j = 0; j < sampleSize; ++j)
        {
        message << initialMeans[i * 108 + j] << "\t";
        }
      message << std::endl;
      }
    message << std::endl;

    message.clear();
    message << "Starting optimization." << std::endl;
    message << std::endl;
    GetLogger()->Info(message.str());

    EstimatorType::Pointer estimator = EstimatorType::New();

    TreeGeneratorType::Pointer treeGenerator = TreeGeneratorType::New();
    treeGenerator->SetSample(sampleList);
    treeGenerator->SetBucketSize(100);
    treeGenerator->Update();

    estimator->SetParameters(initialMeans);
    estimator->SetKdTree(treeGenerator->GetOutput());
    estimator->SetMaximumIteration(100000000);
    estimator->SetCentroidPositionChangesThreshold(0.001);
    estimator->StartOptimization();

    EstimatorType::ParametersType estimatedMeans = estimator->GetParameters();
    message.clear();
    message << "Optimization completed." << std::endl;
    message << std::endl;
    message << "Estimated centroids are: " << std::endl;

    for (unsigned int i = 0; i < nb_classes; ++i)
      {
      message << "Class " << i << ": ";
      for (unsigned int j = 0; j < sampleSize; ++j)
        {
        message << estimatedMeans[i * 108 + j] << "\t";
        }
      message << std::endl;
      }

    message << std::endl;
    message << "Learning completed." << std::endl;
    message << std::endl;
    GetLogger()->Info(message.str());

    /*******************************************/
    /*           Classification                */
    /*******************************************/
    message.clear();
    message << "-- CLASSIFICATION --" << std::endl;
    message << std::endl;
    GetLogger()->Info(message.str());

    m_Classifier = ClassificationFilterType::New();

    m_Classifier->SetInput(m_InImage);
    m_Classifier->SetInputMask(maskImage);

    m_Classifier->SetCentroids(estimator->GetParameters());

    SetParameterOutputImage<UInt8ImageType> ("out", m_Classifier->GetOutput());

  }

  ClassificationFilterType::Pointer m_Classifier;
  FloatVectorImageType::Pointer m_InImage;

};

}
}

OTB_APPLICATION_EXPORT(otb::Wrapper::KMeansClassification)

<|endoftext|>


<commit_before>/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2003 Robert Osfield
 *
 * This application is open source and may be redistributed and/or modified
 * freely and without restriction, both in commericial and non commericial applications,
 * as long as this copyright notice is maintained.
 *
 * This application is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/

#include <osgDB/ReadFile>
#include <osgUtil/Optimizer>
#include <osgProducer/Viewer>
#include <osg/CoordinateSystemNode>

int main( int argc, char **argv )
{

    // use an ArgumentParser object to manage the program arguments.
    osg::ArgumentParser arguments(&argc,argv);

    // set up the usage document, in case we need to print out how to use this program.
    arguments.getApplicationUsage()->setApplicationName(arguments.getApplicationName());
    arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" is the standard OpenSceneGraph example which loads and visualises 3d models.");
    arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] filename ...");
    arguments.getApplicationUsage()->addCommandLineOption("-h or --help","Display this information");

    // construct the viewer.
    osgProducer::Viewer viewer(arguments);

    // set up the value with sensible default event handlers.
    viewer.setUpViewer(osgProducer::Viewer::STANDARD_SETTINGS);

    // get details on keyboard and mouse bindings used by the viewer.
    viewer.getUsage(*arguments.getApplicationUsage());

    // if user request help write it out to cout.
    if (arguments.read("-h") || arguments.read("--help"))
    {
        arguments.getApplicationUsage()->write(std::cout);
        return 1;
    }

    // report any errors if they have occured when parsing the program aguments.
    if (arguments.errors())
    {
        arguments.writeErrorMessages(std::cout);
        return 1;
    }

    if (arguments.argc()<=1)
    {
        arguments.getApplicationUsage()->write(std::cout,osg::ApplicationUsage::COMMAND_LINE_OPTION);
        return 1;
    }

    osg::Timer_t start_tick = osg::Timer::instance()->tick();

    // read the scene from the list of file specified commandline args.
    osg::ref_ptr<osg::Node> loadedModel = osgDB::readNodeFiles(arguments);

    // if no model has been successfully loaded report failure.
    if (!loadedModel)
    {
        std::cout << arguments.getApplicationName() <<": No data loaded" << std::endl;
        return 1;
    }

    // any option left unread are converted into errors to write out later.
    arguments.reportRemainingOptionsAsUnrecognized();

    // report any errors if they have occured when parsing the program aguments.
    if (arguments.errors())
    {
        arguments.writeErrorMessages(std::cout);
    }

    osg::Timer_t end_tick = osg::Timer::instance()->tick();

    std::cout << "Time to load = "<<osg::Timer::instance()->delta_s(start_tick,end_tick)<<std::endl;

    // optimize the scene graph, remove rendundent nodes and state etc.
    osgUtil::Optimizer optimizer;
    optimizer.optimize(loadedModel.get());

    // pass the loaded scene graph to the viewer.
    viewer.setSceneData(loadedModel.get());

    // create the windows and run the threads.
    viewer.realize();

    while( !viewer.done() )
    {
        // wait for all cull and draw threads to complete.
        viewer.sync();

        // update the scene by traversing it with the the update visitor which will
        // call all node update callbacks and animations.
        viewer.update();

        // fire off the cull and draw traversals of the scene.
        viewer.frame();

    }

    // wait for all cull and draw threads to complete before exit.
    viewer.sync();

    return 0;
}

<commit_msg>Added --image and --dem documentation to command line parameters<commit_after>/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2003 Robert Osfield
 *
 * This application is open source and may be redistributed and/or modified
 * freely and without restriction, both in commericial and non commericial applications,
 * as long as this copyright notice is maintained.
 *
 * This application is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/

#include <osgDB/ReadFile>
#include <osgUtil/Optimizer>
#include <osgProducer/Viewer>
#include <osg/CoordinateSystemNode>

int main( int argc, char **argv )
{

    // use an ArgumentParser object to manage the program arguments.
    osg::ArgumentParser arguments(&argc,argv);

    // set up the usage document, in case we need to print out how to use this program.
    arguments.getApplicationUsage()->setApplicationName(arguments.getApplicationName());
    arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" is the standard OpenSceneGraph example which loads and visualises 3d models.");
    arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] filename ...");
    arguments.getApplicationUsage()->addCommandLineOption("--image <filename>","Load an image and render it on a quad");
    arguments.getApplicationUsage()->addCommandLineOption("--dem <filename>","Load an image/DEM and render it on a HeightField");
    arguments.getApplicationUsage()->addCommandLineOption("-h or --help","Display this information");

    // construct the viewer.
    osgProducer::Viewer viewer(arguments);

    // set up the value with sensible default event handlers.
    viewer.setUpViewer(osgProducer::Viewer::STANDARD_SETTINGS);

    // get details on keyboard and mouse bindings used by the viewer.
    viewer.getUsage(*arguments.getApplicationUsage());

    // if user request help write it out to cout.
    if (arguments.read("-h") || arguments.read("--help"))
    {
        arguments.getApplicationUsage()->write(std::cout);
        return 1;
    }

    // report any errors if they have occured when parsing the program aguments.
    if (arguments.errors())
    {
        arguments.writeErrorMessages(std::cout);
        return 1;
    }

    if (arguments.argc()<=1)
    {
        arguments.getApplicationUsage()->write(std::cout,osg::ApplicationUsage::COMMAND_LINE_OPTION);
        return 1;
    }

    osg::Timer_t start_tick = osg::Timer::instance()->tick();

    // read the scene from the list of file specified commandline args.
    osg::ref_ptr<osg::Node> loadedModel = osgDB::readNodeFiles(arguments);

    // if no model has been successfully loaded report failure.
    if (!loadedModel)
    {
        std::cout << arguments.getApplicationName() <<": No data loaded" << std::endl;
        return 1;
    }

    // any option left unread are converted into errors to write out later.
    arguments.reportRemainingOptionsAsUnrecognized();

    // report any errors if they have occured when parsing the program aguments.
    if (arguments.errors())
    {
        arguments.writeErrorMessages(std::cout);
    }

    osg::Timer_t end_tick = osg::Timer::instance()->tick();

    std::cout << "Time to load = "<<osg::Timer::instance()->delta_s(start_tick,end_tick)<<std::endl;

    // optimize the scene graph, remove rendundent nodes and state etc.
    osgUtil::Optimizer optimizer;
    optimizer.optimize(loadedModel.get());

    // pass the loaded scene graph to the viewer.
    viewer.setSceneData(loadedModel.get());

    // create the windows and run the threads.
    viewer.realize();

    while( !viewer.done() )
    {
        // wait for all cull and draw threads to complete.
        viewer.sync();

        // update the scene by traversing it with the the update visitor which will
        // call all node update callbacks and animations.
        viewer.update();

        // fire off the cull and draw traversals of the scene.
        viewer.frame();

    }

    // wait for all cull and draw threads to complete before exit.
    viewer.sync();

    return 0;
}

<|endoftext|>


<commit_before>// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <vespa/document/base/documentid.h>
#include <vespa/document/datatype/documenttype.h>
#include <vespa/document/fieldvalue/document.h>
#include <vespa/document/fieldvalue/bytefieldvalue.h>
#include <vespa/document/fieldvalue/doublefieldvalue.h>
#include <vespa/document/fieldvalue/floatfieldvalue.h>
#include <vespa/document/fieldvalue/intfieldvalue.h>
#include <vespa/document/fieldvalue/longfieldvalue.h>
#include <vespa/document/fieldvalue/rawfieldvalue.h>
#include <vespa/document/fieldvalue/shortfieldvalue.h>
#include <vespa/document/fieldvalue/stringfieldvalue.h>
#include <vespa/vespalib/testkit/test_kit.h>
#include <vespa/searchlib/common/matching_elements.h>
#include <vespa/searchsummary/docsummary/docsumwriter.h>
#include <vespa/searchsummary/docsummary/docsumstate.h>
#include <vespa/searchsummary/docsummary/keywordextractor.h>
#include <vespa/searchsummary/docsummary/docsum_store_document.h>
#include <vespa/vespalib/data/slime/slime.h>
#include <vespa/vespalib/data/smart_buffer.h>
#include <vespa/vespalib/util/size_literals.h>

using namespace vespalib::slime::convenience;
using namespace search::docsummary;
using vespalib::slime::BinaryFormat;
using search::MatchingElements;
using document::ByteFieldValue;
using document::DataType;
using document::Document;
using document::DocumentId;
using document::DocumentType;
using document::DoubleFieldValue;
using document::Field;
using document::FloatFieldValue;
using document::IntFieldValue;
using document::LongFieldValue;
using document::RawFieldValue;
using document::ShortFieldValue;
using document::StringFieldValue;
using document::StructDataType;
using document::StructFieldValue;

namespace {

struct DocsumFixture : IDocsumStore, GetDocsumsStateCallback {
    std::unique_ptr<DynamicDocsumWriter> writer;
    StructDataType  int_pair_type;
    DocumentType    doc_type;
    GetDocsumsState state;
    DocsumFixture();
    ~DocsumFixture() override;
    void getDocsum(Slime &slime) {
        Slime slimeOut;
        SlimeInserter inserter(slimeOut);
        writer->WriteDocsum(1u, &state, this, inserter);
        vespalib::SmartBuffer buf(4_Ki);
        BinaryFormat::encode(slimeOut, buf);
        EXPECT_GREATER(BinaryFormat::decode(buf.obtain(), slime), 0u);
    }
    uint32_t getNumDocs() const override { return 2; }
    std::unique_ptr<const IDocsumStoreDocument> getMappedDocsum(uint32_t docid) override {
        EXPECT_EQUAL(1u, docid);
        auto doc = std::make_unique<Document>(doc_type, DocumentId("id:test:test::0"));
        doc->setValue("int_field", IntFieldValue(4));
        doc->setValue("short_field", ShortFieldValue(2));
        doc->setValue("byte_field", ByteFieldValue(1));
        doc->setValue("float_field", FloatFieldValue(4.5));
        doc->setValue("double_field", DoubleFieldValue(8.75));
        doc->setValue("int64_field", LongFieldValue(8));
        doc->setValue("string_field", StringFieldValue("string"));
        doc->setValue("data_field", RawFieldValue("data"));
        doc->setValue("longstring_field", StringFieldValue("long_string"));
        doc->setValue("longdata_field", RawFieldValue("long_data"));
        {
            StructFieldValue int_pair(int_pair_type);
            int_pair.setValue("foo", IntFieldValue(1));
            int_pair.setValue("bar", IntFieldValue(2));
            doc->setValue("int_pair_field", int_pair);
        }
        return std::make_unique<DocsumStoreDocument>(std::move(doc));
    }
    void FillSummaryFeatures(GetDocsumsState&) override { }
    void FillRankFeatures(GetDocsumsState&) override { }
    std::unique_ptr<MatchingElements> fill_matching_elements(const search::MatchingElementsFields &) override { abort(); }
};

DocsumFixture::DocsumFixture()
    : writer(),
      int_pair_type("int_pair"),
      doc_type("test"),
      state(*this)
{
    auto config = std::make_unique<ResultConfig>();
    ResultClass *cfg = config->AddResultClass("default", 0);
    EXPECT_TRUE(cfg != nullptr);
    EXPECT_TRUE(cfg->AddConfigEntry("int_field", RES_INT));
    EXPECT_TRUE(cfg->AddConfigEntry("short_field", RES_SHORT));
    EXPECT_TRUE(cfg->AddConfigEntry("byte_field", RES_BYTE));
    EXPECT_TRUE(cfg->AddConfigEntry("float_field", RES_FLOAT));
    EXPECT_TRUE(cfg->AddConfigEntry("double_field", RES_DOUBLE));
    EXPECT_TRUE(cfg->AddConfigEntry("int64_field", RES_INT64));
    EXPECT_TRUE(cfg->AddConfigEntry("string_field", RES_STRING));
    EXPECT_TRUE(cfg->AddConfigEntry("data_field", RES_DATA));
    EXPECT_TRUE(cfg->AddConfigEntry("longstring_field", RES_LONG_STRING));
    EXPECT_TRUE(cfg->AddConfigEntry("longdata_field", RES_LONG_DATA));
    EXPECT_TRUE(cfg->AddConfigEntry("int_pair_field", RES_JSONSTRING));
    config->set_default_result_class_id(0);
    config->CreateEnumMaps();
    writer = std::make_unique<DynamicDocsumWriter>(std::move(config), std::unique_ptr<KeywordExtractor>());
    int_pair_type.addField(Field("foo", *DataType::INT));
    int_pair_type.addField(Field("bar", *DataType::INT));
    doc_type.addField(Field("int_field", *DataType::INT));
    doc_type.addField(Field("short_field", *DataType::SHORT));
    doc_type.addField(Field("byte_field", *DataType::BYTE));
    doc_type.addField(Field("float_field", *DataType::FLOAT));
    doc_type.addField(Field("double_field", *DataType::DOUBLE));
    doc_type.addField(Field("int64_field", *DataType::LONG));
    doc_type.addField(Field("string_field", *DataType::STRING));
    doc_type.addField(Field("data_field", *DataType::RAW));
    doc_type.addField(Field("longstring_field", *DataType::STRING));
    doc_type.addField(Field("longdata_field", *DataType::RAW));
    doc_type.addField(Field("int_pair_field", int_pair_type));
}
DocsumFixture::~DocsumFixture() = default;

} // namespace <unnamed>

TEST_FF("require that docsum can be written as slime", DocsumFixture(), Slime()) {
    f1.getDocsum(f2);
    EXPECT_EQUAL(f2.get()["int_field"].asLong(), 4u);
    EXPECT_EQUAL(f2.get()["short_field"].asLong(), 2u);
    EXPECT_EQUAL(f2.get()["byte_field"].asLong(), 1u);
    EXPECT_EQUAL(f2.get()["float_field"].asDouble(), 4.5);
    EXPECT_EQUAL(f2.get()["double_field"].asDouble(), 8.75);
    EXPECT_EQUAL(f2.get()["int64_field"].asLong(), 8u);
    EXPECT_EQUAL(f2.get()["string_field"].asString().make_string(), std::string("string"));
    EXPECT_EQUAL(f2.get()["data_field"].asData().make_string(), std::string("data"));
    EXPECT_EQUAL(f2.get()["longstring_field"].asString().make_string(), std::string("long_string"));
    EXPECT_EQUAL(f2.get()["longdata_field"].asData().make_string(), std::string("long_data"));
    EXPECT_EQUAL(f2.get()["int_pair_field"]["foo"].asLong(), 1u);
    EXPECT_EQUAL(f2.get()["int_pair_field"]["bar"].asLong(), 2u);
}

TEST_MAIN() { TEST_RUN_ALL(); }
<commit_msg>Test failure to get backing document and failure to find summary class when generating summary.<commit_after>// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <vespa/document/base/documentid.h>
#include <vespa/document/datatype/documenttype.h>
#include <vespa/document/fieldvalue/document.h>
#include <vespa/document/fieldvalue/bytefieldvalue.h>
#include <vespa/document/fieldvalue/doublefieldvalue.h>
#include <vespa/document/fieldvalue/floatfieldvalue.h>
#include <vespa/document/fieldvalue/intfieldvalue.h>
#include <vespa/document/fieldvalue/longfieldvalue.h>
#include <vespa/document/fieldvalue/rawfieldvalue.h>
#include <vespa/document/fieldvalue/shortfieldvalue.h>
#include <vespa/document/fieldvalue/stringfieldvalue.h>
#include <vespa/vespalib/testkit/test_kit.h>
#include <vespa/searchlib/common/matching_elements.h>
#include <vespa/searchsummary/docsummary/docsumwriter.h>
#include <vespa/searchsummary/docsummary/docsumstate.h>
#include <vespa/searchsummary/docsummary/keywordextractor.h>
#include <vespa/searchsummary/docsummary/docsum_store_document.h>
#include <vespa/vespalib/data/slime/slime.h>
#include <vespa/vespalib/data/smart_buffer.h>
#include <vespa/vespalib/util/size_literals.h>

using namespace vespalib::slime::convenience;
using namespace search::docsummary;
using vespalib::slime::BinaryFormat;
using search::MatchingElements;
using document::ByteFieldValue;
using document::DataType;
using document::Document;
using document::DocumentId;
using document::DocumentType;
using document::DoubleFieldValue;
using document::Field;
using document::FloatFieldValue;
using document::IntFieldValue;
using document::LongFieldValue;
using document::RawFieldValue;
using document::ShortFieldValue;
using document::StringFieldValue;
using document::StructDataType;
using document::StructFieldValue;

namespace {

struct DocsumFixture : IDocsumStore, GetDocsumsStateCallback {
    std::unique_ptr<DynamicDocsumWriter> writer;
    StructDataType  int_pair_type;
    DocumentType    doc_type;
    GetDocsumsState state;
    bool            fail_get_mapped_docsum;
    bool            empty_get_mapped_docsum;
    DocsumFixture();
    ~DocsumFixture() override;
    void getDocsum(Slime &slime) {
        Slime slimeOut;
        SlimeInserter inserter(slimeOut);
        writer->WriteDocsum(1u, &state, this, inserter);
        vespalib::SmartBuffer buf(4_Ki);
        BinaryFormat::encode(slimeOut, buf);
        EXPECT_GREATER(BinaryFormat::decode(buf.obtain(), slime), 0u);
    }
    uint32_t getNumDocs() const override { return 2; }
    std::unique_ptr<const IDocsumStoreDocument> getMappedDocsum(uint32_t docid) override {
        EXPECT_EQUAL(1u, docid);
        if (fail_get_mapped_docsum) {
            return {};
        }
        if (empty_get_mapped_docsum) {
            return std::make_unique<DocsumStoreDocument>(std::unique_ptr<Document>());
        }
        auto doc = std::make_unique<Document>(doc_type, DocumentId("id:test:test::0"));
        doc->setValue("int_field", IntFieldValue(4));
        doc->setValue("short_field", ShortFieldValue(2));
        doc->setValue("byte_field", ByteFieldValue(1));
        doc->setValue("float_field", FloatFieldValue(4.5));
        doc->setValue("double_field", DoubleFieldValue(8.75));
        doc->setValue("int64_field", LongFieldValue(8));
        doc->setValue("string_field", StringFieldValue("string"));
        doc->setValue("data_field", RawFieldValue("data"));
        doc->setValue("longstring_field", StringFieldValue("long_string"));
        doc->setValue("longdata_field", RawFieldValue("long_data"));
        {
            StructFieldValue int_pair(int_pair_type);
            int_pair.setValue("foo", IntFieldValue(1));
            int_pair.setValue("bar", IntFieldValue(2));
            doc->setValue("int_pair_field", int_pair);
        }
        return std::make_unique<DocsumStoreDocument>(std::move(doc));
    }
    void FillSummaryFeatures(GetDocsumsState&) override { }
    void FillRankFeatures(GetDocsumsState&) override { }
    std::unique_ptr<MatchingElements> fill_matching_elements(const search::MatchingElementsFields &) override { abort(); }
};

DocsumFixture::DocsumFixture()
    : writer(),
      int_pair_type("int_pair"),
      doc_type("test"),
      state(*this),
      fail_get_mapped_docsum(false),
      empty_get_mapped_docsum(false)
{
    auto config = std::make_unique<ResultConfig>();
    ResultClass *cfg = config->AddResultClass("default", 0);
    EXPECT_TRUE(cfg != nullptr);
    EXPECT_TRUE(cfg->AddConfigEntry("int_field", RES_INT));
    EXPECT_TRUE(cfg->AddConfigEntry("short_field", RES_SHORT));
    EXPECT_TRUE(cfg->AddConfigEntry("byte_field", RES_BYTE));
    EXPECT_TRUE(cfg->AddConfigEntry("float_field", RES_FLOAT));
    EXPECT_TRUE(cfg->AddConfigEntry("double_field", RES_DOUBLE));
    EXPECT_TRUE(cfg->AddConfigEntry("int64_field", RES_INT64));
    EXPECT_TRUE(cfg->AddConfigEntry("string_field", RES_STRING));
    EXPECT_TRUE(cfg->AddConfigEntry("data_field", RES_DATA));
    EXPECT_TRUE(cfg->AddConfigEntry("longstring_field", RES_LONG_STRING));
    EXPECT_TRUE(cfg->AddConfigEntry("longdata_field", RES_LONG_DATA));
    EXPECT_TRUE(cfg->AddConfigEntry("int_pair_field", RES_JSONSTRING));
    config->set_default_result_class_id(0);
    config->CreateEnumMaps();
    writer = std::make_unique<DynamicDocsumWriter>(std::move(config), std::unique_ptr<KeywordExtractor>());
    int_pair_type.addField(Field("foo", *DataType::INT));
    int_pair_type.addField(Field("bar", *DataType::INT));
    doc_type.addField(Field("int_field", *DataType::INT));
    doc_type.addField(Field("short_field", *DataType::SHORT));
    doc_type.addField(Field("byte_field", *DataType::BYTE));
    doc_type.addField(Field("float_field", *DataType::FLOAT));
    doc_type.addField(Field("double_field", *DataType::DOUBLE));
    doc_type.addField(Field("int64_field", *DataType::LONG));
    doc_type.addField(Field("string_field", *DataType::STRING));
    doc_type.addField(Field("data_field", *DataType::RAW));
    doc_type.addField(Field("longstring_field", *DataType::STRING));
    doc_type.addField(Field("longdata_field", *DataType::RAW));
    doc_type.addField(Field("int_pair_field", int_pair_type));
}
DocsumFixture::~DocsumFixture() = default;

} // namespace <unnamed>

TEST_FF("require that docsum can be written as slime", DocsumFixture(), Slime()) {
    f1.getDocsum(f2);
    EXPECT_EQUAL(f2.get()["int_field"].asLong(), 4u);
    EXPECT_EQUAL(f2.get()["short_field"].asLong(), 2u);
    EXPECT_EQUAL(f2.get()["byte_field"].asLong(), 1u);
    EXPECT_EQUAL(f2.get()["float_field"].asDouble(), 4.5);
    EXPECT_EQUAL(f2.get()["double_field"].asDouble(), 8.75);
    EXPECT_EQUAL(f2.get()["int64_field"].asLong(), 8u);
    EXPECT_EQUAL(f2.get()["string_field"].asString().make_string(), std::string("string"));
    EXPECT_EQUAL(f2.get()["data_field"].asData().make_string(), std::string("data"));
    EXPECT_EQUAL(f2.get()["longstring_field"].asString().make_string(), std::string("long_string"));
    EXPECT_EQUAL(f2.get()["longdata_field"].asData().make_string(), std::string("long_data"));
    EXPECT_EQUAL(f2.get()["int_pair_field"]["foo"].asLong(), 1u);
    EXPECT_EQUAL(f2.get()["int_pair_field"]["bar"].asLong(), 2u);
}

TEST_FF("require that unknown summary class gives empty slime", DocsumFixture(), Slime())
{
    f1.state._args.setResultClassName("unknown");
    f1.getDocsum(f2);
    EXPECT_TRUE(f2.get().valid());
    EXPECT_EQUAL(vespalib::slime::NIX::ID, f2.get().type().getId());
}

TEST_FF("require that failure to retrieve docsum store document gives empty slime", DocsumFixture(), Slime())
{
    f1.fail_get_mapped_docsum = true;
    f1.getDocsum(f2);
    EXPECT_TRUE(f2.get().valid());
    EXPECT_EQUAL(vespalib::slime::NIX::ID, f2.get().type().getId());
}

TEST_FF("require that empty docsum store document gives empty object", DocsumFixture(), Slime())
{
    f1.empty_get_mapped_docsum = true;
    f1.getDocsum(f2);
    EXPECT_TRUE(f2.get().valid());
    EXPECT_EQUAL(vespalib::slime::OBJECT::ID, f2.get().type().getId());
    EXPECT_EQUAL(0u, f2.get().fields());
}

TEST_MAIN() { TEST_RUN_ALL(); }
<|endoftext|>


<commit_before>/* -*- coding: utf-8; mode: c++; tab-width: 3; indent-tabs-mode: nil -*-

Copyright 2010, 2011, 2012, 2013, 2014
Raffaello D. Di Napoli

This file is part of Abaclade.

Abaclade is free software: you can redistribute it and/or modify it under the terms of the GNU
General Public License as published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

Abaclade is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
Public License for more details.

You should have received a copy of the GNU General Public License along with Abaclade. If not, see
<http://www.gnu.org/licenses/>.
--------------------------------------------------------------------------------------------------*/

// #include <abaclade.hxx> already done in exception-os.cxx.

#include <cstdlib> // std::abort()
#include <signal.h> // sigaction sig*()
#include <ucontext.h> // ucontext_t

////////////////////////////////////////////////////////////////////////////////////////////////////
// abc::exception

namespace {

//! Signals that we can translate into C++ exceptions.
int const g_aiHandledSignals[] = {
// Signal (Default action) Description (standard).
// SIGABRT, // (Core) Abort signal from abort(3) (POSIX.1-1990).
// SIGALRM, // (Term) Timer signal from alarm(2) (POSIX.1-1990).
   SIGBUS,  // (Core) Bus error (bad memory access) (POSIX.1-2001).
// SIGCHLD, // (Ign ) Child stopped or terminated (POSIX.1-1990).
// SIGCONT, // (Cont) Continue if stopped (POSIX.1-1990).
   SIGFPE,  // (Core) Floating point exception (POSIX.1-1990).
// SIGHUP,  // (Term) Hangup on controlling terminal or death of controlling process (POSIX.1-1990).
// SIGILL,  // (Core) Illegal Instruction (POSIX.1-1990).
// SIGINT,  // (Term) Interrupt from keyboard (POSIX.1-1990).
// SIGPIPE, // (Term) Broken pipe: write to pipe with no readers (POSIX.1-1990).
// SIGPROF, // (Term) Profiling timer expired (POSIX.1-2001).
// SIGQUIT, // (Core) Quit from keyboard (POSIX.1-1990).
   SIGSEGV  // (Core) Invalid memory reference (POSIX.1-1990).
// SIGTERM  // (Term) Termination signal (POSIX.1-1990).
// SIGTRAP  // (Core) Trace/breakpoint trap (POSIX.1-2001).
// SIGTSTP  // (Stop) Stop typed at terminal (POSIX.1-1990).
// SIGTTIN  // (Stop) Terminal input for background process (POSIX.1-1990).
// SIGTTOU  // (Stop) Terminal output for background process (POSIX.1-1990).
// SIGUSR1  // (Term) User-defined signal 1 (POSIX.1-1990).
// SIGUSR2  // (Term) User-defined signal 2 (POSIX.1-1990).
};
//! Default handler for each of the signals above.
struct ::sigaction g_asaDefault[ABC_COUNTOF(g_aiHandledSignals)];

//! Possible exception types thrown by throw_after_fault().
ABC_ENUM_AUTO_VALUES(fault_exception_types,
   arithmetic_error,
   division_by_zero_error,
   floating_point_error,
   memory_access_error,
   memory_address_error,
   null_pointer_error,
   overflow_error
);

/*! Throws an exception of the specified type.

@param fxt
   Type of exception to be throw.
@param iArg0
   Exception type-specific argument 0.
@param iArg1
   Exception type-specific argument 1.
*/
void throw_after_fault(
   fault_exception_types::enum_type fxt, std::intptr_t iArg0, std::intptr_t iArg1
) {
//printf("throw_after_fault: %d %p %p\n", fxt, iArg0, iArg1);
   ABC_UNUSED_ARG(iArg1);
   switch (fxt) {
      case fault_exception_types::arithmetic_error:
         ABC_THROW(abc::arithmetic_error, ());
      case fault_exception_types::division_by_zero_error:
         ABC_THROW(abc::division_by_zero_error, ());
      case fault_exception_types::floating_point_error:
         ABC_THROW(abc::floating_point_error, ());
      case fault_exception_types::memory_access_error:
         ABC_THROW(abc::memory_access_error, (reinterpret_cast<void const *>(iArg0)));
      case fault_exception_types::memory_address_error:
         ABC_THROW(abc::memory_address_error, (reinterpret_cast<void const *>(iArg0)));
      case fault_exception_types::null_pointer_error:
         ABC_THROW(abc::null_pointer_error, ());
      case fault_exception_types::overflow_error:
         ABC_THROW(abc::overflow_error, ());
      default:
         // Unexpected exception type. Should never happen.
         std::abort();
   }
}

/*! Translates POSIX signals into C++ exceptions, whenever possible. This works by injecting the
stack frame of a call to throw_after_fault(), and then returning, ending processing of the signal.
Execution will resume from throw_after_fault(), which creates the appearance of a C++ exception
being thrown at the location of the offending instruction, without calling any of the (many)
functions that are forbidden in a signal handler.

@param iSignal
   Signal number for which the function is being called.
@param psi
   Additional information on the signal.
@param pctx
   Thread context. This is used to manipulate the stack of the thread to inject a call frame.
*/
void fault_signal_handler(int iSignal, ::siginfo_t * psi, void * pctx) {
   /* Don’t let external programs mess with us: if the source is not the kernel, ignore the error.
   POSIX.1-2008 states that:
      “Historically, an si_code value of less than or equal to zero indicated that the signal was
      generated by a process via the kill() function, and values of si_code that provided additional
      information for implementation-generated signals, such as SIGFPE or SIGSEGV, were all
      positive. […] if si_code is less than or equal to zero, the signal was generated by a process.
      However, since POSIX.1b did not specify that SI_USER (or SI_QUEUE) had a value less than or
      equal to zero, it is not true that when the signal is generated by a process, the value of
      si_code will always be less than or equal to zero. XSI applications should check whether
      si_code is SI_USER or SI_QUEUE in addition to checking whether it is less than or equal to
      zero.”
   So we do exactly that – except we skip checking for SI_USER and SI_QUEUE at this point because
   they don’t apply to many signals this handler takes care of. */
   if (psi->si_code <= 0) {
      return;
   }

   fault_exception_types::enum_type fxt;
   std::intptr_t iArg0 = 0, iArg1 = 0;
   switch (iSignal) {
      case SIGBUS:
         /* There aren’t many codes here that are safe to handle; most of them indicate that there
         is some major memory corruption going on, and in that case we really don’t want to keep on
         going – even the code to throw an exception could be compromised. */
         switch (psi->si_code) {
            case BUS_ADRALN: // Invalid address alignment.
               fxt = fault_exception_types::memory_access_error;
               iArg0 = reinterpret_cast<std::intptr_t>(psi->si_addr);
               break;
            default:
               std::abort();
         }
         break;

      case SIGFPE:
         switch (psi->si_code) {
            case FPE_INTDIV: // Integer divide by zero.
               fxt = fault_exception_types::division_by_zero_error;
               break;
            case FPE_INTOVF: // Integer overflow.
               fxt = fault_exception_types::overflow_error;
               break;
            case FPE_FLTDIV: // Floating-point divide by zero.
            case FPE_FLTOVF: // Floating-point overflow.
            case FPE_FLTUND: // Floating-point underflow.
            case FPE_FLTRES: // Floating-point inexact result.
            case FPE_FLTINV: // Floating-point invalid operation.
            case FPE_FLTSUB: // Subscript out of range.
               fxt = fault_exception_types::floating_point_error;
               break;
            default:
               /* At the time of writing, the above case labels don’t leave out any values, but
               that’s not necessarily going to be true in 5 years, so… */
               fxt = fault_exception_types::arithmetic_error;
               break;
         }
         break;

      case SIGSEGV:
         if (psi->si_addr == nullptr) {
            fxt = fault_exception_types::null_pointer_error;
         } else {
            fxt = fault_exception_types::memory_address_error;
            iArg0 = reinterpret_cast<std::intptr_t>(psi->si_addr);
         }
         break;

      default:
         /* Handle all unrecognized cases here. Since here we only handle signals for which the
         default actions is a core dump, calling abort (which sends SIGABRT, also causing a core
         dump) is the same as invoking the default action. */
         std::abort();
   }

   /* Change the address at which the thread will resume execution: manipulate the thread context
   to emulate a function call to throw_after_fault(). */

   ::ucontext_t * puctx = static_cast< ::ucontext_t *>(pctx);
   #if ABC_HOST_ARCH_I386
      #if ABC_HOST_API_LINUX
         typedef std::int32_t reg_t;
         reg_t & eip = puctx->uc_mcontext.gregs[REG_EIP];
         reg_t & esp = puctx->uc_mcontext.gregs[REG_ESP];
      #elif ABC_HOST_API_FREEBSD
         typedef std::int32_t reg_t;
         reg_t & eip = puctx->uc_mcontext.mc_eip;
         reg_t & esp = puctx->uc_mcontext.mc_esp;
      #else
         #error "TODO: HOST_API"
      #endif
      /* Push the arguments to throw_after_fault() onto the stack, push the address of the current
      (failing) instruction, then set eip to the start of throw_after_fault(). These steps emulate a
      3-argument subroutine call. */
      reinterpret_cast<reg_t *>(esp -= 4) = static_cast<reg_t>(iArg1);
      reinterpret_cast<reg_t *>(esp -= 4) = static_cast<reg_t>(iArg0);
      reinterpret_cast<reg_t *>(esp -= 4) = static_cast<reg_t>(fxt);
      reinterpret_cast<reg_t *>(esp -= 4) = eip;
      eip = reinterpret_cast<reg_t>(&throw_after_fault);
   #elif ABC_HOST_ARCH_X86_64
      #if ABC_HOST_API_LINUX
         typedef std::int64_t reg_t;
         reg_t & rip = puctx->uc_mcontext.gregs[REG_RIP];
         reg_t & rsp = puctx->uc_mcontext.gregs[REG_RSP];
         reg_t & rdi = puctx->uc_mcontext.gregs[REG_RDI];
         reg_t & rsi = puctx->uc_mcontext.gregs[REG_RSI];
         reg_t & rdx = puctx->uc_mcontext.gregs[REG_RDX];
      #elif ABC_HOST_API_FREEBSD
         typedef std::int64_t reg_t;
         reg_t & rip = puctx->uc_mcontext.mc_rip;
         reg_t & rsp = puctx->uc_mcontext.mc_rsp;
         reg_t & rdi = puctx->uc_mcontext.mc_rdi;
         reg_t & rsi = puctx->uc_mcontext.mc_rsi;
         reg_t & rdx = puctx->uc_mcontext.mc_rdx;
      #else
         #error "TODO: HOST_API"
      #endif
      /* Load the arguments to throw_after_fault() in rdi/rsi/rdx, push the address of the current
      (failing) instruction, then set rip to the start of throw_after_fault(). These steps emulate a
      3-argument subroutine call. */
      rdi = static_cast<reg_t>(fxt);
      rsi = static_cast<reg_t>(iArg0);
      rdx = static_cast<reg_t>(iArg1);
      // TODO: validate that stack alignment to 16 bytes is done by the callee with push rbp.
      *reinterpret_cast<reg_t *>(rsp -= 8) = rip;
      rip = reinterpret_cast<reg_t>(&throw_after_fault);
   #else
      #error "TODO: HOST_ARCH"
   #endif
}

} //namespace

namespace abc {

exception::fault_converter::fault_converter() {
   // Setup handlers for the signals in g_aiHandledSignals.
   struct ::sigaction saNew;
   saNew.sa_sigaction = &fault_signal_handler;
   sigemptyset(&saNew.sa_mask);
   /* SA_SIGINFO (POSIX.1-2001) provides the handler with more information about the signal, which
   we use to generate more precise exceptions. */
   saNew.sa_flags = SA_SIGINFO;
   for (std::size_t i = ABC_COUNTOF(g_aiHandledSignals); i-- > 0; ) {
      ::sigaction(g_aiHandledSignals[i], &saNew, &g_asaDefault[i]);
   }
}

exception::fault_converter::~fault_converter() {
   // Restore the saved signal handlers.
   for (std::size_t i = ABC_COUNTOF(g_aiHandledSignals); i-- > 0; ) {
      ::sigaction(g_aiHandledSignals[i], &g_asaDefault[i], nullptr);
   }
}

} //namespace abc

////////////////////////////////////////////////////////////////////////////////////////////////////
<commit_msg>Simplify register manipulation<commit_after>/* -*- coding: utf-8; mode: c++; tab-width: 3; indent-tabs-mode: nil -*-

Copyright 2010, 2011, 2012, 2013, 2014
Raffaello D. Di Napoli

This file is part of Abaclade.

Abaclade is free software: you can redistribute it and/or modify it under the terms of the GNU
General Public License as published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

Abaclade is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
Public License for more details.

You should have received a copy of the GNU General Public License along with Abaclade. If not, see
<http://www.gnu.org/licenses/>.
--------------------------------------------------------------------------------------------------*/

// #include <abaclade.hxx> already done in exception-os.cxx.

#include <cstdlib> // std::abort()
#include <signal.h> // sigaction sig*()
#include <ucontext.h> // ucontext_t

////////////////////////////////////////////////////////////////////////////////////////////////////
// abc::exception

namespace {

//! Signals that we can translate into C++ exceptions.
int const g_aiHandledSignals[] = {
// Signal (Default action) Description (standard).
// SIGABRT, // (Core) Abort signal from abort(3) (POSIX.1-1990).
// SIGALRM, // (Term) Timer signal from alarm(2) (POSIX.1-1990).
   SIGBUS,  // (Core) Bus error (bad memory access) (POSIX.1-2001).
// SIGCHLD, // (Ign ) Child stopped or terminated (POSIX.1-1990).
// SIGCONT, // (Cont) Continue if stopped (POSIX.1-1990).
   SIGFPE,  // (Core) Floating point exception (POSIX.1-1990).
// SIGHUP,  // (Term) Hangup on controlling terminal or death of controlling process (POSIX.1-1990).
// SIGILL,  // (Core) Illegal Instruction (POSIX.1-1990).
// SIGINT,  // (Term) Interrupt from keyboard (POSIX.1-1990).
// SIGPIPE, // (Term) Broken pipe: write to pipe with no readers (POSIX.1-1990).
// SIGPROF, // (Term) Profiling timer expired (POSIX.1-2001).
// SIGQUIT, // (Core) Quit from keyboard (POSIX.1-1990).
   SIGSEGV  // (Core) Invalid memory reference (POSIX.1-1990).
// SIGTERM  // (Term) Termination signal (POSIX.1-1990).
// SIGTRAP  // (Core) Trace/breakpoint trap (POSIX.1-2001).
// SIGTSTP  // (Stop) Stop typed at terminal (POSIX.1-1990).
// SIGTTIN  // (Stop) Terminal input for background process (POSIX.1-1990).
// SIGTTOU  // (Stop) Terminal output for background process (POSIX.1-1990).
// SIGUSR1  // (Term) User-defined signal 1 (POSIX.1-1990).
// SIGUSR2  // (Term) User-defined signal 2 (POSIX.1-1990).
};
//! Default handler for each of the signals above.
struct ::sigaction g_asaDefault[ABC_COUNTOF(g_aiHandledSignals)];

//! Possible exception types thrown by throw_after_fault().
ABC_ENUM_AUTO_VALUES(fault_exception_types,
   arithmetic_error,
   division_by_zero_error,
   floating_point_error,
   memory_access_error,
   memory_address_error,
   null_pointer_error,
   overflow_error
);

/*! Throws an exception of the specified type.

@param fxt
   Type of exception to be throw.
@param iArg0
   Exception type-specific argument 0.
@param iArg1
   Exception type-specific argument 1.
*/
void throw_after_fault(
   fault_exception_types::enum_type fxt, std::intptr_t iArg0, std::intptr_t iArg1
) {
//printf("throw_after_fault: %d %p %p\n", fxt, iArg0, iArg1);
   ABC_UNUSED_ARG(iArg1);
   switch (fxt) {
      case fault_exception_types::arithmetic_error:
         ABC_THROW(abc::arithmetic_error, ());
      case fault_exception_types::division_by_zero_error:
         ABC_THROW(abc::division_by_zero_error, ());
      case fault_exception_types::floating_point_error:
         ABC_THROW(abc::floating_point_error, ());
      case fault_exception_types::memory_access_error:
         ABC_THROW(abc::memory_access_error, (reinterpret_cast<void const *>(iArg0)));
      case fault_exception_types::memory_address_error:
         ABC_THROW(abc::memory_address_error, (reinterpret_cast<void const *>(iArg0)));
      case fault_exception_types::null_pointer_error:
         ABC_THROW(abc::null_pointer_error, ());
      case fault_exception_types::overflow_error:
         ABC_THROW(abc::overflow_error, ());
      default:
         // Unexpected exception type. Should never happen.
         std::abort();
   }
}

/*! Translates POSIX signals into C++ exceptions, whenever possible. This works by injecting the
stack frame of a call to throw_after_fault(), and then returning, ending processing of the signal.
Execution will resume from throw_after_fault(), which creates the appearance of a C++ exception
being thrown at the location of the offending instruction, without calling any of the (many)
functions that are forbidden in a signal handler.

@param iSignal
   Signal number for which the function is being called.
@param psi
   Additional information on the signal.
@param pctx
   Thread context. This is used to manipulate the stack of the thread to inject a call frame.
*/
void fault_signal_handler(int iSignal, ::siginfo_t * psi, void * pctx) {
   /* Don’t let external programs mess with us: if the source is not the kernel, ignore the error.
   POSIX.1-2008 states that:
      “Historically, an si_code value of less than or equal to zero indicated that the signal was
      generated by a process via the kill() function, and values of si_code that provided additional
      information for implementation-generated signals, such as SIGFPE or SIGSEGV, were all
      positive. […] if si_code is less than or equal to zero, the signal was generated by a process.
      However, since POSIX.1b did not specify that SI_USER (or SI_QUEUE) had a value less than or
      equal to zero, it is not true that when the signal is generated by a process, the value of
      si_code will always be less than or equal to zero. XSI applications should check whether
      si_code is SI_USER or SI_QUEUE in addition to checking whether it is less than or equal to
      zero.”
   So we do exactly that – except we skip checking for SI_USER and SI_QUEUE at this point because
   they don’t apply to many signals this handler takes care of. */
   if (psi->si_code <= 0) {
      return;
   }

   fault_exception_types::enum_type fxt;
   std::intptr_t iArg0 = 0, iArg1 = 0;
   switch (iSignal) {
      case SIGBUS:
         /* There aren’t many codes here that are safe to handle; most of them indicate that there
         is some major memory corruption going on, and in that case we really don’t want to keep on
         going – even the code to throw an exception could be compromised. */
         switch (psi->si_code) {
            case BUS_ADRALN: // Invalid address alignment.
               fxt = fault_exception_types::memory_access_error;
               iArg0 = reinterpret_cast<std::intptr_t>(psi->si_addr);
               break;
            default:
               std::abort();
         }
         break;

      case SIGFPE:
         switch (psi->si_code) {
            case FPE_INTDIV: // Integer divide by zero.
               fxt = fault_exception_types::division_by_zero_error;
               break;
            case FPE_INTOVF: // Integer overflow.
               fxt = fault_exception_types::overflow_error;
               break;
            case FPE_FLTDIV: // Floating-point divide by zero.
            case FPE_FLTOVF: // Floating-point overflow.
            case FPE_FLTUND: // Floating-point underflow.
            case FPE_FLTRES: // Floating-point inexact result.
            case FPE_FLTINV: // Floating-point invalid operation.
            case FPE_FLTSUB: // Subscript out of range.
               fxt = fault_exception_types::floating_point_error;
               break;
            default:
               /* At the time of writing, the above case labels don’t leave out any values, but
               that’s not necessarily going to be true in 5 years, so… */
               fxt = fault_exception_types::arithmetic_error;
               break;
         }
         break;

      case SIGSEGV:
         if (psi->si_addr == nullptr) {
            fxt = fault_exception_types::null_pointer_error;
         } else {
            fxt = fault_exception_types::memory_address_error;
            iArg0 = reinterpret_cast<std::intptr_t>(psi->si_addr);
         }
         break;

      default:
         /* Handle all unrecognized cases here. Since here we only handle signals for which the
         default actions is a core dump, calling abort (which sends SIGABRT, also causing a core
         dump) is the same as invoking the default action. */
         std::abort();
   }

   /* Change the address at which the thread will resume execution: manipulate the thread context to
   emulate a function call to throw_after_fault(). */

   ::ucontext_t * puctx = static_cast< ::ucontext_t *>(pctx);
   #if ABC_HOST_ARCH_I386
      #if ABC_HOST_API_LINUX
         typedef std::int32_t reg_t;
         reg_t & eip = puctx->uc_mcontext.gregs[REG_EIP];
         reg_t *& esp = reinterpret_cast<reg_t *&>(puctx->uc_mcontext.gregs[REG_ESP]);
      #elif ABC_HOST_API_FREEBSD
         typedef std::int32_t reg_t;
         reg_t & eip = puctx->uc_mcontext.mc_eip;
         reg_t *& esp = reinterpret_cast<reg_t *&>(puctx->uc_mcontext.mc_esp);
      #else
         #error "TODO: HOST_API"
      #endif
      /* Push the arguments to throw_after_fault() onto the stack, push the address of the current
      (failing) instruction, then set eip to the start of throw_after_fault(). These steps emulate a
      3-argument subroutine call. */
      *--esp = static_cast<reg_t>(iArg1);
      *--esp = static_cast<reg_t>(iArg0);
      *--esp = static_cast<reg_t>(fxt);
      *--esp = eip;
      eip = reinterpret_cast<reg_t>(&throw_after_fault);
   #elif ABC_HOST_ARCH_X86_64
      #if ABC_HOST_API_LINUX
         typedef std::int64_t reg_t;
         reg_t & rip = puctx->uc_mcontext.gregs[REG_RIP];
         reg_t *& rsp = reinterpret_cast<reg_t *&>(puctx->uc_mcontext.gregs[REG_RSP]);
         reg_t & rdi = puctx->uc_mcontext.gregs[REG_RDI];
         reg_t & rsi = puctx->uc_mcontext.gregs[REG_RSI];
         reg_t & rdx = puctx->uc_mcontext.gregs[REG_RDX];
      #elif ABC_HOST_API_FREEBSD
         typedef std::int64_t reg_t;
         reg_t & rip = puctx->uc_mcontext.mc_rip;
         reg_t *& rsp = reinterpret_cast<reg_t *&>(puctx->uc_mcontext.mc_rsp);
         reg_t & rdi = puctx->uc_mcontext.mc_rdi;
         reg_t & rsi = puctx->uc_mcontext.mc_rsi;
         reg_t & rdx = puctx->uc_mcontext.mc_rdx;
      #else
         #error "TODO: HOST_API"
      #endif
      /* Load the arguments to throw_after_fault() in rdi/rsi/rdx, push the address of the current
      (failing) instruction, then set rip to the start of throw_after_fault(). These steps emulate a
      3-argument subroutine call. */
      rdi = static_cast<reg_t>(fxt);
      rsi = static_cast<reg_t>(iArg0);
      rdx = static_cast<reg_t>(iArg1);
      // TODO: validate that stack alignment to 16 bytes is done by the callee with push rbp.
      *--rsp = rip;
      rip = reinterpret_cast<reg_t>(&throw_after_fault);
   #else
      #error "TODO: HOST_ARCH"
   #endif
}

} //namespace

namespace abc {

exception::fault_converter::fault_converter() {
   // Setup handlers for the signals in g_aiHandledSignals.
   struct ::sigaction saNew;
   saNew.sa_sigaction = &fault_signal_handler;
   sigemptyset(&saNew.sa_mask);
   /* SA_SIGINFO (POSIX.1-2001) provides the handler with more information about the signal, which
   we use to generate more precise exceptions. */
   saNew.sa_flags = SA_SIGINFO;
   for (std::size_t i = ABC_COUNTOF(g_aiHandledSignals); i-- > 0; ) {
      ::sigaction(g_aiHandledSignals[i], &saNew, &g_asaDefault[i]);
   }
}

exception::fault_converter::~fault_converter() {
   // Restore the saved signal handlers.
   for (std::size_t i = ABC_COUNTOF(g_aiHandledSignals); i-- > 0; ) {
      ::sigaction(g_aiHandledSignals[i], &g_asaDefault[i], nullptr);
   }
}

} //namespace abc

////////////////////////////////////////////////////////////////////////////////////////////////////
<|endoftext|>


<commit_before>// Copyright 2016-2018 Chris Conway (Koderz). All Rights Reserved.

#include "RuntimeMeshActor.h"
#include "RuntimeMeshComponent.h"
#include "RuntimeMeshComponentPlugin.h"

ARuntimeMeshActor::ARuntimeMeshActor(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
	, bRunGenerateMeshesOnConstruction(true)
	, bRunGenerateMeshesOnBeginPlay(false)
{
	bCanBeDamaged = false;

	RuntimeMeshComponent = CreateDefaultSubobject<URuntimeMeshComponent>(TEXT("RuntimeMeshComponent0"));
	RuntimeMeshComponent->SetCollisionProfileName(UCollisionProfile::BlockAll_ProfileName);
	RuntimeMeshComponent->Mobility = EComponentMobility::Static;

#if ENGINE_MAJOR_VERSION >= 4 && ENGINE_MINOR_VERSION >= 20
	RuntimeMeshComponent->SetGenerateOverlapEvents(false);
#else
	RuntimeMeshComponent->bGenerateOverlapEvents = false;
#endif
	RootComponent = RuntimeMeshComponent;
}

ERuntimeMeshMobility ARuntimeMeshActor::GetRuntimeMeshMobility()
{
	if (RuntimeMeshComponent)
	{
		return RuntimeMeshComponent->GetRuntimeMeshMobility();
	}
	return ERuntimeMeshMobility::Static;
}

void ARuntimeMeshActor::SetRuntimeMeshMobility(ERuntimeMeshMobility NewMobility)
{
	if (RuntimeMeshComponent)
	{
		RuntimeMeshComponent->SetRuntimeMeshMobility(NewMobility);
	}
}

void ARuntimeMeshActor::SetMobility(EComponentMobility::Type InMobility)
{
	if (RuntimeMeshComponent)
	{
		RuntimeMeshComponent->SetMobility(InMobility);
	}
}

EComponentMobility::Type ARuntimeMeshActor::GetMobility()
{
	if (RuntimeMeshComponent)
	{
		return RuntimeMeshComponent->Mobility;
	}
	return EComponentMobility::Static;
}

void ARuntimeMeshActor::OnConstruction(const FTransform& Transform)
{
	Super::OnConstruction();

	if (bRunGenerateMeshesOnConstruction)
	{
		GenerateMeshes();
	}
}

void ARuntimeMeshActor::BeginPlay()
{
	Super::BeginPlay();

	bool bIsGameWorld = GetWorld() && GetWorld()->IsGameWorld() && !GetWorld()->IsPreviewWorld() && !GetWorld()->IsEditorWorld();

	bool bHadSerializedMeshData = false;
	if (RuntimeMeshComponent)
	{
		URuntimeMesh* Mesh = RuntimeMeshComponent->GetRuntimeMesh();
		if (Mesh)
		{
			bHadSerializedMeshData = Mesh->HasSerializedMeshData();
		}
	}

	if ((bIsGameWorld && !bHadSerializedMeshData) || bRunGenerateMeshesOnBeginPlay)
	{
		GenerateMeshes();
	}
}

void ARuntimeMeshActor::GenerateMeshes_Implementation()
{

}

<commit_msg>Update RuntimeMeshActor.cpp<commit_after>// Copyright 2016-2018 Chris Conway (Koderz). All Rights Reserved.

#include "RuntimeMeshActor.h"
#include "RuntimeMeshComponent.h"
#include "RuntimeMeshComponentPlugin.h"

ARuntimeMeshActor::ARuntimeMeshActor(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
	, bRunGenerateMeshesOnConstruction(true)
	, bRunGenerateMeshesOnBeginPlay(false)
{
	bCanBeDamaged = false;

	RuntimeMeshComponent = CreateDefaultSubobject<URuntimeMeshComponent>(TEXT("RuntimeMeshComponent0"));
	RuntimeMeshComponent->SetCollisionProfileName(UCollisionProfile::BlockAll_ProfileName);
	RuntimeMeshComponent->Mobility = EComponentMobility::Static;

#if ENGINE_MAJOR_VERSION >= 4 && ENGINE_MINOR_VERSION >= 20
	RuntimeMeshComponent->SetGenerateOverlapEvents(false);
#else
	RuntimeMeshComponent->bGenerateOverlapEvents = false;
#endif
	RootComponent = RuntimeMeshComponent;
}

ERuntimeMeshMobility ARuntimeMeshActor::GetRuntimeMeshMobility()
{
	if (RuntimeMeshComponent)
	{
		return RuntimeMeshComponent->GetRuntimeMeshMobility();
	}
	return ERuntimeMeshMobility::Static;
}

void ARuntimeMeshActor::SetRuntimeMeshMobility(ERuntimeMeshMobility NewMobility)
{
	if (RuntimeMeshComponent)
	{
		RuntimeMeshComponent->SetRuntimeMeshMobility(NewMobility);
	}
}

void ARuntimeMeshActor::SetMobility(EComponentMobility::Type InMobility)
{
	if (RuntimeMeshComponent)
	{
		RuntimeMeshComponent->SetMobility(InMobility);
	}
}

EComponentMobility::Type ARuntimeMeshActor::GetMobility()
{
	if (RuntimeMeshComponent)
	{
		return RuntimeMeshComponent->Mobility;
	}
	return EComponentMobility::Static;
}

void ARuntimeMeshActor::OnConstruction(const FTransform& Transform)
{
	Super::OnConstruction(Transform);

	if (bRunGenerateMeshesOnConstruction)
	{
		GenerateMeshes();
	}
}

void ARuntimeMeshActor::BeginPlay()
{
	Super::BeginPlay();

	bool bIsGameWorld = GetWorld() && GetWorld()->IsGameWorld() && !GetWorld()->IsPreviewWorld() && !GetWorld()->IsEditorWorld();

	bool bHadSerializedMeshData = false;
	if (RuntimeMeshComponent)
	{
		URuntimeMesh* Mesh = RuntimeMeshComponent->GetRuntimeMesh();
		if (Mesh)
		{
			bHadSerializedMeshData = Mesh->HasSerializedMeshData();
		}
	}

	if ((bIsGameWorld && !bHadSerializedMeshData) || bRunGenerateMeshesOnBeginPlay)
	{
		GenerateMeshes();
	}
}

void ARuntimeMeshActor::GenerateMeshes_Implementation()
{

}

<|endoftext|>