Author Topic: Offset method question  (Read 6466 times)

0 Members and 1 Guest are viewing this topic.

Joe Burke

  • Guest
Offset method question
« 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.

b.benn

  • Mosquito
  • Posts: 12
Re: Offset method question
« Reply #1 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.

ElpanovEvgeniy

  • Water Moccasin
  • Posts: 1540
  • Moscow (Russia)
Re: Offset method question
« Reply #2 on: July 11, 2008, 10:04:36 AM »
蝸牛そろそろ登れ富士の山 /Kobayashi Issa/

Joe Burke

  • Guest
Re: Offset method question
« Reply #3 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.

Joe Burke

  • Guest
Re: Offset method question
« Reply #4 on: July 11, 2008, 10:37:07 AM »
Besides, a closed pline should be a no-brainer assuming the offset distance is within bounds.

Chuck Gabriel

  • Guest
Re: Offset method question
« Reply #5 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?

b.benn

  • Mosquito
  • Posts: 12
Re: Offset method question
« Reply #6 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.

It's Alive!

  • BricsCAD
  • Needs a day job
  • Posts: 6928
  • AKA Daniel
Re: Offset method question
« Reply #7 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();
      }
    }
  }
}

gile

  • Water Moccasin
  • Posts: 2219
  • Marseille, France
Re: Offset method question
« Reply #8 on: July 11, 2008, 04:35:31 PM »
Hi Joe,

You can have a look at this routine, 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.

« Last Edit: July 20, 2008, 09:03:58 AM by gile »
Speaking English as a French Frog

gile

  • Water Moccasin
  • Posts: 2219
  • Marseille, France
Re: Offset method question
« Reply #9 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)
)
Speaking English as a French Frog

Joe Burke

  • Guest
Re: Offset method question
« Reply #10 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.



Joe Burke

  • Guest
Re: Offset method question
« Reply #11 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...

gile

  • Water Moccasin
  • Posts: 2219
  • Marseille, France
Re: Offset method question
« Reply #12 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. 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).
Speaking English as a French Frog

Joe Burke

  • Guest
Re: Offset method question
« Reply #13 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?

Spike Wilbury

  • Guest
Re: Offset method question
« Reply #14 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.