TheSwamp

Code Red => ARX Programming => Topic started by: It's Alive! on December 22, 2009, 08:23:41 AM

Title: point in triangle
Post by: It's Alive! on December 22, 2009, 08:23:41 AM
Code: [Select]
 static void ArxPntInTri_getit(void)
  {
    ads_point ads_testpnt;
    AcGePoint3d a,b,c,p;
  
    if(acedGetPoint(NULL,_T("\nPick Point to test: "),ads_testpnt) != RTNORM)
    {
      PRNTERR
      return;
    }

    p = asPnt3d(ads_testpnt);
    AcDbDatabase* pDb = acdbHostApplicationServices()->workingDatabase();
    AcDbBlockTableRecordPointer pBlockTableRecord(pDb->currentSpaceId(),kForRead);
    TRYRETVOID (pBlockTableRecord.openStatus());
    AcDbBlockTableRecordIterator* pIter;
    TRYRETVOID (pBlockTableRecord->newIterator( pIter ))
    
    for (pIter->start();!pIter->done();pIter->step())
    {
      AcDbObjectId entId;
      TRYMSG(pIter->getEntityId(entId));
      AcDbEntityPointer pEnt(entId,AcDb::kForRead);
      AcDbFace* pFace = AcDbFace::cast(pEnt);
      if(pFace)
      {
        TRYMSG(pFace->getVertexAt(0,a));
        TRYMSG(pFace->getVertexAt(1,b));
        TRYMSG(pFace->getVertexAt(2,c));

        if(isInTriangle(AcGePoint2d(a.x,a.y),AcGePoint2d(b.x,b.y),
                        AcGePoint2d(c.x,c.y),AcGePoint2d(p.x,p.y)))
        {
          TRYMSG(pFace->upgradeOpen());
          TRYMSG(pFace->setColorIndex(1));
        }
      }
    }
    delete pIter;
  }

  //http://www.blackpawn.com/texts/pointinpoly/default.html
  static bool isInTriangle(const AcGePoint2d &a,
                                 const AcGePoint2d &b,
                                 const AcGePoint2d &c,
                                 const AcGePoint2d &p)
  {
    AcGeVector2d v0 = c-a;
    AcGeVector2d v1 = b-a;
    AcGeVector2d v2 = p-a;
    double  dot00 = v0.dotProduct(v0);
    double  dot01 = v0.dotProduct(v1);
    double  dot02 = v0.dotProduct(v2);
    double  dot11 = v1.dotProduct(v1);
    double  dot12 = v1.dotProduct(v2);
    double invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
    double  u = (dot11 * dot02 - dot01 * dot12) * invDenom;
    double  v = (dot00 * dot12 - dot01 * dot02) * invDenom;
    return (u > 0) && (v > 0) && (u + v < 1);
  }

fixed leak
Title: Re: point in triangle
Post by: MickD on December 22, 2009, 03:31:06 PM
Nice and neat Dan!
As you are only passing in x & y coord's the function relies on the face not being perp to world I'm guessing, maybe a test is requied? or is that covered by a fuzz factor?

You have quite a few macros going there, care to share :)
You don't see too many macros in C++ as it's percieved to not be 'pure' but they can sure save a lot of typing ;)
Title: Re: point in triangle
Post by: It's Alive! on December 22, 2009, 06:02:27 PM
You're Right! I had a request to write a routine that would find the triangle that enclosed a point. Since the Delaunay triangle algorithm that create the triangles only used X & Y to calculate the circumcircle, I thought I would only use X & Y.. or is my thinking flawed?   :|

It's the same Macros from this thread  http://www.theswamp.org/index.php?topic=28170.0
I know, I know Bjarne Stroustrup would have a Danish meatball, but I can't tell you how many times these little gems of saved me from digging through tons of code to find a bug.  8-)

