commit 1d11a26ceb1458791883218a2889d0e9e802900e
parent d53a6a17ee1c271f6c492e4bb7a49f91732aff07
Author: Dominik Schmidt <das1993@hotmail.com>
Date: Thu, 27 Aug 2015 12:33:25 +0200
Add DDoc & Unittests
Diffstat:
puzzle.d | | | 189 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1 file changed, 189 insertions(+), 0 deletions(-)
diff --git a/puzzle.d b/puzzle.d
@@ -1,5 +1,13 @@
module puzzle;
+version(unittest){
+ static immutable string testimage="iVBORw0KGgoAAAANSUhEUgAAAE4AAAASAQMAAADPKHrSAAAABlBMVEX///8AAABVwtN+AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wgbCSsvSqRkugAAAGxJREFUCNdjYEAAGyYDMH2AgUGCSQDMdAAyGTCZDQwSbLL9yRL//jkrANVKHDPk6OlxEwBpcxHkkJCAMwUE3H9AmQYGbkATlEDMhAQwE6htxqFDTgIM9s+3tz549+/f5wcg01kQLkJi8jHgAQA3KhrFG5DorwAAAABJRU5ErkJggg==";
+ ubyte[] testimagedat(){
+ import std.base64;
+ return cast(ubyte[])Base64.decode(testimage);
+ }
+}
+
extern(C){
struct PuzzleContext {
uint puzzle_max_width;
@@ -99,8 +107,22 @@ class PuzzleException : Exception{
}
}
+/**
+ * PuzzleContext wrapper
+ *
+ * Has to be inititialized with `Puzzle.initialize();`
+ * and frees the context on destruction.
+ *
+ * Be careful, when you stack-allocate it, the object gets
+ * rendered unusable once the scope has been left.
+ *
+ */
struct Puzzle{
PuzzleContext ctx;
+ /**
+ * Initializes the Puzzle-context.
+ *
+ */
void initialize()
out{
assert(ctx.valid());
@@ -108,23 +130,57 @@ struct Puzzle{
body{
puzzle_init_context(&ctx);
}
+
+ ///Frees the PuzzleContext (If there is one)
~this(){
if(ctx.valid){
puzzle_free_context(&ctx);
}
}
+ /**
+ * Returns a newly allocated DVec-Structure
+ *
+ * $(DDOC_SEE_ALSO struct DVec)
+ *
+ */
DVec dvec(){
return DVec(&ctx);
}
+
+ /**
+ * Returns a newly allocated CVec-Structure
+ *
+ * $(DDOC_SEE_ALSO struct CVec)
+ *
+ */
CVec cvec(){
return CVec(&ctx);
}
}
+///
+unittest{
+ Puzzle p;
+ assert(!p.ctx.valid);
+ p.initialize();
+ assert(p.ctx.valid);
+
+ DVec d=p.dvec();
+ assert(d.ctx.valid);
+
+ DVec c=p.dvec();
+ assert(c.ctx.valid);
+}
+/**
+ * Wraps the PuzzleDVec-structure
+ *
+ */
struct DVec{
PuzzleDvec vec;
PuzzleContext *ctx;
@disable this();
+
+ ///Initializes the DVec
this(PuzzleContext *ctx)
in{
assert(ctx.valid(), "Context invalid");
@@ -133,30 +189,61 @@ struct DVec{
this.ctx=ctx;
puzzle_init_dvec(ctx,&vec);
}
+
+ ///Frees the DVec (If it has been allocated by the library);
~this(){
if(ctx!=null){
puzzle_free_dvec(ctx,&vec);
}
}
+
+ /**
+ * Generates the vector from a file.
+ * Params:
+ * f = File to read the data from.
+ * Throws:
+ * PuzzleException if the puzzle-function fails to load the image
+ */
void load(string f){
if(puzzle_fill_dvec_from_file(ctx, &vec, toStringz(f))!=0){
throw new PuzzleException("Couldn't read from file");
}
}
+
+ /**
+ * Generates the vector from memory
+ * Params:
+ * mem = The memory of an Image
+ * Throws:
+ * PuzzleException if the memory couldn't be read.
+ */
void load(void mem[]){
if(puzzle_fill_dvec_from_mem(ctx, &vec, cast(void*)mem, mem.length)!=0){
throw new PuzzleException("Couldn't read from memory");
}
}
+ /**
+ * Get the CVec derived from this DVector.
+ * Returns:
+ * The corresponding(and comparable) CVector to this DVector
+ */
CVec cvec(){
CVec cvec=CVec(ctx);
puzzle_fill_cvec_from_dvec(ctx,&cvec.vec,&vec);
return cvec;
}
+
+ /**
+ * Get the length of the vector
+ * Returns:
+ * The length of the vector
+ */
@safe @nogc @property size_t length(){
return vec.sizeof_vec;
}
}
+
+///
struct CVec{
PuzzleCvec vec;
PuzzleContext *ctx;
@@ -165,14 +252,37 @@ struct CVec{
this(c.ctx);
puzzle_uncompress_cvec(ctx, &c.vec, &vec);
}
+ /**
+ * Generates a Vector by hand.
+ *
+ * This can be used to save a Vector and reload it again.
+ *
+ * Params:
+ * a = The array of data to fill the vector with.
+ */
@nogc pure nothrow this(byte[] a){
this(cast(char*)a.ptr, a.length);
}
+ ///ditto
@nogc pure nothrow this(char *a, size_t length){
vec.vec=a;
vec.sizeof_vec=length;
}
+ ///
+ unittest{
+ Puzzle p;
+ p.initialize();
+ CVec c=p.cvec();
+
+ c.load(testimagedat());
+ assert(c.length>0);
+
+ CVec c2=CVec(c.vec.vec,c.length);
+ assert(std.math.approxEqual(c.compare(c2),0));
+ }
+
+ ///Initializes a Vector
this(PuzzleContext *ctx)
in{
assert(ctx.valid(), "Context invalid");
@@ -181,49 +291,110 @@ struct CVec{
this.ctx=ctx;
puzzle_init_cvec(ctx,&vec);
}
+ ///Frees the vector(If it was allocated by the library)
~this(){
if(ctx!=null){
puzzle_free_cvec(ctx,&vec);
}
}
+
+ /**
+ * Generates the vector from a file.
+ * Params:
+ * f = File to read the data from.
+ * Throws:
+ * PuzzleException if the puzzle-function fails to load the image
+ */
void load(string f){
if(puzzle_fill_cvec_from_file(ctx, &vec, toStringz(f))!=0){
throw new PuzzleException("Couldn't read from file");
}
}
+
+ /**
+ * Generates the vector from memory
+ * Params:
+ * mem = The memory of an Image
+ * Throws:
+ * PuzzleException if the memory couldn't be read.
+ */
void load(void mem[]){
if(puzzle_fill_cvec_from_mem(ctx, &vec, cast(void*)mem, mem.length)!=0){
throw new PuzzleException("Couldn't read from memory");
}
}
+
+ /**
+ * Compare two CVectors.
+ * Params:
+ * b = The second vector to compare to
+ * hasText = Wether one of the images had any text in it.
+ * Returns:
+ * The distance of both vectors.
+ */
double compare(in ref CVec b, bool hasText=false){
return puzzle_vector_normalized_distance(ctx,&vec,&b.vec,(hasText) ? 1 : 0);
}
+
+
+ /**
+ * Compresses a CVector
+ * Returns:
+ * A compressed CVector
+ */
CVec_Compressed compress(){
return CVec_Compressed(this);
}
+
+ /**
+ * Get the length of the vector
+ * Returns:
+ * The length of the vector
+ */
@nogc @safe @property size_t length(){
return vec.sizeof_vec;
}
}
+
+///
+unittest{
+ Puzzle p;
+ p.initialize();
+ CVec c=p.cvec();
+ c.load(testimagedat());
+
+ assert(std.math.approxEqual(c.compare(c),0));
+}
+
+///A compressed CVector
struct CVec_Compressed{
PuzzleCompressedCvec vec;
PuzzleContext *ctx;
@disable this();
+
+ ///Generates a compressed CVector corresponding to c
this(ref CVec c){
this(c.ctx);
puzzle_compress_cvec(ctx, &vec, &c.vec);
}
+ /**
+ * Generates a Vector by hand.
+ *
+ * Params:
+ * a = The array of data to fill the vector with.
+ */
this(ubyte[] a){
this(a.ptr,a.length);
}
+ ///ditto
this(ubyte* a, size_t length){
vec.vec=a;
vec.sizeof_compressed_vec=length;
}
+ ///Allocates a new Compressed CVector in the library
this(PuzzleContext *ctx)
in{
assert(ctx.valid(), "Context invalid");
@@ -232,15 +403,33 @@ struct CVec_Compressed{
this.ctx=ctx;
puzzle_init_compressed_cvec(ctx,&vec);
}
+
+ ///Frees the Vector, if it was allocated by the library
~this(){
if(ctx!=null){
puzzle_free_compressed_cvec(ctx,&vec);
}
}
+
+ ///Returns the corresponding uncompressed CVector
CVec uncompress(){
return CVec(this);
}
+
+ ///
@safe @nogc @property size_t length(){
return vec.sizeof_compressed_vec;
}
}
+
+///
+unittest{
+ Puzzle p;
+ p.initialize();
+ CVec c=p.cvec();
+ c.load(testimagedat());
+ CVec_Compressed cc=c.compress();
+ CVec c2=cc.uncompress();
+
+ assert(std.math.approxEqual(c.compare(c2),0));
+}