/* //paragraph [1] Title: [{\Large \bf \begin{center}] [\end{center}}] //paragraph [10] Footnote: [{\footnote{] [}}] //[TOC] [\tableofcontents] //[NP] [\newpage] //[ue] [\"u] //[e] [\'e] ---- This file is part of SECONDO. Copyright (C) 2004, University in Hagen, Department of Computer Science, Database Systems for New Applications. SECONDO 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 2 of the License, or (at your option) any later version. SECONDO 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 SECONDO; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ---- 01590 Fachpraktikum "Erweiterbare Datenbanksysteme" WS 2014 / 2015 Jens Breit, Joachim Dechow, Daniel Fuchs, Simon Jacobi, G[ue]nther Milosits, Daijun Nagamine, Hans-Joachim Klauke. Betreuer: Dr. Thomas Behr, Fabio Vald[e]s [1] Implementation of a Spatial3D algebra: rotate, mirror, translate, scaleDir, scale [TOC] [NP] 1 Includes and Defines */ #include #include "Matrix4x4.h" #include "Spatial3D.h" #include "Spatial3DTransformations.h" #include "Algebras/Relation-C++/RelationAlgebra.h" extern NestedList* nl; extern QueryProcessor* qp; using namespace std; namespace spatial3DTransformations { /* 1 Some auxiliary functions for the transformations: 1.1 ~multiplyMatrix4x4WithPoint~ Multiplys the Point with the matrix. */ void multiplyMatrix4x4WithPoint(Matrix4x4* matrix, Point3d* point) { double newX = point->getX() * matrix->values[0][0] + point->getY() * matrix->values[0][1] + point->getZ() * matrix->values[0][2] + 1 * matrix->values[0][3]; double newY = point->getX() * matrix->values[1][0] + point->getY() * matrix->values[1][1] + point->getZ() * matrix->values[1][2] + 1 * matrix->values[1][3]; double newZ = point->getX() * matrix->values[2][0] + point->getY() * matrix->values[2][1] + point->getZ() * matrix->values[2][2] + 1 * matrix->values[2][3]; point->set(newX, newY, newZ); }; void multiplyMatrix4x4WithSimplePoint( Matrix4x4* matrix, SimplePoint3d& point) { double newX = point.getX() * matrix->values[0][0] + point.getY() * matrix->values[0][1] + point.getZ() * matrix->values[0][2] + 1 * matrix->values[0][3]; double newY = point.getX() * matrix->values[1][0] + point.getY() * matrix->values[1][1] + point.getZ() * matrix->values[1][2] + 1 * matrix->values[1][3]; double newZ = point.getX() * matrix->values[2][0] + point.getY() * matrix->values[2][1] + point.getZ() * matrix->values[2][2] + 1 * matrix->values[2][3]; point.set(newX, newY, newZ); }; /* 1.2 ~rotatePoint~, ~rotateTriangleContainer~ Rotates the objects. */ void rotatePoint( Point3d* point,const Point3d* center, const Vector3d* axis, double angle,Point3d& res) { Matrix4x4* matrix = Matrix4x4::GetRotationMatrix(center->getX(),center->getY(), center->getZ(),axis->getX(),axis->getY(), axis->getZ(),angle); multiplyMatrix4x4WithPoint(matrix,point); res.set(point); delete matrix; }; void rotateTriangleContainer(TriangleContainer* container, const Point3d* center, const Vector3d* axis, double angle,TriangleContainer& res) { Matrix4x4* matrix = Matrix4x4::GetRotationMatrix(center->getX(),center->getY(), center->getZ(),axis->getX(),axis->getY(), axis->getZ(),angle); int size = container->size(); res.clear(); res.startBulkLoad(); for(int i = 0; i < size; i++){ Triangle triangle = container->get(i); SimplePoint3d pointA = triangle.getA(); SimplePoint3d pointB = triangle.getB(); SimplePoint3d pointC = triangle.getC(); multiplyMatrix4x4WithSimplePoint(matrix,pointA); multiplyMatrix4x4WithSimplePoint(matrix,pointB); multiplyMatrix4x4WithSimplePoint(matrix,pointC); res.add(Triangle(pointA,pointB,pointC)); } res.endBulkLoad(NO_REPAIR); delete matrix; }; /* 1.3 ~mirrorPoint~, ~mirrorTriangleContainer~ Mirrors the objects. */ void mirrorPoint(Point3d* point, const Plane3d* plane,Point3d& res) { Matrix4x4* matrix = Matrix4x4::GetMirrorMatrix( plane->getPoint().getX(), plane->getPoint().getY(), plane->getPoint().getZ(), plane->getNormalVector().getX(), plane->getNormalVector().getY(), plane->getNormalVector().getZ()); multiplyMatrix4x4WithPoint(matrix,point); res.set(point); delete matrix; }; void mirrorTriangleContainer(TriangleContainer* container, const Plane3d* plane, TriangleContainer& res) { Matrix4x4* matrix = Matrix4x4::GetMirrorMatrix( plane->getPoint().getX(), plane->getPoint().getY(), plane->getPoint().getZ(), plane->getNormalVector().getX(), plane->getNormalVector().getY(), plane->getNormalVector().getZ()); int size = container->size(); res.clear(); res.startBulkLoad(); for(int i = 0; i < size; i++){ Triangle triangle = container->get(i); SimplePoint3d pointA = triangle.getA(); SimplePoint3d pointB = triangle.getB(); SimplePoint3d pointC = triangle.getC(); multiplyMatrix4x4WithSimplePoint(matrix,pointA); multiplyMatrix4x4WithSimplePoint(matrix,pointB); multiplyMatrix4x4WithSimplePoint(matrix,pointC); res.add(Triangle(pointA,pointC,pointB)); } res.endBulkLoad(NO_REPAIR); delete matrix; }; /* 1.4 ~translatePoint~, ~translateTriangleContainer~ Translates the objects. */ void translatePoint( Point3d* point, const Vector3d* translation,Point3d& res) { Matrix4x4* matrix = Matrix4x4::GetTranslateMatrix( translation->getX(),translation->getY(),translation->getZ()); multiplyMatrix4x4WithPoint(matrix,point); res.set(point); delete matrix; }; void translateTriangleContainer( TriangleContainer* container, const Vector3d* translation,TriangleContainer& res) { Matrix4x4* matrix = Matrix4x4::GetTranslateMatrix( translation->getX(),translation->getY(),translation->getZ()); int size = container->size(); res.clear(); res.startBulkLoad(); for(int i = 0; i < size; i++){ Triangle triangle = container->get(i); SimplePoint3d pointA = triangle.getA(); SimplePoint3d pointB = triangle.getB(); SimplePoint3d pointC = triangle.getC(); multiplyMatrix4x4WithSimplePoint(matrix,pointA); multiplyMatrix4x4WithSimplePoint(matrix,pointB); multiplyMatrix4x4WithSimplePoint(matrix,pointC); res.add(Triangle(pointA,pointB,pointC)); } res.endBulkLoad(NO_REPAIR); delete matrix; }; /* 1.5 ~scaleDirPoint~, ~scaleDirTriangleContainer~, ~scaleDirPoint~, ~scaleDirTriangleContainer~ Scales the objects. */ void scaleDirPoint( Point3d* point, const Point3d* center, const Vector3d* direction,Point3d& res) { Matrix4x4* matrix = Matrix4x4::GetScaleDirMatrix( center->getX(),center->getY(), center->getZ(), direction->getX(),direction->getY(), direction->getZ()); multiplyMatrix4x4WithPoint(matrix,point); res.set(point); delete matrix; }; void scaleDirTriangleContainer(TriangleContainer* container, const Point3d* center, const Vector3d* direction,TriangleContainer& res) { Matrix4x4* matrix = Matrix4x4::GetScaleDirMatrix( center->getX(),center->getY(), center->getZ(), direction->getX(),direction->getY(), direction->getZ()); int size = container->size(); res.clear(); res.startBulkLoad(); for(int i = 0; i < size; i++){ Triangle triangle = container->get(i); SimplePoint3d pointA = triangle.getA(); SimplePoint3d pointB = triangle.getB(); SimplePoint3d pointC = triangle.getC(); multiplyMatrix4x4WithSimplePoint(matrix,pointA); multiplyMatrix4x4WithSimplePoint(matrix,pointB); multiplyMatrix4x4WithSimplePoint(matrix,pointC); res.add(Triangle(pointA,pointB,pointC)); } res.endBulkLoad(NO_REPAIR); delete matrix; }; void scalePoint(Point3d* point, const Point3d* center, double factor,Point3d& res) { Matrix4x4* matrix = Matrix4x4::GetScaleMatrix( center->getX(),center->getY(), center->getZ(),factor); multiplyMatrix4x4WithPoint(matrix,point); res.set(point); delete matrix; }; void scaleTriangleContainer(TriangleContainer* container, const Point3d* center,double factor,TriangleContainer& res) { Matrix4x4* matrix = Matrix4x4::GetScaleMatrix( center->getX(),center->getY(), center->getZ(),factor); int size = container->size(); res.clear(); res.startBulkLoad(); for(int i = 0; i < size; i++){ Triangle triangle = container->get(i); SimplePoint3d pointA = triangle.getA(); SimplePoint3d pointB = triangle.getB(); SimplePoint3d pointC = triangle.getC(); multiplyMatrix4x4WithSimplePoint(matrix,pointA); multiplyMatrix4x4WithSimplePoint(matrix,pointB); multiplyMatrix4x4WithSimplePoint(matrix,pointC); res.add(Triangle(pointA,pointB,pointC)); } res.endBulkLoad(NO_REPAIR); delete matrix; }; /* 10 Operators Definition of operators is similar to definition of type constructors. An operator is defined by creating an instance of class ~Operator~. Again we have to define some functions before we are able to create an ~Operator~ instance. 10.1 Type mapping functions A type mapping function takes a nested list as argument. Its contents are type descriptions of an operator's input parameters. A nested list describing the output type of the operator is returned. 10.1.1 Type mapping function Spatial3DRotateMap This type mapping function is used for the ~rotate~ operator. The mamp is spatial3Dtype x point3D x vector3D x real -> spatial3Dtype */ ListExpr Spatial3DRotateMap( ListExpr args ) { if ( nl->ListLength( args ) != 4 ) { ErrorReporter::ReportError("wrong number of arguments (4 expected)"); return nl->TypeError(); } ListExpr arg1 = nl->First(args); ListExpr arg2 = nl->Second(args); ListExpr arg3 = nl->Third(args); ListExpr arg4 = nl->Fourth(args); if(nl->AtomType(arg2)!=SymbolType){ ErrorReporter::ReportError( "spatial3Dtype x point3D x vector3D x real expected"); return nl->TypeError(); } string st = nl->SymbolValue(arg2); if( st!=Point3d::BasicType()){ ErrorReporter::ReportError( "spatial3Dtype x point3D x vector3D x real expected"); return nl->TypeError(); } if(nl->AtomType(arg3)!=SymbolType){ ErrorReporter::ReportError( "spatial3Dtype x point3D x vector3D x real expected"); return nl->TypeError(); } st = nl->SymbolValue(arg3); if( st!=Vector3d::BasicType()){ ErrorReporter::ReportError( "spatial3Dtype x point3D x vector3D x real expected"); return nl->TypeError(); } if(!nl->IsEqual(arg4,CcReal::BasicType())){ ErrorReporter::ReportError( "spatial3Dtype x point3D x vector3D x real expected"); return nl->TypeError(); } if(nl->AtomType(arg1)!=SymbolType){ ErrorReporter::ReportError( "spatial3Dtype x point3D x vector3D x real expected"); return nl->TypeError(); } st = nl->SymbolValue(arg1); if( st!=Point3d::BasicType() && st!=Surface3d::BasicType() && st!=Volume3d::BasicType()){ ErrorReporter::ReportError( "spatial3Dtype x point3D x vector3D x real expected"); return nl->TypeError(); } return nl->SymbolAtom(st); } /* 10.1.2 Type mapping function Spatial3DMirrorMap This type mapping function is used for the ~mirror~ operator. The mamp is spatial3Dtype x plane3D -> spatial3Dtype */ ListExpr Spatial3DMirrorMap( ListExpr args ) { if ( nl->ListLength( args ) != 2 ) { ErrorReporter::ReportError("wrong number of arguments (2 expected)"); return nl->TypeError(); } ListExpr arg1 = nl->First(args); ListExpr arg2 = nl->Second(args); if(nl->AtomType(arg2)!=SymbolType){ ErrorReporter::ReportError("spatial3Dtype x plane3D expected"); return nl->TypeError(); } string st = nl->SymbolValue(arg2); if( st!=Plane3d::BasicType()){ ErrorReporter::ReportError("spatial3Dtype x plane3D expected"); return nl->TypeError(); } if(nl->AtomType(arg1)!=SymbolType){ ErrorReporter::ReportError("spatial3Dtype x plane3D expected"); return nl->TypeError(); } st = nl->SymbolValue(arg1); if( st!=Point3d::BasicType() && st!=Surface3d::BasicType() && st!=Volume3d::BasicType()){ ErrorReporter::ReportError("spatial3Dtype x plane3D expected"); return nl->TypeError(); } return nl->SymbolAtom(st); } /* 10.1.3 Type mapping function Spatial3DTranslateMap This type mapping function is used for the ~translate~ operator. The mamp is spatial3Dtype x vector3D -> spatial3Dtype */ ListExpr Spatial3DTranslateMap( ListExpr args ) { if ( nl->ListLength( args ) != 2 ) { ErrorReporter::ReportError("wrong number of arguments (2 expected)"); return nl->TypeError(); } ListExpr arg1 = nl->First(args); ListExpr arg2 = nl->Second(args); if(nl->ListLength( arg2 ) != 1){ ErrorReporter::ReportError("spatial3Dtype x vector3D expected1"); return nl->TypeError(); } if(nl->AtomType(nl->First( arg2 ))!=SymbolType){ ErrorReporter::ReportError("spatial3Dtype x vector3D expected5"); return nl->TypeError(); } string st = nl->SymbolValue(nl->First( arg2 )); if( st != Vector3d::BasicType()){ ErrorReporter::ReportError("spatial3Dtype x vector3D expected2"); return nl->TypeError(); } if(nl->AtomType(arg1)!=SymbolType){ ErrorReporter::ReportError("spatial3Dtype x vector3D expected3"); return nl->TypeError(); } st = nl->SymbolValue(arg1); if( st!=Point3d::BasicType() && st!=Surface3d::BasicType() && st!=Volume3d::BasicType()){ ErrorReporter::ReportError("spatial3Dtype x vector3D expected4"); return nl->TypeError(); } return nl->SymbolAtom(st); } /* 10.1.4 Type mapping function Spatial3DScaleDirMap This type mapping function is used for the ~translate~ operator. The mamp is spatial3Dtype x vector3D -> spatial3Dtype */ ListExpr Spatial3DScaleDirMap( ListExpr args ) { if ( nl->ListLength( args ) != 3 ) { ErrorReporter::ReportError("wrong number of arguments (2 expected)"); return nl->TypeError(); } ListExpr arg1 = nl->First(args); ListExpr arg2 = nl->Second(args); ListExpr arg3 = nl->Third(args); if(nl->AtomType(arg2)!=SymbolType){ ErrorReporter::ReportError( "spatial3Dtype x point3D x vector3D expected"); return nl->TypeError(); } string st = nl->SymbolValue(arg2); if( st!=Point3d::BasicType()){ ErrorReporter::ReportError( "spatial3Dtype x point3D x vector3D expected"); return nl->TypeError(); } if(nl->AtomType(arg3)!=SymbolType){ ErrorReporter::ReportError( "spatial3Dtype x point3D x vector3D expected"); return nl->TypeError(); } st = nl->SymbolValue(arg3); if( st!=Vector3d::BasicType()){ ErrorReporter::ReportError( "spatial3Dtype x point3D x vector3D expected"); return nl->TypeError(); } if(nl->AtomType(arg1)!=SymbolType){ ErrorReporter::ReportError( "spatial3Dtype x point3D x vector3D expected"); return nl->TypeError(); } st = nl->SymbolValue(arg1); if( st!=Point3d::BasicType() && st!=Surface3d::BasicType() && st!=Volume3d::BasicType()){ ErrorReporter::ReportError( "spatial3Dtype x point3D x vector3D expected"); return nl->TypeError(); } return nl->SymbolAtom(st); } /* 10.1.4 Type mapping function Spatial3DScaleDirMap This type mapping function is used for the ~translate~ operator. The mamp is spatial3Dtype x vector3D -> spatial3Dtype */ ListExpr Spatial3DScaleMap( ListExpr args ) { if ( nl->ListLength( args ) != 3 ) { ErrorReporter::ReportError("wrong number of arguments (2 expected)"); return nl->TypeError(); } ListExpr arg1 = nl->First(args); ListExpr arg2 = nl->Second(args); ListExpr arg3 = nl->Third(args); if(nl->AtomType(arg2)!=SymbolType){ ErrorReporter::ReportError("spatial3Dtype x point3D x real expected"); return nl->TypeError(); } string st = nl->SymbolValue(arg2); if( st!=Point3d::BasicType()){ ErrorReporter::ReportError("spatial3Dtype x point3D x real expected"); return nl->TypeError(); } if(!nl->IsEqual(arg3,CcReal::BasicType())){ ErrorReporter::ReportError("spatial3Dtype x point3D x real expected"); return nl->TypeError(); } if(nl->AtomType(arg1)!=SymbolType){ ErrorReporter::ReportError("spatial3Dtype x point3D x real expected"); return nl->TypeError(); } st = nl->SymbolValue(arg1); if( st!=Point3d::BasicType() && st!=Surface3d::BasicType() && st!=Volume3d::BasicType()){ ErrorReporter::ReportError("spatial3Dtype x point3D x real expected"); return nl->TypeError(); } return nl->SymbolAtom(st); } /* 10.3 Selection functions A selection function is quite similar to a type mapping function. The only difference is that it doesn't return a type but the index of a value mapping function being able to deal with the respective combination of input parameter types. Note that a selection function does not need to check the correctness of argument types; it has already been checked by the type mapping function that it is applied to correct arguments. 10.3.1 Selection function ~Spatial3DSelectTransformation~ This select function is used for the ~translate~, ~rotate~, ~mirror~ , ~scale~ and ~scaleDir~ operators. */ int Spatial3DSelect( ListExpr args ) { ListExpr arg1 = nl->First( args ); string st = nl->SymbolValue(arg1); if( st==Point3d::BasicType()){ return 0; } if( st==Surface3d::BasicType()){ return 1; } if( st==Volume3d::BasicType()){ return 2; } return -1; // This point should never be reached } /* 10.4 Value mapping functions 10.4.1 Value mapping functions of operator ~rotate~ */ template int Spatial3DRotate( Word* args, Word& result, int message, Word& local, Supplier s ){ result = qp->ResultStorage(s); T* res = static_cast(result.addr); T* st = static_cast(args[0].addr); Point3d* p = static_cast(args[1].addr); Vector3d* v = static_cast(args[2].addr); CcReal* a = static_cast(args[3].addr); if(!st->IsDefined() || !p->IsDefined() || !v->IsDefined() || !a->IsDefined()){ res->SetDefined(false); return 0; } double angle = a->GetRealval() * M_PI / 180; st->rotate(p,v,angle,*res); return 0; } /* 10.4.2 Value mapping functions of operator ~mirror~ */ template int Spatial3DMirror( Word* args, Word& result, int message, Word& local, Supplier s ){ result = qp->ResultStorage(s); T* res = static_cast(result.addr); T* st = static_cast(args[0].addr); Plane3d* p = static_cast(args[1].addr); if(!st->IsDefined() || !p->IsDefined()){ res->SetDefined(false); return 0; } st->mirror(p,*res); return 0; } /* 10.4.3 Value mapping functions of operator ~translate~ */ template int Spatial3DTranslate( Word* args, Word& result, int message, Word& local, Supplier s ){ result = qp->ResultStorage(s); T* res = static_cast(result.addr); T* st = static_cast(args[0].addr); Supplier son; Word arg; son = qp->GetSon( args[1].addr, 0 ); qp->Request( son, arg ); Vector3d* v = static_cast(arg.addr); if(!st->IsDefined() || !v->IsDefined()){ res->SetDefined(false); return 0; } st->translate(v,*res); return 0; } /* 10.4.4 Value mapping functions of operator ~scaleDir~ */ template int Spatial3DScaleDir( Word* args, Word& result, int message, Word& local, Supplier s ){ result = qp->ResultStorage(s); T* res = static_cast(result.addr); T* st = static_cast(args[0].addr); Point3d* p = static_cast(args[1].addr); Vector3d* v = static_cast(args[2].addr); if(!st->IsDefined() || !p->IsDefined() || !v->IsDefined()){ res->SetDefined(false); return 0; } st->scaleDir(p,v,*res); return 0; } /* 10.4.5 Value mapping functions of operator ~scale~ */ template int Spatial3DScale( Word* args, Word& result, int message, Word& local, Supplier s ){ result = qp->ResultStorage(s); T* res = static_cast(result.addr); T* st = static_cast(args[0].addr); Point3d* p = static_cast(args[1].addr); CcReal* a = static_cast(args[2].addr); if(!st->IsDefined() || !p->IsDefined() || !a->IsDefined()){ res->SetDefined(false); return 0; } double factor = a->GetRealval(); st->scale(p,factor,*res); return 0; } /* 10.5 Definition of operators Definition of operators is done in a way similar to definition of type constructors: an instance of class ~Operator~ is defined. Because almost all operators are overloaded, we have first do define an array of value mapping functions for each operator. 10.5.1 Definition of value mapping vectors */ ValueMapping spatial3drotatemap[] = { Spatial3DRotate, Spatial3DRotate, Spatial3DRotate}; ValueMapping spatial3dmirrormap[] = { Spatial3DMirror, Spatial3DMirror, Spatial3DMirror}; ValueMapping spatial3dtranslatemap[] = { Spatial3DTranslate, Spatial3DTranslate, Spatial3DTranslate}; ValueMapping spatial3dscaledirmap[] = { Spatial3DScaleDir, Spatial3DScaleDir, Spatial3DScaleDir}; ValueMapping spatial3dscalemap[] = { Spatial3DScale, Spatial3DScale, Spatial3DScale}; /* 10.5.2 Definition of specification strings */ const string Spatial3DSpecRotate = "( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) " "( (point3D||surface3d||volume3d x point3D x vector3D x real) -> " "point3D||surface3d||volume3d" " _ rotate[ p, v, theta ]" " rotates the object by 'theta' degrees around the line," "through the point p with vector v" " query object rotate[point,vector,10.0]" ") )"; const string Spatial3DSpecMirror = "( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) " "( (point3D||surface3d||volume3d x plane) -> " "point3D||surface3d||volume3d" " _ mirror[ p]" " mirrors the object on the plane" " query object mirror[plane]" ") )"; const string Spatial3DSpecTranslate = "( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) " "( (point3D||surface3d||volume3d x vector3D) -> " "point3D||surface3d||volume3d" " _ translate[ v]" " translate the object with the vector" " query object translate[vector]" ") )"; const string Spatial3DSpecScaleDir = "( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) " "( (point3D||surface3d||volume3d x point3D x vector3D) -> " "point3D||surface3d||volume3d" " _ scaleDir[ p, v]" " scales the object from a point with a vector--->" " query object scaleDir[point,vector]" ") )"; const string Spatial3DSpecScale = "( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) " "( (point3D||surface3d||volume3d x point3D x real) -> " "point3D||surface3d||volume3d" " _ scale[ p, a]" " scales the object from a point by a factor--->" " query object scale[point, factor]" ") )"; /* 10.5.3 Definition of the operators */ Operator* getRotatePtr(){ return new Operator( "rotate", Spatial3DSpecRotate, 3, spatial3drotatemap, Spatial3DSelect, Spatial3DRotateMap ); } Operator* getMirrorPtr(){ return new Operator( "mirror", Spatial3DSpecMirror, 3, spatial3dmirrormap, Spatial3DSelect, Spatial3DMirrorMap ); } Operator* getTranslatePtr(){ return new Operator( "translate", Spatial3DSpecTranslate, 3, spatial3dtranslatemap, Spatial3DSelect, Spatial3DTranslateMap ); } Operator* getScaleDirPtr(){ return new Operator( "scaleDir", Spatial3DSpecScaleDir, 3, spatial3dscaledirmap, Spatial3DSelect, Spatial3DScaleDirMap ); } Operator* getScalePtr(){ return new Operator( "scale", Spatial3DSpecScale, 3, spatial3dscalemap, Spatial3DSelect, Spatial3DScaleMap ); } }