Code: [Select]
//returns ES
#define TRYRETES(statement)    { Acad::ErrorStatus st = (statement); if (st != Acad::eOk){\
  acutPrintf(_T("\nError: Line %ld [%s]\nIn function %s"),\
  __LINE__, acadErrorStatusText(st),_T(__FUNCTION__)); return st;}}

// returns void
#define TRYRETVOID(statement)  { Acad::ErrorStatus st = (statement); if (st != Acad::eOk){\
  acutPrintf(_T("\nError: Line %ld [%s]\nIn function %s"),\
  __LINE__, acadErrorStatusText(st),_T(__FUNCTION__)); return;}}

// only prints es message
#define TRYMSG(statement)      { Acad::ErrorStatus st = (statement); if (st != Acad::eOk){\
  acutPrintf(_T("\nError: Line %ld [%s]\nIn function %s"),\
  __LINE__, acadErrorStatusText(st),_T(__FUNCTION__));}}

#define PRNTERR acutPrintf(_T("\nFail %s,Line %ld: ") ,_T(__FUNCTION__), __LINE__ );
Title: Re: point in triangle
Post by: Kerry on December 22, 2009, 06:31:43 PM
OT a little.

Dan,
In your Capture.png : there is an area bottom left which is NOT triangulated. ??

appears to have 3 boundarys missing
Title: Re: point in triangle
Post by: MickD on December 22, 2009, 06:46:46 PM
Thanks for the macros, almost forgot that thread :)

...I thought I would only use X & Y.. or is my thinking flawed?   :|

It should be fine as long as you are working in world coord's (world ucs) but even then I would do a check for colinear edges just in case. Take topology for example, you could encounter a shear cliff face which may be perpendicular to world. A slim chance but you never know :)

Title: Re: point in triangle
Post by: It's Alive! on December 22, 2009, 08:27:52 PM
OT a little.

Dan,
In your Capture.png : there is an area bottom left which is NOT triangulated. ??

appears to have 3 boundarys missing

That's my goof, I had erased those. :whistle:
Title: Re: point in triangle
Post by: It's Alive! on December 22, 2009, 08:47:17 PM
Thanks for the macros, almost forgot that thread :)

You're welcome, I guess I should have removed the macros before I posted, I normally do.

It should be fine as long as you are working in world coord's (world ucs) but even then I would do a check for colinear edges just in case. Take topology for example, you could encounter a shear cliff face which may be perpendicular to world. A slim chance but you never know :)


Ah good point.  :-)
Title: Re: point in triangle
Post by: frtfff on December 22, 2009, 08:51:07 PM
Crazy code.How do you know this ?
Title: Re: point in triangle
Post by: It's Alive! on December 22, 2009, 09:03:04 PM
Crazy code.How do you know this ?

Cause I'm a wild and crazy guy  :laugh:

Title: Re: point in triangle
Post by: pkohut on February 05, 2010, 05:26:26 PM
It's the same Macros from this thread  http://www.theswamp.org/index.php?topic=28170.0
I know, I know Bjarne Stroustrup would have a Danish meatball, but I can't tell you how many times these little gems of saved me from digging through tons of code to find a bug.  8-)

I'm in the "Don't like macros" camp.  Whenever your thinking of writing a macro for compile time
substitution you'd probably be better served making it an inline function.

Beside the issues that Stroustrup, Meyers, Schildt, and company mention, you can not easily
debug function macros or step into them while debugging.

Also, there are 3 macros defined, TRYRETES, TRYRETVOID, and TRYMSG.  Each is basically
the same (the second and third are).  To avoid any mental gymnastics, I'd suggest just
settling on one, that being the first.  Because the macro is inlined, the return st in the first
macro will simply be compiled away or become a simple register modification.

Inline function alternative -
Code: [Select]
inline Acad::ErrorStatus EsCheck(Acad::ErrorStatus es)
{
  if(es != Acad::eOk) {
    acutPrintf(_T("\nError: Line %ld [%s]\nIn function %s"),
            __LINE__, acadErrorStatusText(es), _T(__FUNCTION__));
  }
  return es;
}

