Difference between revisions of "Example cmd sy partifymesh"

From The Foundry MODO SDK wiki
Jump to: navigation, search
(Initial version.)
 
(Alter the code comments 'cause of the wiki formating for <source>)
Line 14: Line 14:
  
 
<source lang="cpp">
 
<source lang="cpp">
//Copyright Synide(Sy) 2011
+
/*
//synide@rocketmail.com
+
    Copyright Synide(Sy) 2011
//=====================================
+
    synide@rocketmail.com
// Notes:
+
    =====================================
//            April, 2012 - Revised for Liki example.
+
    Notes:
 +
    - April, 2012 - Revised for Liki example.
 +
*/
  
 
#include <time.h>
 
#include <time.h>
Line 32: Line 34:
  
 
std::string fnI2S(unsigned value, bool notnullterminated = true, unsigned padding = 0, bool asHexadecimal = false, bool withHexPrefix = false) {
 
std::string fnI2S(unsigned value, bool notnullterminated = true, unsigned padding = 0, bool asHexadecimal = false, bool withHexPrefix = false) {
     //'fnI2S'
+
     /*
    //    Integer to string, a variant of David Vasquez's Integer2String
+
          'fnI2S'
 +
          Integer to string, a variant of David Vasquez's Integer2String
 +
    */
 
     std::ostringstream os;
 
     std::ostringstream os;
 
     std::string result;
 
     std::string result;
Line 93: Line 97:
 
void CSyPartifyMesh::cmd_Execute(unsigned int flags) {
 
void CSyPartifyMesh::cmd_Execute(unsigned int flags) {
 
/*
 
/*
*    We restrict the LayerScan's results to the 'Primary' selected mesh so that we
+
    We restrict the LayerScan's results to the 'Primary' selected mesh so that we
*    guard against the user having multiple 'selected' meshs which could result in overload.
+
    guard against the user having multiple 'selected' meshs which could result in overload.
 
*/
 
*/
  
Line 109: Line 113:
 
         theItem.Name(&ItemName);
 
         theItem.Name(&ItemName);
 
      
 
      
     // As there should only ever be 1 mesh found by the LayerScan it's index should be zero.
+
     /* As there should only ever be 1 mesh found by the LayerScan it's index should be zero. */
 
     if (LayerScan.EditMeshByIndex(0, theMesh))
 
     if (LayerScan.EditMeshByIndex(0, theMesh))
 
     {
 
     {
 
         int NoOfPolygons = theMesh.NPolygons();
 
         int NoOfPolygons = theMesh.NPolygons();
  
         // Only do work if there are polygons.
+
         /* Only do work if there are polygons. */
 
         if (NoOfPolygons > 0)
 
         if (NoOfPolygons > 0)
 
         {
 
         {
             // Set 'thePolygon' object as the polygon accessor for the 'theMesh' object.
+
             /* Set 'thePolygon' object as the polygon accessor for the 'theMesh' object. */
 
             thePolygon.fromMeshObj(theMesh);
 
             thePolygon.fromMeshObj(theMesh);
  
             // Set 'theTag' object as the 'tags' accessor for the polygon object.
+
             /* Set 'theTag' object as the 'tags' accessor for the polygon object. */
 
             theTag.set(thePolygon);
 
             theTag.set(thePolygon);
  
Line 130: Line 134:
 
             for (int i1 = 0; i1 < NoOfPolygons; i1++)
 
             for (int i1 = 0; i1 < NoOfPolygons; i1++)
 
             {
 
             {
                 //Ask the polygon accessor to populate itself based on the poly index.
+
                 /* Ask the polygon accessor to populate itself based on the poly index. */
 
                 thePolygon.SelectByIndex(i1);
 
                 thePolygon.SelectByIndex(i1);
  
                 // Construct a unique 'part' tag name.
+
                 /* Construct a unique 'part' tag name. */
 
                 tag = "poly_" + fnI2S(i1);
 
                 tag = "poly_" + fnI2S(i1);
  
                 // Ask the 'tags' accessor to set the 'PART' tag.
+
                 /* Ask the 'tags' accessor to set the 'PART' tag. */
 
                 theTag.Set(LXi_PTAG_PART, tag.c_str());
 
                 theTag.Set(LXi_PTAG_PART, tag.c_str());
  
  
                 //logging at 20%, 40%, 60%, 80%
+
                 /* logging at 20%, 40%, 60%, 80% */
 
                 if (PartifyMeshLog && (i1 == (msg_stride * msg_counter)))
 
                 if (PartifyMeshLog && (i1 == (msg_stride * msg_counter)))
 
                 {
 
                 {
Line 164: Line 168:
 
             }
 
             }
  
             // Tell the LayerScan that we are only 'editing' polygon tags.
+
             /* Tell the LayerScan that we are only 'editing' polygon tags. */
 
             LayerScan.SetMeshChange(0, LXf_MESHEDIT_POL_TAGS);
 
             LayerScan.SetMeshChange(0, LXf_MESHEDIT_POL_TAGS);
  
             // Apply the mesh changes.
+
             /* Apply the mesh changes. */
 
             LayerScan.Apply();
 
             LayerScan.Apply();
  
Line 187: Line 191:
 
};
 
};
  
//NOTE: Read the code carefully, this is a sy_partifymesh::initialize() method, NOT the global initialize() method.
+
/* NOTE: Read the code carefully, this is a sy_partifymesh::initialize() method, NOT the global initialize() method. */
 
void initialize() {
 
void initialize() {
 
         CLxGenericPolymorph    *srv;
 
         CLxGenericPolymorph    *srv;
Line 195: Line 199:
 
         thisModule.AddServer(sy_partifymesh::SERVER_NAME, srv); //Just to illustrate thisModule usage instead of lx::AddServer
 
         thisModule.AddServer(sy_partifymesh::SERVER_NAME, srv); //Just to illustrate thisModule usage instead of lx::AddServer
 
};
 
};
 +
 +
};/* namespace sy_partifymesh */
 +
  
 
/*
 
/*
//NOTE: Read the code carefully, this is a sy_partifymesh::cleanup() method, NOT the global cleanup() method.
+
  I like the new layout of utilizing namespaces with an initialize() in each and then
void cleanup()
+
  calling those from the global initialize() method. Keeps things tidy!
{
+
  FYI. Don't need to extern in this instance 'cause we're only 1 file...
   
+
};
+
 
*/
 
*/
 
};//namespace sy_partifymesh
 
 
 
// I like the new layout of utilizing namespaces with an initialize() in each and then
 
// calling those from the global initialize() method. Keeps things tidy!
 
// FYI. Don't need to extern in this instance 'cause we're only 1 file...
 
  
 
void initialize()
 
void initialize()
Line 216: Line 214:
 
};
 
};
  
/* No need for a cleanup() but if we did they'd go here...
+
/*
// NOTE: cleanup() should rarely need to be used if things a destructed well.
+
  No need for a cleanup() but if we did they'd go here...
void cleanup()
+
  NOTE: cleanup() should rarely need to be used if things a destructed well.
{
+
    sy_partifymesh::cleanup();
+
};
+
 
*/
 
*/
 +
//void cleanup()
 +
//{
 +
//    sy_partifymesh::cleanup();
 +
//};
 
</source>
 
</source>
  

Revision as of 15:16, 12 April 2012

Introduction

This article is a an example of a adding a 'new' command to modo.

...TODO: more info req'd... eg. style, coding do's & dont's etc., why one should have used a PreExecute() method to prompt the user with continue or not if there were a ginormous amount of polygons in the given mesh, etc. etc.


Code

... TODO: Discuss the code ...


/*
    Copyright Synide(Sy) 2011
    synide@rocketmail.com
    =====================================
    Notes:
     - April, 2012 - Revised for Liki example.
*/
 
#include <time.h>
#include <string>
#include <sstream>
 
#include <lxidef.h>
#include <lx_plugin.hpp>
#include <lx_layer.hpp>
#include <lx_mesh.hpp>
#include <lx_log.hpp>
#include <lxu_command.hpp>
 
std::string fnI2S(unsigned value, bool notnullterminated = true, unsigned padding = 0, bool asHexadecimal = false, bool withHexPrefix = false) {
    /*
          'fnI2S'
          Integer to string, a variant of David Vasquez's Integer2String
    */
    std::ostringstream os;
    std::string result;
    if (asHexadecimal) {os << std::hex;}
    if (value < 10 && padding) {os << 0;}
    if (value < 100 && padding > 1) {os << 0;}
    if (value < 1000 && padding > 2) {os << 0;}
    if (notnullterminated)
        os << value;
    else
        os << value << std::ends;
    if (asHexadecimal && withHexPrefix)
        result = std::string("0x");
    result += os.str();
    return result;
};
 
namespace sy_partifymesh
{
 
static char *SERVER_NAME                = "sy.partifymesh";
static char *LOG_NAME                    = "sy/cmds/partifymesh";
 
/*
 * 'sy.partifymesh' command is derived from 'CLxBasicCommand'.
 *
 * Was going to have a parameter passed to the command (name of the mesh to process) but decided against it in the end.
 *
 * We won't bother with utilizing any of the system called pre-work methods for doing
 * sanity checks etc. We'll just set the 'flags' for this command and let the system flow
 * straight through to 'cmd_Execute()' where we'll just go ahead and do the work regardless.
 * Not recommended coding... :)
 *
 * As I'm not sure if commands can utlize a 'CLxUser_Monitor'. We might have to
 * fire progress reports through to the log system. - TODO.
 */
class CSyPartifyMesh : public CLxBasicCommand {
    public:
        CSyPartifyMesh();
        int                             basic_CmdFlags      () LXx_OVERRIDE;
        void                            cmd_Execute         (unsigned int flags) LXx_OVERRIDE;
        CLxUser_LayerService            LayerService;
        CLxUser_LogService              LogService;
        CLxUser_Log                     PartifyMeshLog;
        static LXtTagInfoDesc           descInfo[];
 
};
 
LXtTagInfoDesc CSyPartifyMesh::descInfo[] = {{LXsSRV_LOGSUBSYSTEM,LOG_NAME}, {0}};
 
CSyPartifyMesh::CSyPartifyMesh() {
    LogService.GetSubSystem(LOG_NAME, PartifyMeshLog);
};
 
int CSyPartifyMesh::basic_CmdFlags() {
    return LXfCMD_UNDO;
};
 
void CSyPartifyMesh::cmd_Execute(unsigned int flags) {
/*
    We restrict the LayerScan's results to the 'Primary' selected mesh so that we
    guard against the user having multiple 'selected' meshs which could result in overload.
*/
 
    CLxUser_LayerScan               LayerScan;
    CLxUser_Item                    theItem;
    CLxUser_Mesh                    theMesh;
    CLxUser_Polygon                 thePolygon;
    CLxUser_StringTag               theTag;
    const char                      *ItemName;
 
    LayerService.BeginScan(LXf_LAYERSCAN_PRIMARY | LXf_LAYERSCAN_WRITEMESH, LayerScan);
 
    if (LayerScan.ItemByIndex(0, theItem))
        theItem.Name(&ItemName);
 
    /* As there should only ever be 1 mesh found by the LayerScan it's index should be zero. */
    if (LayerScan.EditMeshByIndex(0, theMesh))
    {
        int NoOfPolygons = theMesh.NPolygons();
 
        /* Only do work if there are polygons. */
        if (NoOfPolygons > 0)
        {
            /* Set 'thePolygon' object as the polygon accessor for the 'theMesh' object. */
            thePolygon.fromMeshObj(theMesh);
 
            /* Set 'theTag' object as the 'tags' accessor for the polygon object. */
            theTag.set(thePolygon);
 
            std::string tag;
            unsigned msg_stride = NoOfPolygons / 5;
            unsigned msg_counter = 1;
            clock_t timerStart = clock();
 
            for (int i1 = 0; i1 < NoOfPolygons; i1++)
            {
                /* Ask the polygon accessor to populate itself based on the poly index. */
                thePolygon.SelectByIndex(i1);
 
                /* Construct a unique 'part' tag name. */
                tag = "poly_" + fnI2S(i1);
 
                /* Ask the 'tags' accessor to set the 'PART' tag. */
                theTag.Set(LXi_PTAG_PART, tag.c_str());
 
 
                /* logging at 20%, 40%, 60%, 80% */
                if (PartifyMeshLog && (i1 == (msg_stride * msg_counter)))
                {
                    clock_t timerDiff = clock() - timerStart;
                    double duration = (double)timerDiff / (double)CLOCKS_PER_SEC;
                    switch(msg_counter)
                    {
                        case 1:
                            PartifyMeshLog.Message(LXe_INFO, "20\%%, %u polygons in %.6f seconds.", i1, duration);
                            break;
                        case 2:
                            PartifyMeshLog.Message(LXe_INFO, "40\%%, %u polygons in %.6f seconds.", i1, duration);
                            break;
                        case 3:
                            PartifyMeshLog.Message(LXe_INFO, "60\%%, %u polygons in %.6f seconds.", i1, duration);
                            break;
                        case 4:
                            PartifyMeshLog.Message(LXe_INFO, "80\%%, %u polygons in %.6f seconds.", i1, duration);
                            break;
                    }
                    msg_counter += 1;
                }
            }
 
            /* Tell the LayerScan that we are only 'editing' polygon tags. */
            LayerScan.SetMeshChange(0, LXf_MESHEDIT_POL_TAGS);
 
            /* Apply the mesh changes. */
            LayerScan.Apply();
 
            clock_t timerDiff = clock() - timerStart;
            double duration = (double)timerDiff / (double)CLOCKS_PER_SEC;
            PartifyMeshLog.Message(LXe_INFO, "100\%%, %u polygons in %.6f seconds.", NoOfPolygons, duration);
        }
        else
        {
            if (ItemName)
                PartifyMeshLog.Message(LXe_WARNING, "'%s' has zero polygons.", ItemName);
        }
    }
    else
    {
        PartifyMeshLog.Message(LXe_NOTFOUND, "No valid mesh found!");
    }
 
};
 
/* NOTE: Read the code carefully, this is a sy_partifymesh::initialize() method, NOT the global initialize() method. */
void initialize() {
        CLxGenericPolymorph    *srv;
        srv = new CLxPolymorph<CSyPartifyMesh>;
        srv->AddInterface(new CLxIfc_Command <CSyPartifyMesh>);
        srv->AddInterface(new CLxIfc_StaticDesc <CSyPartifyMesh>);
        thisModule.AddServer(sy_partifymesh::SERVER_NAME, srv); //Just to illustrate thisModule usage instead of lx::AddServer
};
 
};/* namespace sy_partifymesh */
 
 
/*
   I like the new layout of utilizing namespaces with an initialize() in each and then
   calling those from the global initialize() method. Keeps things tidy!
   FYI. Don't need to extern in this instance 'cause we're only 1 file...
*/
 
void initialize()
{
    sy_partifymesh::initialize();
};
 
/*
   No need for a cleanup() but if we did they'd go here...
   NOTE: cleanup() should rarely need to be used if things a destructed well.
*/
//void cleanup()
//{
//    sy_partifymesh::cleanup();
//};


Config

In the following xml file a new commands category is created to apportion this particular command under 'Sy/Cmds' in modo. Also, we specify command help details regarding what the command does. It should be noted that one would also include help & examples for the command and/or command parameters within the 'CommandHelp' element. Finally, we tell modo to enable the log sub-system that this command plugin is registering itself under because by default they are disabled within the logging system.


<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <atom type="Categories">
    <hash type="Category" key="Commands">
      <hash type="C" key="sy.partifymesh">Sy/Cmds</hash>
    </hash>
  </atom>
 
  <atom type="CommandHelp">
    <hash type="Command" key="sy.partifymesh@en_US">
      <atom type="UserName">sy.partifymesh</atom>
      <atom type="Desc">Give each polygon in selected mesh a unique 'part' tag.</atom>
      <atom type="Tooltip"></atom>
    </hash>
  </atom>
  <atom type="StartupCommands">
    <list type="Command">log.subEnable "sy/cmds/partifymesh" true</list>
  </atom>
</configuration>