diff --git a/cc/core/Vec.cc b/cc/core/Vec.cc index cd55214e5..d14fc7d11 100644 --- a/cc/core/Vec.cc +++ b/cc/core/Vec.cc @@ -3,6 +3,7 @@ Nan::Persistent Vec2::constructor; Nan::Persistent Vec3::constructor; Nan::Persistent Vec4::constructor; +Nan::Persistent Vec6::constructor; NAN_MODULE_INIT(Vec::Init) { v8::Local vec2Ctor = Nan::New(Vec2::New); @@ -39,6 +40,20 @@ NAN_MODULE_INIT(Vec::Init) { Nan::SetPrototypeMethod(vec4Ctor, "norm", Vec4::Norm); Vec4::Init(vec4Ctor); + v8::Local vec6Ctor = Nan::New(Vec6::New); + Vec6::constructor.Reset(vec6Ctor); + vec6Ctor->InstanceTemplate()->SetInternalFieldCount(1); + vec6Ctor->SetClassName(Nan::New("Vec6").ToLocalChecked()); + Nan::SetAccessor(vec6Ctor->InstanceTemplate(), Nan::New("u").ToLocalChecked(), Vec6::u_getter); + Nan::SetAccessor(vec6Ctor->InstanceTemplate(), Nan::New("v").ToLocalChecked(), Vec6::v_getter); + Nan::SetAccessor(vec6Ctor->InstanceTemplate(), Nan::New("w").ToLocalChecked(), Vec6::w_getter); + Nan::SetAccessor(vec6Ctor->InstanceTemplate(), Nan::New("x").ToLocalChecked(), Vec6::x_getter); + Nan::SetAccessor(vec6Ctor->InstanceTemplate(), Nan::New("y").ToLocalChecked(), Vec6::y_getter); + Nan::SetAccessor(vec6Ctor->InstanceTemplate(), Nan::New("z").ToLocalChecked(), Vec6::z_getter); + Nan::SetPrototypeMethod(vec6Ctor, "at", Vec6::At); + Nan::SetPrototypeMethod(vec6Ctor, "norm", Vec6::Norm); + Vec6::Init(vec6Ctor); + v8::Local ctor = Nan::New(Vec::New); ctor->InstanceTemplate()->SetInternalFieldCount(1); ctor->SetClassName(Nan::New("Vec").ToLocalChecked()); @@ -46,35 +61,53 @@ NAN_MODULE_INIT(Vec::Init) { Nan::Set(target,Nan::New("Vec2").ToLocalChecked(), FF::getFunction(ctor)); Nan::Set(target,Nan::New("Vec3").ToLocalChecked(), FF::getFunction(ctor)); Nan::Set(target,Nan::New("Vec4").ToLocalChecked(), FF::getFunction(ctor)); + Nan::Set(target,Nan::New("Vec6").ToLocalChecked(), FF::getFunction(ctor)); }; NAN_METHOD(Vec::New) { FF::TryCatch tryCatch("Vec::New"); FF_ASSERT_CONSTRUCT_CALL(); - if (info.Length() < 2) { - return tryCatch.throwError("Vec::New - expected arguments (w), x, y, (z)"); + if (info.Length() < 2 || info.Length() > 6 || info.Length() == 5) { + return tryCatch.throwError("Vec::New - expected arguments (u, v), (w), x, y, (z)"); } v8::Local jsVec; - if (info.Length() == 4) { - jsVec = FF::newInstance(Nan::New(Vec4::constructor)); - Nan::ObjectWrap::Unwrap(jsVec)->self = cv::Vec4d( - info[0]->ToNumber(Nan::GetCurrentContext()).ToLocalChecked()->Value(), - info[1]->ToNumber(Nan::GetCurrentContext()).ToLocalChecked()->Value(), - info[2]->ToNumber(Nan::GetCurrentContext()).ToLocalChecked()->Value(), - info[3]->ToNumber(Nan::GetCurrentContext()).ToLocalChecked()->Value() - ); - } else { - double x = info[0]->ToNumber(Nan::GetCurrentContext()).ToLocalChecked()->Value(); - double y = info[1]->ToNumber(Nan::GetCurrentContext()).ToLocalChecked()->Value(); - if (info.Length() == 3) { - double z = info[2]->ToNumber(Nan::GetCurrentContext()).ToLocalChecked()->Value(); - jsVec = FF::newInstance(Nan::New(Vec3::constructor)); - Nan::ObjectWrap::Unwrap(jsVec)->self = cv::Vec3d(x, y, z); - } - else { + + switch(info.Length()) { + case 2: jsVec = FF::newInstance(Nan::New(Vec2::constructor)); - Nan::ObjectWrap::Unwrap(jsVec)->self = cv::Vec2d(x, y); - } + Nan::ObjectWrap::Unwrap(jsVec)->self = cv::Vec2d( + info[0]->ToNumber(Nan::GetCurrentContext()).ToLocalChecked()->Value(), + info[1]->ToNumber(Nan::GetCurrentContext()).ToLocalChecked()->Value() + ); + break; + case 3: + jsVec = FF::newInstance(Nan::New(Vec3::constructor)); + Nan::ObjectWrap::Unwrap(jsVec)->self = cv::Vec3d( + info[0]->ToNumber(Nan::GetCurrentContext()).ToLocalChecked()->Value(), + info[1]->ToNumber(Nan::GetCurrentContext()).ToLocalChecked()->Value(), + info[2]->ToNumber(Nan::GetCurrentContext()).ToLocalChecked()->Value() + ); + break; + case 4: + jsVec = FF::newInstance(Nan::New(Vec4::constructor)); + Nan::ObjectWrap::Unwrap(jsVec)->self = cv::Vec4d( + info[0]->ToNumber(Nan::GetCurrentContext()).ToLocalChecked()->Value(), + info[1]->ToNumber(Nan::GetCurrentContext()).ToLocalChecked()->Value(), + info[2]->ToNumber(Nan::GetCurrentContext()).ToLocalChecked()->Value(), + info[3]->ToNumber(Nan::GetCurrentContext()).ToLocalChecked()->Value() + ); + break; + case 6: + jsVec = FF::newInstance(Nan::New(Vec6::constructor)); + Nan::ObjectWrap::Unwrap(jsVec)->self = cv::Vec6d( + info[0]->ToNumber(Nan::GetCurrentContext()).ToLocalChecked()->Value(), + info[1]->ToNumber(Nan::GetCurrentContext()).ToLocalChecked()->Value(), + info[2]->ToNumber(Nan::GetCurrentContext()).ToLocalChecked()->Value(), + info[3]->ToNumber(Nan::GetCurrentContext()).ToLocalChecked()->Value(), + info[4]->ToNumber(Nan::GetCurrentContext()).ToLocalChecked()->Value(), + info[5]->ToNumber(Nan::GetCurrentContext()).ToLocalChecked()->Value() + ); + break; } info.GetReturnValue().Set(jsVec); } diff --git a/cc/core/Vec.h b/cc/core/Vec.h index 908c75aa9..55e9c338a 100644 --- a/cc/core/Vec.h +++ b/cc/core/Vec.h @@ -3,6 +3,7 @@ #include "Vec2.h" #include "Vec3.h" #include "Vec4.h" +#include "Vec6.h" #ifndef __FF_VEC_H__ #define __FF_VEC_H__ @@ -14,6 +15,7 @@ class Vec : public Nan::ObjectWrap { static NAN_METHOD(NewVec2); static NAN_METHOD(NewVec3); static NAN_METHOD(NewVec4); + static NAN_METHOD(NewVec6); static Nan::Persistent constructor; }; diff --git a/cc/core/Vec6.h b/cc/core/Vec6.h new file mode 100644 index 000000000..dfcd72455 --- /dev/null +++ b/cc/core/Vec6.h @@ -0,0 +1,50 @@ +#include +#include "coreUtils.h" +#include "NativeNodeUtils.h" +#include "macros.h" + +#ifndef __FF_VEC6_H__ +#define __FF_VEC6_H__ + +class Vec6 : public FF::ObjectWrap { +public: + static Nan::Persistent constructor; + + static const char* getClassName() { + return "Vec6"; + } + + static NAN_METHOD(New) { + Vec6* self = new Vec6(); + self->Wrap(info.Holder()); + info.GetReturnValue().Set(info.Holder()); + } + + static void Init(v8::Local ctor) { + FF_PROTO_SET_MATRIX_OPERATIONS(ctor); + } + + FF_GETTER_CUSTOM(u, FF::DoubleConverter, self[0]); + FF_GETTER_CUSTOM(v, FF::DoubleConverter, self[1]); + FF_GETTER_CUSTOM(w, FF::DoubleConverter, self[2]); + FF_GETTER_CUSTOM(x, FF::DoubleConverter, self[3]); + FF_GETTER_CUSTOM(y, FF::DoubleConverter, self[4]); + FF_GETTER_CUSTOM(z, FF::DoubleConverter, self[5]); + + FF_INIT_VEC6_OPERATIONS(); + static NAN_METHOD(Dot) { + FF_OPERATOR_RET_SCALAR(&cv::Vec6d::dot, FF_APPLY_CLASS_FUNC, Vec6, "Dot"); + } + static NAN_METHOD(Norm) { + info.GetReturnValue().Set(Nan::New(cv::norm(Vec6::unwrapSelf(info)))); + } + + static NAN_METHOD(At) { + FF::TryCatch tryCatch("Vec6::At"); + FF_ASSERT_INDEX_RANGE(info[0]->ToInt32(Nan::GetCurrentContext()).ToLocalChecked()->Value(), 5, "Vec6"); + cv::Vec6d vecSelf = Vec6::unwrapSelf(info); + info.GetReturnValue().Set(vecSelf[info[0]->ToInt32(Nan::GetCurrentContext()).ToLocalChecked()->Value()]); + } +}; + +#endif \ No newline at end of file diff --git a/cc/core/core.cc b/cc/core/core.cc index 00a815681..17798bd0f 100644 --- a/cc/core/core.cc +++ b/cc/core/core.cc @@ -127,6 +127,13 @@ NAN_METHOD(Core::Partition) { } numLabels = cv::partition(pts, labels, predicateFactory(cb)); } + else if (Vec6::hasInstance(data0)) { + std::vector pts; + if (Vec6::ArrayConverter::arg(0, &pts, info)) { + return tryCatch.reThrow(); + } + numLabels = cv::partition(pts, labels, predicateFactory(cb)); + } else if (Mat::hasInstance(data0)) { std::vector mats; if (Mat::ArrayConverter::arg(0, &mats, info)) { diff --git a/cc/core/coreUtils.h b/cc/core/coreUtils.h index 79f01df7c..df19527fa 100644 --- a/cc/core/coreUtils.h +++ b/cc/core/coreUtils.h @@ -164,6 +164,7 @@ #define FF_INIT_VEC2_OPERATIONS() FF_INIT_MATRIX_OPERATIONS(Vec2); #define FF_INIT_VEC3_OPERATIONS() FF_INIT_MATRIX_OPERATIONS(Vec3); #define FF_INIT_VEC4_OPERATIONS() FF_INIT_MATRIX_OPERATIONS(Vec4); +#define FF_INIT_VEC6_OPERATIONS() FF_INIT_MATRIX_OPERATIONS(Vec6); namespace FF { template diff --git a/lib/index.d.ts b/lib/index.d.ts index 682f58e2a..784c5d5c9 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -6,6 +6,7 @@ export * from './typings/Vec.d'; export * from './typings/Vec2.d'; export * from './typings/Vec3.d'; export * from './typings/Vec4.d'; +export * from './typings/Vec6.d'; export * from './typings/Point.d'; export * from './typings/Point2.d'; export * from './typings/Point3.d'; diff --git a/lib/typings/Vec4.d.ts b/lib/typings/Vec4.d.ts index 0cb5bcf6d..abe0a5960 100644 --- a/lib/typings/Vec4.d.ts +++ b/lib/typings/Vec4.d.ts @@ -1,9 +1,9 @@ import { Vec } from './Vec.d'; export class Vec4 extends Vec { + readonly w: number; readonly x: number; readonly y: number; readonly z: number; - readonly w: number; - constructor(x: number, y: number, z: number, w: number); + constructor(w: number, x: number, y: number, z: number); } diff --git a/lib/typings/Vec6.d.ts b/lib/typings/Vec6.d.ts new file mode 100644 index 000000000..13d0bfd9b --- /dev/null +++ b/lib/typings/Vec6.d.ts @@ -0,0 +1,11 @@ +import { Vec } from './Vec.d'; + +export class Vec6 extends Vec { + readonly u: number; + readonly v: number; + readonly w: number; + readonly x: number; + readonly y: number; + readonly z: number; + constructor(u: number, v: number, w: number, x: number, y: number, z: number); +} diff --git a/lib/typings/cv.d.ts b/lib/typings/cv.d.ts index 80df6b56b..688e6f5b6 100644 --- a/lib/typings/cv.d.ts +++ b/lib/typings/cv.d.ts @@ -3,6 +3,7 @@ import { Size } from './Size.d'; import { Vec2 } from './Vec2.d'; import { Vec3 } from './Vec3.d'; import { Vec4 } from './Vec4.d'; +import { Vec6 } from './Vec6.d'; import { Point2 } from './Point2.d'; import { Point3 } from './Point3.d'; import { KeyPoint } from './KeyPoint.d'; @@ -139,6 +140,7 @@ export function partition(data: Point3[], predicate: (pt1: Point3, pt2: Point3) export function partition(data: Vec2[], predicate: (vec1: Vec2, vec2: Vec2) => boolean): { labels: number[], numLabels: number }; export function partition(data: Vec3[], predicate: (vec1: Vec3, vec2: Vec3) => boolean): { labels: number[], numLabels: number }; export function partition(data: Vec4[], predicate: (vec1: Vec4, vec2: Vec4) => boolean): { labels: number[], numLabels: number }; +export function partition(data: Vec6[], predicate: (vec1: Vec6, vec2: Vec6) => boolean): { labels: number[], numLabels: number }; export function partition(data: Mat[], predicate: (mat1: Mat, mat2: Mat) => boolean): { labels: number[], numLabels: number }; export function perspectiveTransform(mat: Mat, m: Mat): Mat; export function perspectiveTransformAsync(mat: Mat, m: Mat): Promise; diff --git a/test/tests/core/Vec/VecTests.js b/test/tests/core/Vec/VecTests.js index 2a6da1ce6..4a027c91a 100644 --- a/test/tests/core/Vec/VecTests.js +++ b/test/tests/core/Vec/VecTests.js @@ -45,6 +45,23 @@ module.exports = function ({ cv, utils }) { expect(vec4.at(3)).to.equal(30); }); }); + + describe('Vec6', () => { + const vec6 = new cv.Vec(5, 10, 20, 30, 40, 50); + it('should throw index out of bounds', () => { + assertError(() => vec6.at(-1), 'Index out of bounds: Vec6 at index -1'); + assertError(() => vec6.at(6), 'Index out of bounds: Vec6 at index 6'); + }); + + it('should return values from indices', () => { + expect(vec6.at(0)).to.equal(5); + expect(vec6.at(1)).to.equal(10); + expect(vec6.at(2)).to.equal(20); + expect(vec6.at(3)).to.equal(30); + expect(vec6.at(4)).to.equal(40); + expect(vec6.at(5)).to.equal(50); + }); + }); }); }; diff --git a/test/tests/core/Vec/constructorTests.js b/test/tests/core/Vec/constructorTests.js index fbbf8f035..0fd45a26a 100644 --- a/test/tests/core/Vec/constructorTests.js +++ b/test/tests/core/Vec/constructorTests.js @@ -14,6 +14,10 @@ module.exports = function ({ cv, utils }) { assertError(() => new cv.Vec(0), 'expected arguments'); }); + it('should throw for trying to insantiate invalid vec5', () => { + assertError(() => new cv.Vec(5, 10, 20, 30, 40), 'Vec::New - expected arguments (u, v), (w), x, y, (z)'); + }); + describe('Vec2', () => { it('should have int positions', () => { const x = 100; @@ -107,5 +111,49 @@ module.exports = function ({ cv, utils }) { assertPropsWithValue(new cv.Vec(w, x, y, z))({ w, x, y, z }); }); }); + + describe('Vec6', () => { + it('should have int positions', () => { + const u = 50; + const v = 100; + const w = 200; + const x = 300; + const y = 400; + const z = 500; + assertPropsWithValue(new cv.Vec(u, v, w, x, y, z))({ u, v, w, x, y, z }); + }); + + it('should have double positions', () => { + const u = 50.99; + const v = 100.12345; + const w = 200.89764; + const x = 300.034; + const y = 400.254; + const z = 500.543; + assertPropsWithValue(new cv.Vec(u, v, w, x, y, z))({ u, v, w, x, y, z }); + }); + + it('should have negative int positions', () => { + it('should have int positions', () => { + const u = -50; + const v = -100; + const w = -200; + const x = -300; + const y = -400; + const z = -500; + assertPropsWithValue(new cv.Vec(u, v, w, x, y, z))({ u, v, w, x, y, z }); + }); + }); + + it('should have negative double positions', () => { + const u = -50.99; + const v = -100.12345; + const w = -200.89764; + const x = -300.034; + const y = -400.254; + const z = -500.543; + assertPropsWithValue(new cv.Vec(u, v, w, x, y, z))({ u, v, w, x, y, z }); + }); + }); }); }; diff --git a/test/tests/core/Vec/operatorTests.js b/test/tests/core/Vec/operatorTests.js index 2c24c0ec9..26de9bc7d 100644 --- a/test/tests/core/Vec/operatorTests.js +++ b/test/tests/core/Vec/operatorTests.js @@ -289,5 +289,93 @@ module.exports = function ({ cv, utils }) { }); }); }); + + describe('Vec6', () => { + const vec0 = new cv.Vec(50, 100, 200, 300, 400, 500); + const vec1 = new cv.Vec(10, 25, 50, 75, 100, 125); + const vec2 = new cv.Vec(2, 5, 4, 3, 2, 1); + const operatorRequiresArg = OperatorRequiresArg(vec0); + describe('add', () => { + operatorRequiresArg('add'); + + it('add vectors', () => { + assertPropsWithValue(vec0.add(vec1))({ u: 60, v: 125, w: 250, x: 375, y: 500, z: 625 }); + }); + }); + + describe('sub', () => { + operatorRequiresArg('sub'); + + it('subtract vectors', () => { + assertPropsWithValue(vec0.sub(vec1))({ u: 40, v: 75, w: 150, x: 225, y: 300, z: 375 }); + }); + }); + + describe('mul', () => { + operatorRequiresArg('mul', true); + + it('multiply vector by scalar', () => { + assertPropsWithValue(vec0.mul(2))({ u: 100, v: 200, w: 400, x: 600, y: 800, z: 1000 }); + }); + }); + + describe('div', () => { + operatorRequiresArg('div', true); + + it('divide vector by scalar', () => { + assertPropsWithValue(vec0.div(2))({ u: 25, v: 50, w: 100, x: 150, y: 200, z: 250 }); + }); + }); + + describe('hMul', () => { + operatorRequiresArg('hMul'); + + it('elementwise multiply vectors', () => { + assertPropsWithValue(vec0.hMul(vec2))({ u: 100, v: 500, w: 800, x: 900, y: 800, z: 500 }); + }); + }); + + describe('hDiv', () => { + operatorRequiresArg('hDiv'); + + it('elementwise divide vectors', () => { + assertPropsWithValue(vec0.hDiv(vec2))({ u: 25, v: 20, w: 50, x: 100, y: 200, z: 500 }); + }); + }); + + describe('dot', () => { + operatorRequiresArg('dot'); + + it('compute dot product of vectors', () => { + expect(vec0.dot(vec2)).to.equal(3600); + }); + }); + + describe('absdiff', () => { + operatorRequiresArg('absdiff'); + + it('apply absdiff to matrices', () => { + assertPropsWithValue(new cv.Vec(0, 100, 50, 25, 150, 10).absdiff(new cv.Vec(50, 25, 75, 25, 50, 20)))({ u: 50, v: 75, w: 25, x: 0, y: 100, z: 10 }); + }); + }); + + describe('exp', () => { + it('apply exp to vector', () => { + assertPropsWithValue(new cv.Vec(Math.log(1), Math.log(4), 0, Math.log(0), Math.log(4), Math.log(4)).exp())({ u: 1, v: 4, w: 1, x: 0, y: 4, z: 4 }); + }); + }); + + describe('sqrt', () => { + it('apply sqrt to vector', () => { + assertPropsWithValue(new cv.Vec(0, 4, 16, 64, 256, 1024).sqrt())({ u: 0, v: 2, w: 4, x: 8, y: 16, z: 32 }); + }); + }); + + describe('norm', () => { + it('should return magnitude', () => { + expect(new cv.Vec(Math.sqrt(8), Math.sqrt(8), Math.sqrt(8), Math.sqrt(8), Math.sqrt(16), Math.sqrt(16)).norm()).to.equal(8); + }); + }); + }); }); }; diff --git a/test/tests/core/coreTests.js b/test/tests/core/coreTests.js index eb115721a..cb21ad8e0 100644 --- a/test/tests/core/coreTests.js +++ b/test/tests/core/coreTests.js @@ -82,6 +82,10 @@ module.exports = function ({ cv, utils }) { partitionTests(() => new cv.Vec(0, 0, 0, 0)); }); + describe('Vec6 input', () => { + partitionTests(() => new cv.Vec(0, 0, 0, 0, 0, 0)); + }); + describe('Mat input', () => { partitionTests(() => new cv.Mat()); });