TheSwamp

Code Red => AutoLISP (Vanilla / Visual) => Topic started by: Joe Burke on July 11, 2008, 09:17:22 AM

Title: Offset method question
Post by: Joe Burke on July 11, 2008, 09:17:22 AM
Developer help says the ActiveX Offset method offsets to the inside when the distance argument passed is negative. IOW, a "smaller" curve is the result. This seems to be true with object types such as an arc or a circle. But not true with a lightweight polyline object. In fact, the opposite seems to be true.

Here's some rough test code.

Code: [Select]
;; Test Offset method.
(defun c:TOF ( / d e obj)
  (princ "\nNegative value offsets inside. Positive offsets outside. ")
  (setq d (getdist "\nEnter offset distance: "))
  (setq e (car (entsel "\nSelect circle or arc or polyline :")))
  (setq obj (vlax-ename->vla-object e))
  (car (vlax-invoke obj 'Offset d))
)

Am I missing something here? If I enter a positive distance and select an arc or circle, the object is offset outside as expected. But if I enter a positive value and select a lwpline (closed or not) the pline is offset inside.

So it seems I have to test the object type to get the expected result? If pline, reverse the sign of the distance value? What?

I tested this in 2004, 2006 and 2008. All seem to behave the same.

TIA for any thoughts on this.
Title: Re: Offset method question
Post by: b.benn on July 11, 2008, 09:53:09 AM
With open polylines, there really is not an inside or outside.  I think the offest for a pline is based on leftside offset for positive numbers and rightside offset for negative numbers.  (Side is based on direction that the line was drawn.)

This is true for both open and closed plines.
Title: Re: Offset method question
Post by: ElpanovEvgeniy on July 11, 2008, 10:04:36 AM
Polylines direction indication (http://www.theswamp.org/index.php?topic=12220.msg151277#msg151277)
Title: Re: Offset method question
Post by: Joe Burke on July 11, 2008, 10:15:01 AM
With open polylines, there really is not an inside or outside.  I think the offest for a pline is based on leftside offset for positive numbers and rightside offset for negative numbers.  (Side is based on direction that the line was drawn.)

This is true for both open and closed plines.

Bruce,

If that's true, the function sucks. Assuming a pline has more than two vertices, it does not look like a line and the points are not collinear, then there should be an inside or outside option which works as expected with the Offset method.
Title: Re: Offset method question
Post by: Joe Burke on July 11, 2008, 10:37:07 AM
Besides, a closed pline should be a no-brainer assuming the offset distance is within bounds.
Title: Re: Offset method question
Post by: Chuck Gabriel on July 11, 2008, 10:38:58 AM
With open polylines, there really is not an inside or outside.  I think the offest for a pline is based on leftside offset for positive numbers and rightside offset for negative numbers.  (Side is based on direction that the line was drawn.)

This is true for both open and closed plines.

Bruce,

If that's true, the function sucks. Assuming a pline has more than two vertices, it does not look like a line and the points are not collinear, then there should be an inside or outside option which works as expected with the Offset method.

What if the (open) polyline has an S shaped curve in it?  How do you decide which way is inside and which is outside?
Title: Re: Offset method question
Post by: b.benn on July 11, 2008, 12:03:30 PM
With open polylines, there really is not an inside or outside.  I think the offest for a pline is based on leftside offset for positive numbers and rightside offset for negative numbers.  (Side is based on direction that the line was drawn.)

This is true for both open and closed plines.

Bruce,

If that's true, the function sucks. Assuming a pline has more than two vertices, it does not look like a line and the points are not collinear, then there should be an inside or outside option which works as expected with the Offset method.

Joe,

I think that we would all agree that there are many aspects of the built in functions that are not ideal.  Here is a very rough function based on your TOF and the functions of ElpanovEvgeniy that may get you what you need for closed polylines.  No error proofing; very little testing:

Code: [Select]

(defun c:TOF ( / d e obj)
  (princ "\nNegative value offsets inside. Positive offsets outside. ")
  (setq d (getdist "\nEnter offset distance: "))
  (setq e (car (entsel "\nSelect circle or arc or polyline :")))
  (if (= (cdr (assoc 0 (entget e))) "LWPOLYLINE")
  (if (lwcl e)
  (rlw e)
  )
  )
  (setq obj (vlax-ename->vla-object e))
  (car (vlax-invoke obj 'Offset d))
)


(defun rlw (lw / E LW X1 X2 X3 X4 X5 X6)
    (setq e (entget lw))

      (foreach a1 e
        (cond
          ((= (car a1) 10) (setq x2 (cons a1 x2)))
          ((= (car a1) 40) (setq x4 (cons (cons 41 (cdr a1)) x4)))
          ((= (car a1) 41) (setq x3 (cons (cons 40 (cdr a1)) x3)))
          ((= (car a1) 42) (setq x5 (cons (cons 42 (- (cdr a1))) x5)))
          ((= (car a1) 210) (setq x6 (cons a1 x6)))
          (t (setq x1 (cons a1 x1)))
        ) ;_  cond
      ) ;_  foreach
      (entmod
        (append
          (reverse x1)
          (append
            (apply
              (function append)
              (apply
                (function mapcar)
                (cons
                  'list
                  (list x2
                        (cdr (reverse (cons (car x3) (reverse x3))))
                        (cdr (reverse (cons (car x4) (reverse x4))))
                        (cdr (reverse (cons (car x5) (reverse x5))))
                  ) ;_  list
                ) ;_  cons
              ) ;_  apply
            ) ;_  apply
            x6
          ) ;_  append
        ) ;_  append
      ) ;_  entmod
      (entupd lw)


) ;_  defun

(defun lwcl (ent / LW LST MAXP MINP)
  (setq lw (vlax-ename->vla-object ent))
  (vla-GetBoundingBox lw 'MinP 'MaxP)
  (setq
    minp (vlax-safearray->list minp)
    MaxP (vlax-safearray->list MaxP)
    lst  (mapcar
           (function
             (lambda (x)
               (vlax-curve-getParamAtPoint
                 lw
                 (vlax-curve-getClosestPointTo lw x)
               ) ;_  vlax-curve-getParamAtPoint
             ) ;_  lambda
           ) ;_  function
           (list minp
                 (list (car minp) (cadr MaxP))
                 MaxP
                 (list (car MaxP) (cadr minp))
           ) ;_  list
         ) ;_  mapcar
  ) ;_  setq
  (if (or
        (<= (car lst) (cadr lst) (caddr lst) (cadddr lst))
        (<= (cadr lst) (caddr lst) (cadddr lst) (car lst))
        (<= (caddr lst) (cadddr lst) (car lst) (cadr lst))
        (<= (cadddr lst) (car lst) (cadr lst) (caddr lst))
      ) ;_  or
    t
  ) ;_  if
) ;_  defun



By the way, I think that I stated the direction incorrectly in my previous post - positive is a right side offset, negative is a left side offset.
Title: Re: Offset method question
Post by: It's Alive! on July 11, 2008, 12:39:01 PM
GetOffsetCurves(distance) in .NET produces similar results. I need to test if it’s an AcDbPolyline, if so change the sign of distance. 
Here is the code if anyone wants to test.

Code: [Select]
namespace ExecMethod
{
  public class Commands
  {

    [CommandMethod("doit")]
    static public void doit()
    {
      Editor ed = AcAp.Application.DocumentManager.MdiActiveDocument.Editor;
      Database db = HostApplicationServices.WorkingDatabase;
      try
      {
        PromptEntityOptions peo = new PromptEntityOptions("\nGet Entity: ");
        peo.AllowNone = false;
        PromptEntityResult per = ed.GetEntity(peo);

        if (per.Status == PromptStatus.OK)
        {
          PromptDistanceOptions pdo = new PromptDistanceOptions("\nGet Distance: ");
          PromptDoubleResult pdr = ed.GetDistance(pdo);

          if (pdr.Status == PromptStatus.OK)
          {
            Offset(db, per.ObjectId, pdr.Value);
          }

        }
      }
      catch (System.Exception ex)
      {
        ed.WriteMessage(ex.Message);
        ed.WriteMessage(ex.StackTrace);
      }
    }

    public static void Offset(Database db, ObjectId id, double distance)
    {

      AcDb.TransactionManager manager = db.TransactionManager;

      using (Transaction transaction = manager.StartTransaction())
      {

        BlockTableRecord record = (BlockTableRecord)transaction.GetObject
                       (SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForWrite, false);

        Curve curve = transaction.GetObject(id, OpenMode.ForRead, false) as Curve;

       
        if (curve.GetRXClass().Name == "AcDbPolyline") //[color=red]<---------------------------  Test[/color]       
        {
          distance = -distance;
        }

        DBObjectCollection offsetCurves = curve.GetOffsetCurves(distance);

        foreach (Entity obj in offsetCurves)
        {
          record.AppendEntity(obj);
          transaction.AddNewlyCreatedDBObject(obj, true);
        }

        transaction.Commit();
      }
    }
  }
}
Title: Re: Offset method question
Post by: gile on July 11, 2008, 04:35:31 PM
Hi Joe,

You can have a look at this routine (http://www.theswamp.org/index.php?topic=21933.msg266096#msg266096), It offsets pline segments.
as in the Offet command, the user pick a point to choose the offset side. To determine if vla-offset argument have to be positive or negative, I evaluate if the specified point is on the right or on the left of the nearest segment using a little routine :

Code: [Select]
(defun clockwise-p (p1 p2 p3) (< (sin (- (angle p1 p3) (angle p1 p2))) -1e-14))

Without selecting a point, I think a positive value offsets to the right of the first segment, a negative to the left.

Sorry but it I can't have a goog display of the picture (missing segments) please, right click on it to get a better display.

Title: Re: Offset method question
Post by: gile on July 11, 2008, 05:10:44 PM
Better, here're the test routines and a test file

Code: [Select]
(defun c:test+ ()
  (mapcar (function (lambda (x) (vla-put-color x 3)))
  (vlax-invoke
    (vlax-ename->vla-object (car (entsel)))
    'Offset
    +50
  )
  )
  (princ)
)


(defun c:test- ()
  (mapcar (function (lambda (x) (vla-put-color x 1)))
  (vlax-invoke
    (vlax-ename->vla-object (car (entsel)))
    'Offset
    -50
  )
  )
  (princ)
)
Title: Re: Offset method question
Post by: Joe Burke on July 12, 2008, 09:25:05 AM
Thanks for all replies.  :-)

Within the larger routine I'm working on, all objects which might be offset are closed using this test function.

 
Code: [Select]
  ;; The reason for doing it this way, rather than test with
  ;; vlax-curve-isClosed, is to allow a pline which is not closed,
  ;; but has equal first and last points, to pass the test.
  (defun FirstLastPts (obj / p1 p2)
    (setq p1 (vlax-curve-getPointAtParam obj (vlax-curve-getStartParam obj)))
    (setq p2 (vlax-curve-getPointAtParam obj (vlax-curve-getEndParam obj)))
    (equal p1 p2 1e-10)
  )

I can deal with how the Offset method works with plines. It just doesn't feel right in some cases.


Title: Re: Offset method question
Post by: Joe Burke on July 20, 2008, 08:10:19 AM
gile,

Just wanted to say thanks for your examples. I should have paid more attention when you posted.

Eventually I came to the same conclusion. It's the direction of travel, clockwise or counter-clockwise, which deterrmines which way a pline is offset using the offset method.

IMO the function should be smarter as it applies to closed plines.

I ended up using (command "._offset" ...). Sigh...
Title: Re: Offset method question
Post by: gile on July 20, 2008, 09:13:50 AM
You're welcome Joe.

I just see the link to a routine I purposed as example was missing in my first reply.
The code is here (http://www.theswamp.org/index.php?topic=21933.msg266096#msg266096). It uses some vector calculus to translate the picked point coordinates (UCS coordinates) to its projection on the pline plane according to the current view (seems to work in 3d).
Title: Re: Offset method question
Post by: Joe Burke on July 20, 2008, 10:39:15 AM
gile,

That's interesting. I'll have to study it.

The routine I'm working on is 2D only. What it does is trim/clip all objects on active layers to a selected circle or closed pline (any 2D type), ellipse, or spline. IOW, break objects at the clipping object boundary. Keep everything inside and delete everything outside. Similar to the ExpressTools cookie cutter thing, but it works with blocks and deletes the outside objects.

I thought the routine would be fairly easy. Not so once I got into it. It's working well now, but still more tweaking to do...

Has anyone else tried to do similar or know of a routine which does it?
Title: Re: Offset method question
Post by: Spike Wilbury on July 20, 2008, 10:47:56 AM
Hola Jose (Joe)

Again, bothering here in this place....  :ugly:

Not sure.... but
The only one that I remember is the one that Bill Kramer did, many years ago, but do not recall if it was also for blocks.
Title: Re: Offset method question
Post by: Joe Burke on July 20, 2008, 10:53:54 AM
Hola Jose (Joe)

Again, bothering here in this place....  :ugly:

Not sure.... but
The only one that I remember is the one that Bill Kramer did, many years ago, but do not recall if it was also for blocks.

Hola Luis,

Do you recall the name of Bill's routine?
Title: Re: Offset method question
Post by: Spike Wilbury on July 20, 2008, 10:58:00 AM
Was very easy to find.... here is the link:

www.cad.pl/lisp/detail.lsp

Was published on the cadence magazine.
Title: Re: Offset method question
Post by: Joe Burke on July 20, 2008, 11:38:36 AM
Was very easy to find.... here is the link:

www.cad.pl/lisp/detail.lsp

Was published on the cadence magazine.

Thanks, Luis.

I tried the routine, but I probably don't understand what it is intended to do.

As I see the task, just select a closed object and the routine does the rest.
Title: Re: Offset method question
Post by: Spike Wilbury on July 20, 2008, 11:55:09 AM
Was very easy to find.... here is the link:

www.cad.pl/lisp/detail.lsp

Was published on the cadence magazine.

Thanks, Luis.

I tried the routine, but I probably don't understand what it is intended to do.

As I see the task, just select a closed object and the routine does the rest.

I see, that routine came to my mind, it was something closer, of what you are trying to do.

BTW, have you tried to convert the selection to an annon or temp block and use something similar like the command clipit? but, I guess you want to trim the objects
sorry I have not done or needed something like this, so I can be of more help.
Title: Re: Offset method question
Post by: Joe Burke on July 22, 2008, 07:50:40 AM
Hola Luis,

Right, I want to trim the objects to the clipping object. Something I'd normally do with xclip, either with a block or an xref.

The reason is a project is in the construction phase and we are receiving many requests for information (RFIs) from the general contractor. To answer these I often need to chop out a portion of a larger file, usually a plan, in order to generate a stand-alone document. Notes are added which only pertain to the RFI.

This also serves as a record keeping device. Plus the source/base files may be further modified without effecting the RFI document.