Title: Re: point in triangle
Post by: It's Alive! on February 05, 2010, 06:34:07 PM
Hi Paul,

I was thinking about changing those to inline functions, but I had the impression that they behaved differently.
Question, Will your example actually return out of the function? Or just return  another copy of an error status?
My goal is to test and if the test fails, print a message then exit the function. (without using exceptions)
Title: Re: point in triangle
Post by: pkohut on February 05, 2010, 07:39:32 PM
They do behave differently, more to the point, the inlined function behaves as
expected.  Macros on the otherhand can have wierd side effects that can be
hard to track down.

It will return a copy of the error status.  Since Acad::ErrorStatus is an enum it
will be just a simple move of an int to a register making the value immediatedly
ready for the next operation, if it's needed.

I tested a few different implementation since I last posted and came up with
this one.  Overall it's better than the other because the print function is not
inlined, and you get encapsulation for free.  Also free is its size, because there
is no member data.

If you turn on "Assembler output" -> "Assembly with source code", you can look
at the generated code.  It's pretty interesting what a little tweek in the code
will do at build time.

Code: [Select]
struct EsChecker
{
    static Acad::ErrorStatus op(Acad::ErrorStatus es)
    {
        if(es != Acad::eOk) {
            EsPrint(es);
        }
        return es;
    }

    __declspec(noinline) static void EsPrint(Acad::ErrorStatus es)
    {
        acutPrintf(_T("\nError: Line %ld [%s]\nIn function %s"),
            __LINE__, acadErrorStatusText(es), _T(__FUNCTION__));
    }
};
Useage:
Code: [Select]
EsChecker::op(pDb->getFilename(pcszFilename));
Title: Re: point in triangle
Post by: It's Alive! on February 05, 2010, 07:47:25 PM
But then I would need to test the error status again if I wanted to return out of the function on != eOk right?
Title: Re: point in triangle
Post by: It's Alive! on February 05, 2010, 08:05:49 PM
I want to break out of the function when es != eOk.

in this example, the macro returns out of the function and prints an error message. When they are nested it performs a kind of stack trace.

I haven't tried, but the expansion of __FUNCTION__  elsewhere, might not give me  correct info.



Title: Re: point in triangle
Post by: It's Alive! on February 05, 2010, 08:15:47 PM
I guess I could just start using exceptions to unwind the stack. I was just following along with the ARX style and the macros saved typing..

What's your opinion on exceptions? I read that compilers refuse to unroll loops that are in a try catch
Title: Re: point in triangle
Post by: pkohut on February 05, 2010, 08:26:24 PM
But then I would need to test the error status again if I wanted to return out of the function on != eOk right?

Oh, I see what the macros are doing, all three are different.  So yes you would need the test.
Title: Re: point in triangle
Post by: It's Alive! on February 05, 2010, 08:30:06 PM
see I get a nice little statck trace  :laugh:

Code: [Select]
  static Acad::ErrorStatus foofar( void )
  {
    return Acad::eNoDatabase;
  }

  static Acad::ErrorStatus foo( void )
  {
    TRYRETES(foofar())
    //...
  }

  static Acad::ErrorStatus foobar( void )
  {
    TRYRETES(foo())
    //...
  }

  // - RxlRxLib._getPoint command (do not rename)
  static void RxlRxLib_getPoint(void)
  {
    TRYRETVOID(foobar())
    //..
  }

Title: Re: point in triangle
Post by: pkohut on February 05, 2010, 08:47:07 PM
Here's a tweeked version of your macros.  This allows the continued use
of the macro feature you use.

Code: [Select]
__declspec(noinline) void EsPrint(Acad::ErrorStatus es)
{
    acutPrintf(_T("\nError: Line %ld [%s]\nIn function %s"),
        __LINE__, acadErrorStatusText(es), _T(__FUNCTION__));
}

