diff --git a/src/AIS/AIS_PointCloud.cxx b/src/AIS/AIS_PointCloud.cxx index 0b63b95bfb..6fda44896f 100644 --- a/src/AIS/AIS_PointCloud.cxx +++ b/src/AIS/AIS_PointCloud.cxx @@ -15,7 +15,6 @@ #include #include -#include #include #include @@ -37,6 +36,7 @@ IMPLEMENT_STANDARD_RTTIEXT(AIS_PointCloud, AIS_InteractiveObject) AIS_PointCloud::AIS_PointCloud() : AIS_InteractiveObject() { + SetHilightMode (1); } //======================================================================= @@ -66,27 +66,28 @@ void AIS_PointCloud::SetPoints (const Handle(TColgp_HArray1OfPnt)& theCoords return; Standard_Integer aNumPoints = theCoords->Length(); + Standard_Boolean hasColors = !theColors.IsNull() && aNumPoints == theColors->Length(); - Standard_Boolean aHasColors = Standard_False; - if (!theColors.IsNull() && aNumPoints == theColors->Length()) - aHasColors = Standard_True; - - myPoints = new Graphic3d_ArrayOfPoints (aNumPoints, aHasColors); + Standard_Integer aDiffColors = 0; + if (hasColors) + aDiffColors = theColors->Lower() - theCoords->Lower(); + myPoints = new Graphic3d_ArrayOfPoints (aNumPoints, hasColors); for (Standard_Integer aPntIter = theCoords->Lower(); aPntIter <= theCoords->Upper(); aPntIter++) { myPoints->AddVertex (theCoords->Value (aPntIter)); + if (hasColors) + myPoints->SetVertexColor (myPoints->VertexNumber(), theColors->Value (aPntIter + aDiffColors)); } +} - if (aHasColors) - { - Standard_Integer aNumVertex = 1; - for(Standard_Integer aColorIter = theColors->Lower(); aColorIter <= theColors->Upper(); aColorIter++) - { - myPoints->SetVertexColor (aNumVertex, theColors->Value (aColorIter)); - aNumVertex++; - } - } +//======================================================================= +//function : GetPoints +//purpose : +//======================================================================= +const Handle(Graphic3d_ArrayOfPoints) AIS_PointCloud::GetPoints() const +{ + return myPoints; } //======================================================================= @@ -121,19 +122,29 @@ void AIS_PointCloud::SetColor (const Quantity_Color& theColor) //======================================================================= void AIS_PointCloud::Compute(const Handle(PrsMgr_PresentationManager3d)& /*thePresentationManager*/, const Handle(Prs3d_Presentation)& thePresentation, - const Standard_Integer /*theMode*/) + const Standard_Integer theMode) { thePresentation->Clear(); - if (GetPoints().IsNull() || !GetPoints()->IsValid()) - return; - - Handle(Graphic3d_Group) aGroup = Prs3d_Root::CurrentGroup (thePresentation); - if (myDrawer->HasPointAspect()) + switch (theMode) { - aGroup->SetPrimitivesAspect (myDrawer->PointAspect()->Aspect()); + case 0: + { + const Handle(Graphic3d_ArrayOfPoints) aPoints = GetPoints(); + if (aPoints.IsNull() || !aPoints->IsValid()) + return; + + Handle(Graphic3d_AspectMarker3d) aMarkerAspect = myDrawer->PointAspect()->Aspect(); + if (!myDrawer->HasPointAspect()) + aMarkerAspect->SetType (Aspect_TOM_POINT); + + Handle(Graphic3d_Group) aGroup = Prs3d_Root::CurrentGroup (thePresentation); + aGroup->SetGroupPrimitivesAspect (aMarkerAspect); + aGroup->AddPrimitiveArray (aPoints); + } + default: + break; } - aGroup->AddPrimitiveArray (GetPoints()); } //======================================================================= diff --git a/src/AIS/AIS_PointCloud.hxx b/src/AIS/AIS_PointCloud.hxx index 6a875d8b4c..02603c0cc0 100644 --- a/src/AIS/AIS_PointCloud.hxx +++ b/src/AIS/AIS_PointCloud.hxx @@ -33,6 +33,7 @@ DEFINE_STANDARD_HANDLE (AIS_PointCloud, AIS_InteractiveObject) //! Interactive object for set of points. +//! Selection services are not provided by this class. class AIS_PointCloud : public AIS_InteractiveObject { @@ -54,7 +55,7 @@ public: //! Get the points. //! @return the array of points. - Standard_EXPORT virtual const Handle(Graphic3d_ArrayOfPoints)& GetPoints () const; + Standard_EXPORT virtual const Handle(Graphic3d_ArrayOfPoints) GetPoints() const; //! Redefined method implemets the standard behavior. Standard_EXPORT virtual void SetColor (const Quantity_NameOfColor theColor); diff --git a/src/AIS/AIS_PointCloud.lxx b/src/AIS/AIS_PointCloud.lxx deleted file mode 100644 index 5858036efc..0000000000 --- a/src/AIS/AIS_PointCloud.lxx +++ /dev/null @@ -1,19 +0,0 @@ -// Created on: 2014-08-25 -// Created by: Maxim GLIBIN -// Copyright (c) 2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - -inline const Handle(Graphic3d_ArrayOfPoints)& AIS_PointCloud::GetPoints() const -{ - return myPoints; -} diff --git a/src/AIS/FILES b/src/AIS/FILES index b335e6b64a..2f3c30c8e9 100755 --- a/src/AIS/FILES +++ b/src/AIS/FILES @@ -22,5 +22,4 @@ AIS_DiameterDimension.cxx AIS_RadiusDimension.hxx AIS_RadiusDimension.cxx AIS_PointCloud.hxx -AIS_PointCloud.lxx AIS_PointCloud.cxx diff --git a/src/ViewerTest/ViewerTest_ObjectCommands.cxx b/src/ViewerTest/ViewerTest_ObjectCommands.cxx index 83a479889d..ebe642cdb3 100644 --- a/src/ViewerTest/ViewerTest_ObjectCommands.cxx +++ b/src/ViewerTest/ViewerTest_ObjectCommands.cxx @@ -5047,120 +5047,168 @@ static Standard_Integer VPointCloud (Draw_Interpretor& /*theDI*/, if (theArgNum < 2) { std::cout << theArgs[0] << " error: wrong number of parameters. Type 'help " - << theArgs[0] << "' for more information.\n"; + << theArgs[0] << "' for more information." << std::endl; return 1; } Handle(AIS_InteractiveContext) anAISContext = ViewerTest::GetAISContext(); if (anAISContext.IsNull()) { - std::cerr << "Call 'vinit' before!\n"; + std::cout << "Call 'vinit' before!" << std::endl; return 1; } Standard_Integer anArgIter = 1; - // Get point cloud object name + // Point cloud name TCollection_AsciiString aName (theArgs[anArgIter++]); - // Sets default value - Standard_Integer aMode = 0; - Standard_Integer aMarkerType = -1; - Quantity_NameOfColor aColorName = Quantity_NOC_YELLOW; - Standard_Real aScale = 1.0; - Standard_Boolean aHasColor = Standard_True; - Standard_Integer aNumberOfPoints = 100; + // Default value + Standard_Integer aModeSetPoints = 0; + Standard_Integer aMarkerType = -1; + Quantity_NameOfColor aColorName = Quantity_NOC_GOLDENROD; + Standard_Real aScale = 1.0; + Standard_Integer aPointsOnSide = 100; + Standard_Real aStartPos[3] = {0.0, 0.0, 0.0}; + + Standard_Boolean hasColors = Standard_True; + Standard_Boolean hasAspect = Standard_False; // Parses arguments for (; anArgIter < theArgNum; ++anArgIter) { - const TCollection_AsciiString anArg (theArgs[anArgIter]); - if (anArg.Search ("Mode=") > -1) + Standard_CString anArg = theArgs[anArgIter]; + TCollection_AsciiString aFlag (anArg); + aFlag.LowerCase(); + + if (aFlag == "-mode") { - aMode = anArg.Token ("=", 2).IntegerValue(); - if (aMode < 0 && aMode > 1) + if (++anArgIter >= theArgNum) { - std::cerr << "Wrong argument : " << anArg << std::endl; + std::cout << "Error: wrong syntax at " << anArg << std::endl; + return 1; + } + aModeSetPoints = Draw::Atoi (theArgs[anArgIter]); + if (aModeSetPoints < 0 || aModeSetPoints > 1) + { + std::cout << "Wrong value for argument " << aFlag << std::endl; return 1; } } - else if (anArg.Search ("MarkerType=") > -1) + else if (aFlag == "-nbpointsonside") { - aMarkerType = anArg.Token ("=", 2).IntegerValue(); + if (++anArgIter >= theArgNum) + { + std::cout << "Error: wrong syntax at " << aFlag << std::endl; + return 1; + } + aPointsOnSide = Draw::Atoi (theArgs[anArgIter]); + if (aPointsOnSide <= 0) + { + std::cout << "Wrong value for argument " << aFlag << std::endl; + return 1; + } } - else if (anArg.Search ("ColorName=") > -1) + else if (aFlag == "-markertype") { - aColorName = ViewerTest::GetColorFromName (anArg.Token ("=", 2).ToCString()); - aHasColor = Standard_False; + if (++anArgIter >= theArgNum) + { + std::cout << "Error: wrong syntax at " << aFlag << std::endl; + return 1; + } + aMarkerType = Draw::Atoi (theArgs[anArgIter]); + hasAspect = Standard_True; } - else if (anArg.Search ("Scale=") > -1) + else if (aFlag == "-scale") { - aScale = anArg.Token ("=", 2).RealValue(); + if (++anArgIter >= theArgNum) + { + std::cout << "Error: wrong syntax at " << aFlag << std::endl; + return 1; + } + aScale = Draw::Atof (theArgs[anArgIter]); + hasAspect = Standard_True; } - else if (anArg.Search ("NumPoints=") > -1) + else if (aFlag == "-color") { - aNumberOfPoints = anArg.Token ("=", 2).IntegerValue(); + if (++anArgIter >= theArgNum) + { + std::cout << "Error: wrong syntax at " << aFlag << std::endl; + return 1; + } + aColorName = ViewerTest::GetColorFromName (theArgs[anArgIter]); + hasColors = Standard_False; + hasAspect = Standard_True; } else { - std::cerr << "Wrong argument: " << anArg << std::endl; + std::cout << "Wrong argument: " << aFlag << std::endl; return 1; } } + Standard_Integer aNumberOfPoints = (Standard_Integer )Pow (aPointsOnSide, 3); + std::cout << "Number of points: " << aNumberOfPoints << std::endl; + // Point cloud initialization Handle(AIS_PointCloud) aPointCloud = new AIS_PointCloud(); - - if (aMode == 0) + if (aModeSetPoints == 0) { - Handle(Graphic3d_ArrayOfPoints) anArrayPoints = new Graphic3d_ArrayOfPoints (aNumberOfPoints, aHasColor); - for (Standard_Integer anIter = 1; anIter < aNumberOfPoints; anIter++) + Handle(Graphic3d_ArrayOfPoints) anArrayPoints = new Graphic3d_ArrayOfPoints (aNumberOfPoints, + aNumberOfPoints != 1 && hasColors); + if (aNumberOfPoints == 1) { - // Create random point - gp_Pnt aPoint (randomReal (0., 5000.), - randomReal (0., 5000.), - randomReal (0., 5000.)); - - // Create random color - Quantity_Color aColor (randomReal (0., 1.), - randomReal (0., 1.), - randomReal (0., 1.), - Quantity_TOC_RGB); - - // Add point with color in array - anArrayPoints->AddVertex (aPoint, aColor); + anArrayPoints->AddVertex (aStartPos[0], aStartPos[1], aStartPos[2]); + anArrayPoints->SetVertexColor (anArrayPoints->VertexNumber(), + Quantity_Color (Quantity_NOC_YELLOW)); } - // Set array of points in point cloud object - aPointCloud->SetPoints (anArrayPoints); - } - else if (aMode == 1) - { - Handle(TColgp_HArray1OfPnt) aCoords = new TColgp_HArray1OfPnt (1, aNumberOfPoints); - Handle(Quantity_HArray1OfColor) aColors = new Quantity_HArray1OfColor (1, aNumberOfPoints); - - if (aCoords->Length() != aColors->Length()) + else { - std::cerr << "Wrong length of arrays" << std::endl; + for (Standard_Real aStepX = 1; aStepX <= aPointsOnSide; aStepX++) + { + for (Standard_Real aStepY = 1; aStepY <= aPointsOnSide; aStepY++) + { + for (Standard_Real aStepZ = 1; aStepZ <= aPointsOnSide; aStepZ++) + { + anArrayPoints->AddVertex (aStartPos[0] + aStepX, + aStartPos[1] + aStepY, + aStartPos[2] + aStepZ); + anArrayPoints->SetVertexColor (anArrayPoints->VertexNumber(), + aStepX / aPointsOnSide, + aStepY / aPointsOnSide, + aStepZ / aPointsOnSide); + } + } + } + } + + if (anArrayPoints.IsNull() || !anArrayPoints->IsValid()) + { + std::cerr << "No points found." << std::endl; return 1; } - for (Standard_Integer aPntIt = aCoords->Lower(); aPntIt <= aCoords->Upper(); aPntIt++) + // Set array of points in point cloud object + aPointCloud->SetPoints (anArrayPoints); + } + else if (aModeSetPoints == 1) + { + Handle(TColgp_HArray1OfPnt) aCoords = new TColgp_HArray1OfPnt (1, aNumberOfPoints); + for (Standard_Integer i = 1; i <= aNumberOfPoints; i++) { gp_Pnt aPoint (randomReal (0., 5000.), randomReal (0., 5000.), randomReal (0., 5000.)); - aCoords->SetValue (aPntIt, aPoint); + aCoords->SetValue (i, aPoint); } - if (aHasColor) + Handle(Quantity_HArray1OfColor) aColors = new Quantity_HArray1OfColor (1, aNumberOfPoints); + if (hasColors) { - for (Standard_Integer aColorIt = aColors->Lower(); aColorIt <= aColors->Upper(); aColorIt++) + for (Standard_Integer i = 1; i <= aNumberOfPoints; i++) { - Quantity_Color aColor (randomReal (0., 1.), - randomReal (0., 1.), - randomReal (0., 1.), - Quantity_TOC_RGB); - aColors->SetValue (aColorIt, aColor); + Quantity_Color aColor (360. * i / aNumberOfPoints, 1.0, 0.5, Quantity_TOC_HLS); + aColors->SetValue (i, aColor); } } else @@ -5171,14 +5219,17 @@ static Standard_Integer VPointCloud (Draw_Interpretor& /*theDI*/, aPointCloud->SetPoints (aCoords, aColors); } - // Set point aspect for attributes of interactive object - aPointCloud->Attributes()->SetPointAspect ( - new Prs3d_PointAspect (aMarkerType >= 0 ? (Aspect_TypeOfMarker )aMarkerType : Aspect_TOM_POINT, - aColorName, - aScale)); + if (hasAspect) + { + // Set point aspect for attributes of interactive object + aPointCloud->Attributes()->SetPointAspect ( + new Prs3d_PointAspect (aMarkerType >= 0 ? (Aspect_TypeOfMarker )aMarkerType : Aspect_TOM_POINT, + aColorName, + aScale)); + } VDisplayAISObject (aName, aPointCloud); - + return 0; } @@ -5341,10 +5392,12 @@ void ViewerTest::ObjectCommands(Draw_Interpretor& theCommands) __FILE__, VFont, group); theCommands.Add ("vpointcloud", - "vpointcloud usage:\n" - "vpointcloud ObjectName [Mode=1]\n" - " [NumPoints=100]\n" - " [MarkerType=0] [ColorName=GREEN] [Scale=1.0]" - "\n\t\t: Create an interactive object for arbitary set of points.", - __FILE__, VPointCloud, group); + "vpointcloud [name]" + "\n\t\t: [-mode ModeSetPoints]" + "\n\t\t: [-nbpointsonside NumberPointsOnSide]" + "\n\t\t: [-markertype TypeOfMarker]" + "\n\t\t: [-color ColorName]" + "\n\t\t: [-scale Scale]" + "\n\t\t: Create an interactive object for arbitary set of points.", + __FILE__, VPointCloud, group); } diff --git a/src/ViewerTest/ViewerTest_ViewerCommands.cxx b/src/ViewerTest/ViewerTest_ViewerCommands.cxx index ca971e1f83..105ff60881 100644 --- a/src/ViewerTest/ViewerTest_ViewerCommands.cxx +++ b/src/ViewerTest/ViewerTest_ViewerCommands.cxx @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -4355,61 +4356,147 @@ static int VDiffImage (Draw_Interpretor& theDI, Standard_Integer theArgNb, const //purpose : Emulates different types of selection by mouse: // 1) single click selection // 2) selection with rectangle having corners at pixel positions (x1,y1) and (x2,y2) -// 3) selection with polygon having corners at -// pixel positions (x1,y1),...,(xn,yn) -// 4) any of these selections with shift button pressed +// 3) selection with polygon having corners at pixel positions (x1,y1),...,(xn,yn) +// 4) selection of specified shape(s) +// 5) any of these selections with shift button pressed //======================================================================= -static Standard_Integer VSelect (Draw_Interpretor& di, - Standard_Integer argc, - const char ** argv) +static Standard_Integer VSelect (Draw_Interpretor& /*theDI*/, + Standard_Integer theArgNum, + const char ** theArgs) { - if(argc < 3) + if (theArgNum < 2) { - di << "Usage : " << argv[0] << " x1 y1 [x2 y2 [... xn yn]] [shift_selection = 1|0]" << "\n"; + std::cout << theArgs[0] << " error: wrong number of parameters. Type 'help " + << theArgs[0] << "' for more information." << std::cout; return 1; } - Handle(AIS_InteractiveContext) myAIScontext = ViewerTest::GetAISContext(); - if(myAIScontext.IsNull()) + Handle(AIS_InteractiveContext) anAISContext = ViewerTest::GetAISContext(); + if (anAISContext.IsNull()) { - di << "use 'vinit' command before " << argv[0] << "\n"; + std::cout << "Call 'vinit' before!" << std::endl; return 1; } - const Standard_Boolean isShiftSelection = (argc>3 && !(argc%2) && (atoi(argv[argc-1])==1)); - Handle(ViewerTest_EventManager) aCurrentEventManager = ViewerTest::CurrentEventManager(); - aCurrentEventManager->MoveTo(atoi(argv[1]),atoi(argv[2])); - if(argc <= 4) + + Standard_Integer anArgIter = 1; + Standard_Boolean toSelectShape = Standard_False; + Standard_Boolean isShift = Standard_False; + + TCollection_AsciiString anArgum (theArgs[anArgIter]); + if (!anArgum.IsIntegerValue() && !anArgum.IsRealValue()) + toSelectShape = Standard_True; + + NCollection_Sequence aParams; + for (; anArgIter < theArgNum; ++anArgIter) { - if(isShiftSelection) - aCurrentEventManager->ShiftSelect(); - else - aCurrentEventManager->Select(); + TCollection_AsciiString anArgum (theArgs[anArgIter]); + anArgum.LowerCase(); + + if (anArgum == "-shift") + { + isShift = Standard_True; + continue; + } + + if (!anArgum.IsEmpty()) + aParams.Append (anArgum); } - else if(argc <= 6) + + if (toSelectShape) { - if(isShiftSelection) - aCurrentEventManager->ShiftSelect(atoi(argv[1]),atoi(argv[2]),atoi(argv[3]),atoi(argv[4])); - else - aCurrentEventManager->Select(atoi(argv[1]),atoi(argv[2]),atoi(argv[3]),atoi(argv[4])); + NCollection_Sequence::Iterator aParamIter (aParams); + for (; aParamIter.More(); aParamIter.Next()) + { + TCollection_AsciiString aShapeName = aParamIter.Value(); + if (!GetMapOfAIS().IsBound2 (aShapeName)) + { + std::cout << "Warning: no object with name '" << aShapeName << "' found." << std::endl; + continue; + } + + const Handle(Standard_Transient) anObject = GetMapOfAIS().Find2 (aShapeName); + if (!anObject->IsKind (STANDARD_TYPE (AIS_InteractiveObject))) + { + std::cout << "Warning: no shape with type '" << STANDARD_TYPE (AIS_InteractiveObject)->Name() + << "' found." << std::endl; + continue; + } + + const Handle(AIS_InteractiveObject) anInterObject = Handle(AIS_InteractiveObject)::DownCast (anObject); + if (anInterObject.IsNull()) + { + std::cout << "Error: 3D object is expected to be an AIS_InteractiveObject" << std::endl; + continue; + } + + if (anAISContext->HasOpenedContext()) + { + if (isShift) + anAISContext->LocalContext()->AddOrRemoveSelected (anInterObject); + else + anAISContext->LocalContext()->SetSelected (anInterObject); + } + else + { + if (isShift) + anAISContext->AddOrRemoveCurrentObject (anInterObject); + else + anAISContext->SetCurrentObject (anInterObject); + } + } } else { - Standard_Integer anUpper = 0; + Standard_Integer aNumParams = aParams.Length(); + if (aNumParams < 2 || aNumParams % 2 != 0) + { + std::cout << "Error: no X Y defined." << std::endl; + return 1; + } - if(isShiftSelection) - anUpper = (argc-1)/2; + Handle(ViewerTest_EventManager) aCurrentEventManager = ViewerTest::CurrentEventManager(); + aCurrentEventManager->MoveTo (Draw::Atoi (aParams (1).ToCString()), + Draw::Atoi (aParams (2).ToCString())); + if (aNumParams == 2) + { + if (isShift) + aCurrentEventManager->ShiftSelect(); + else + aCurrentEventManager->Select(); + } + else if (aNumParams == 4) + { + if (isShift) + aCurrentEventManager->ShiftSelect (Draw::Atoi (aParams (1).ToCString()), + Draw::Atoi (aParams (2).ToCString()), + Draw::Atoi (aParams (3).ToCString()), + Draw::Atoi (aParams (4).ToCString())); + else + aCurrentEventManager->Select (Draw::Atoi (aParams (1).ToCString()), + Draw::Atoi (aParams (2).ToCString()), + Draw::Atoi (aParams (3).ToCString()), + Draw::Atoi (aParams (4).ToCString())); + } else - anUpper = argc/2; - TColgp_Array1OfPnt2d aPolyline(1,anUpper); + { + Standard_Integer anUpper = 0; + if (isShift) + anUpper = (aNumParams - 1) / 2; + else + anUpper = aNumParams / 2; + TColgp_Array1OfPnt2d aPolyline (1, anUpper); - for(Standard_Integer i=1;i<=anUpper;++i) - aPolyline.SetValue(i,gp_Pnt2d(atoi(argv[2*i-1]),atoi(argv[2*i]))); + for (Standard_Integer i = 1; i <= anUpper; ++i) + aPolyline.SetValue (i, gp_Pnt2d (Draw::Atoi (aParams (2*i - 1).ToCString()), + Draw::Atoi (aParams (2*i).ToCString()))); - if(isShiftSelection) - aCurrentEventManager->ShiftSelect(aPolyline); - else - aCurrentEventManager->Select(aPolyline); + if (isShift) + aCurrentEventManager->ShiftSelect (aPolyline); + else + aCurrentEventManager->Select (aPolyline); + } } + return 0; } @@ -6298,12 +6385,14 @@ void ViewerTest::ViewerCommands(Draw_Interpretor& theCommands) "diffimage : diffimage imageFile1 imageFile2 toleranceOfColor(0..1) blackWhite(1|0) borderFilter(1|0) [diffImageFile]", __FILE__, VDiffImage, group); theCommands.Add ("vselect", - "vselect x1 y1 [x2 y2 [x3 y3 ... xn yn]] [shift_selection = 0|1]\n" + "vselect x1 y1 [x2 y2 [x3 y3 ... xn yn]] [-shift]\n" + "vselect name1 [name2 [name3 ... nameN]] [-shift]\n" "- emulates different types of selection:\n" "- 1) single click selection\n" "- 2) selection with rectangle having corners at pixel positions (x1,y1) and (x2,y2)\n" "- 3) selection with polygon having corners in pixel positions (x1,y1), (x2,y2),...,(xn,yn)\n" - "- 4) any of these selections with shift button pressed", + "- 4) selection of specified shape(s)\n" + "- 5) any of these selections with shift button pressed", __FILE__, VSelect, group); theCommands.Add ("vmoveto", "vmoveto x y"