//returns ES
#define TRYRETES(statement)    { Acad::ErrorStatus st = (statement); if (st != Acad::eOk){\
    EsPrint(st);\
    return st;}}

// returns void
#define TRYRETVOID(statement)  { Acad::ErrorStatus st = (statement); if (st != Acad::eOk){\
    EsPrint(st);\
    return;}}

// only prints es message
#define TRYMSG(statement)      { Acad::ErrorStatus st = (statement); if (st != Acad::eOk){\
    EsPrint(st);\
    }}
Title: Re: point in triangle
Post by: It's Alive! on February 05, 2010, 08:55:55 PM
hmmm, This is what I get

Title: Re: point in triangle
Post by: It's Alive! on February 05, 2010, 08:58:41 PM
maybe

Code: [Select]
__declspec(noinline) void EsPrint(Acad::ErrorStatus es, LPCTSTR func)
{
  acutPrintf(_T("\nError: Line %ld [%s]\nIn function %s"),
    __LINE__, acadErrorStatusText(es), func);
}

//returns ES
#define TRYRETES(statement)    { Acad::ErrorStatus st = (statement); if (st != Acad::eOk){\
  EsPrint(st,_T(__FUNCTION__));\
  return st;}}

// returns void
#define TRYRETVOID(statement)  { Acad::ErrorStatus st = (statement); if (st != Acad::eOk){\
  EsPrint(st,_T(__FUNCTION__));\
  return;}}

// only prints es message
#define TRYMSG(statement)      { Acad::ErrorStatus st = (statement); if (st != Acad::eOk){\
  EsPrint(st,_T(__FUNCTION__));\
}}
Title: Re: point in triangle
Post by: It's Alive! on February 05, 2010, 09:00:21 PM
works! Thanks Paul!

Title: Re: point in triangle
Post by: pkohut on February 05, 2010, 09:05:31 PM
Ha, was just replying with a similar fix.
Title: Re: point in triangle
Post by: It's Alive! on February 05, 2010, 09:16:43 PM
Question, Why would I not want EsPrint inline ?
Title: Re: point in triangle
Post by: pkohut on February 06, 2010, 01:58:22 AM
Your handlers are small enough that the extra 16 bytes they use per call
isn't a big deal, even used a 1000 times.  What if you decide the handler
should do something extra, maybe 3 extra somethings and adds a missly
64 bytes for each use.  Tough call, unless you take the advise of "Item 30
in Effective C++, by Scott Myers"

Quote
This leads to a logical strategy for determining which functions should be declared inline and which should not. Initially, don't inline anything, or at least limit your inlining to those functions that must be inlined (see item 46) or are truly trivial (such as Person::age on page 135). By employing inlines cautionsly, you facilitate your use of a debugger, but you also put inlining in its proper place: as a hand applied optimization.  Don't forget the empirically determined rule of 80-20, which states that a typical program spends 80% of its time executing only 20% of its code. It's an important rule, because it reminds you that your goal as a software developer is to identify the 20% of your code that can increase your programs overall performance. You can inline and otherwise tweak your function until the cows come home, but it's a wasted effort unless you're focusing on the right functions.
Title: Re: point in triangle
Post by: pkohut on February 06, 2010, 03:51:13 AM
Code: [Select]
...
    TRYRETVOID (pBlockTableRecord.openStatus());
    AcDbBlockTableRecordIterator* pIter;
    TRYRETVOID (pBlockTableRecord->newIterator( pIter ))
   
    for (pIter->start();!pIter->done();pIter->step())
    {
      AcDbObjectId entId;
      TRYRETVOID(pIter->getEntityId(entId));
      AcDbEntityPointer pEnt(entId,AcDb::kForRead);
...

Watch out, you leaked a pIter!

Noticed it while trying figure out an alternative error handler.
Title: Re: point in triangle
Post by: It's Alive! on February 06, 2010, 03:59:24 AM
Code: [Select]
...
    TRYRETVOID (pBlockTableRecord.openStatus());
    AcDbBlockTableRecordIterator* pIter;
    TRYRETVOID (pBlockTableRecord->newIterator( pIter ))
   
    for (pIter->start();!pIter->done();pIter->step())
    {
      AcDbObjectId entId;
      TRYRETVOID(pIter->getEntityId(entId));
      AcDbEntityPointer pEnt(entId,AcDb::kForRead);
...

Watch out, you leaked a pIter!

Noticed it while trying figure out an alternative error handler.

Doh, I had delete at the bottom, so it would have leaked if this line failed   :-o :-o
TRYRETVOID(pIter->getEntityId(entId));
Title: Re: point in triangle
Post by: It's Alive! on February 06, 2010, 07:09:37 AM
 added the line macro :laugh:

Code: [Select]
void PrintErrorStatus(Acad::ErrorStatus es, LPCTSTR func)
{
  acutPrintf(_T("\nError: [%s]\nIn function %s"),
  acadErrorStatusText(es), func);
}

void PrintErrorStatus(Acad::ErrorStatus es, long line, LPCTSTR func)
{
  acutPrintf(_T("\nError: Line %ld [%s]\nIn function %s"),
    line, acadErrorStatusText(es), func);
}

//returns ES
#define TRYRETES(statement)    { Acad::ErrorStatus st = (statement);\
  if (st != Acad::eOk){\
  PrintErrorStatus(st,__LINE__,_T(__FUNCTION__));return st;}}\
  (void)0 //force ;

// returns void
#define TRYRETVOID(statement)  { Acad::ErrorStatus st = (statement);\
  if (st != Acad::eOk){\
  PrintErrorStatus(st,__LINE__,_T(__FUNCTION__));return;}}\
  (void)0 //force ;

// only prints es message
#define TRYMSG(statement)      { Acad::ErrorStatus st = (statement);\
  if (st != Acad::eOk){\
  PrintErrorStatus(st,__LINE__,_T(__FUNCTION__));}}\
  (void)0 //force ;
Title: Re: point in triangle
Post by: owenwengerd on February 07, 2010, 02:29:10 PM
It's interesting to watch this tracing macro conversation evolve.

I was recently looking at some code I wrote 10 years ago, where I had made an effort that looked very similar to what Daniel is doing, but I've long since abandoned the one size fits all approach in favor of more precise handling of error cases. If it's really an exception, I throw an exception; otherwise I handle it as an error by propagating it back to the caller after first cleaning up and emitting trace output. IMO nothing is gained by trying to compress all that work into a single macro: doing that just makes the code less readable and less maintainable, and the effort to compress error handling can quickly consume the programming process and divert the focus away from good code design.

I use a vararg trace function that evaluates to nothing in release builds and only emits code in debug builds. I can then use simple macros to send line, function, and state information to that function. The trace output function is then designed to emit debugger output and log output to a file or to the AutoCAD command line depending on registry settings.
Title: Re: point in triangle
Post by: pkohut on February 07, 2010, 05:00:26 PM
IMO nothing is gained by trying to compress all that work into a single macro: doing that just makes the code less readable and less maintainable, and the effort to compress error handling can quickly consume the programming process and divert the focus away from good code design.

Fully agree.  Handling error return codes is a big part of development and they should not be ignored, even if handling them makes the code "ugly".  What the macros do is allow the coder to type a little bit less, line things up nicely in the editor, loose the visual structure of the application that naturally occurs with block brackets, and create way to many exit points in functions (talking C++ here where GC is not an option).
Title: Re: point in triangle
Post by: It's Alive! on February 08, 2010, 06:47:07 AM
Thanks for the feedback guys, I will look into building some sort of trace output function.