Logical error with equality test + Break command behavior with entity names

Logical error with equality test + Break command behavior with entity names

serag.hassouna
Advocate Advocate
1,171 Views
10 Replies
Message 1 of 11

Logical error with equality test + Break command behavior with entity names

serag.hassouna
Advocate
Advocate

I'm trying to debug a function ((tr-2ends)) , first of all, these screenshots describe what I want it to accomplish.


  • Raw drawingRaw drawing intended resultintended result

and here's the code for this function

;|otherelem: a small utility function that takes a list of 2 elements and an item, 
it returns the other item |; (defun otherelem (mylist myelem / ret) (foreach o mylist (if (not (equal myelem o)) (setq ret o)) );foreach (progn ret) );end defun [otherelem] ;tr-2ends: this function takes a curve, and 2 points that belong to it, then removes any segment
;that isn't located between those 2 points. ;| Be notified that the curve's entity name doesn't change after trimming it,
but if it's breaked using "Break" command, one of the 2 resulted curves
will take the older entity name and the other will take a new entity name |; (defun tr-2ends (ename pt1 pt2 / elist obj spt ept dir_s2e dir_p2p br_pts ptslist i j elem rement) (vl-load-com) ;load Activex support (setq elist (list ename)) ;list of all elements that will result from breaking the initial curve (setq obj (vlax-ename->vla-object ename)) (setq spt (vlax-curve-getstartpoint obj) ept (vlax-curve-getendpoint obj) );setq (setq dir_s2e (if (< (car spt) (car ept)) 1 -1) ;direction from start to end dir_p2p (if (< (car pt1) (car pt2)) 1 -1) ;direction from breaking point 1 to breaking point 2 br_pts (list pt1 pt2) );|setq dir (dir_s2e, dir_p2p) [specifies the direction of the curve,
from left to right or the opposite] |; (if (not (equal dir_s2e dir_p2p)) (setq br_pts (reverse br_pts)) );if ;(quit);debug line (setq i 0);increment of the loop that's responsible for breaking and removing (setq rement ename);initialize rement (setq elist (list rement));|elist is a list that contains both rement
and the other element resulted from "break" command |; (while (< i 2) (command "_Break" rement (nth i br_pts) (nth i br_pts)) (setq elist (append elist (list (entlast)))) (princ "\nelist: ");debug line (print elist);debug line ;get a list of start and end points of both curves (setq ptslist (mapcar (function (lambda (eobj / obj espt eept entpts dir_ent) (setq obj (vlax-ename->vla-object eobj)) (setq espt (vlax-curve-getstartpoint obj) eept (vlax-curve-getendpoint obj) );setq [of start & end points] (setq entpts (list espt eept)) (setq dir_ent (if (< (car espt) (car eept)) 1 -1));[determine direction] (if (not (equal dir_s2e dir_ent)) (setq entpts (reverse entpts)) );if (progn entpts);return entpts );lambda );function elist );mapcar ); setq ptslist ;get the reminder element and remove the other one (setq j 0) (princ "\nbreak point in use: ");debug line (print (nth i br_pts));debug line (princ "\npoints list: ");debug line (print ptslist);debug line (while (< j 2) (setq elem (nth j elist)) (princ "\ntest value:");debug line (print (equal (nth i br_pts) (nth 0 (nth j ptslist))));|debug line
[the test expression I talk about]|; (princ "\ncomparison point: ");debug line (print (nth 0 (nth j ptslist)));debug line (progn (if (equal (nth i br_pts) (nth 0 (nth j ptslist))) (if (= i 0) (progn (setq rement elem) (entdel (otherelem elist rement)) (print "elem in i=0");debug line );progn [then part of 2nd if], i=0 (progn (setq rement (otherelem elist elem) );setq (entdel elem) (print "elem in i = 1");debug line );progn [else part of 2nd if], i=1 );2nd if ;No else part for the 1st if );if [select the element that have the breaking point as its starting point] (setq j (1+ j)) );progn [of foreach] );end foreach (setq i (1+ i)) (setq elist (list rement));rewind elist );end while [of breaking & removing] (progn rement);what the function returns );end defun [tr-2ends]

____________

 

Sometimes it works fine with LWPOLYLINEs, especially when starting a fresh new drawing, but fails with POLYLINEs, SPLINEs, and LINEs.
Also, I doubt what I've written before (in the commented introduction to "tr-2ends") about what "Break" command do with the entity names of its resulted 2 objects.

imagine if AutoCAD assigns 2 new entity names for them, and the entity name of the old entity vanishes and is not assigned to anyone of them, how could I track those 2 object with just "entlast" that returns only the last "generated" entity name?!


In Short:

  1. The function doesn't always behave as intended, and when it doesn't do so, the test expression returns a seemingly incorrect result.
  2. What does "Break" command really do with the entity names with the old entity and the resulted 2 entities ?
0 Likes
Accepted solutions (1)
1,172 Views
10 Replies
Replies (10)
Message 2 of 11

john.uhden
Mentor
Mentor

Do you really want to break the apparent polyline into segments or to trim away the portions beyond the two points?

I would probably go about it a different way.  Rather than using (command) I would calculate the results and modify the the original. Maybe it would require 48 lines of code vs. 28, but it would work every time.

I am working on a similar situation at work right now.  I always create a plist of the polyline first, in the format ((bulge x y)(bulge x y) etc.) and do all my calculations on the vertices, removing or adding or changing as the case may be, utimately using (vlax-put object 'Coordinates (apply 'append (mapcar 'cdr plist))) obviously followed by setting the bulges.

John F. Uhden

Message 3 of 11

Kent1Cooper
Consultant
Consultant

I think I would go about this in a different way.  It looks like you're Breaking the object at the same place, i.e. not removing anything from it, and then trying to figure out which part to remove.  Try finding the distances from the start of the object to the 2 points, and if pt1 is closer to the start, Break the object from the start to pt1, and again from the end to pt2.  Or the other pairing if pt2 is closer to the start.  Then each Break will only take an end off, and won't leave two  objects.

 

When you do that with a Line, Spline, Arc, Ellipse, or LWPolyline, the remaining piece keeps the entity name, so you can just Break the same again at the other end.  It's only with a "heavy" Polyline that the left-over piece gets a new entity name.  So you would need to check whether the object-to-be-shortened is one of those, and if so, put in a

 

(setq ename (entlast))

 

after the first Break, so that the second Break will take the other end off the right thing.

 

[But if you keep your approach, and since you mention equality problems in the Subject line, you have some (equal) functions in there that should include a fuzz factor.  Whenever comparing numerical values or points with (equal), depending on how they were arrived at, there can be tiny way-down-in-decimal-places differences that can make them not truly (equal), but a fuzz factor like 1e-6 or something will usually let them be recognized as close enough.]

Kent Cooper, AIA
Message 4 of 11

serag.hassouna
Advocate
Advocate

What I want is to trim away the portions beyond the 2 points, however, your suggestion about the modification of polylines is insightful, even though I'm trying to reach a general solution that works with lines, splines, heavy and lightweight polylines, and arcs.

0 Likes
Message 5 of 11

serag.hassouna
Advocate
Advocate

The equality problem is solved by considering a fuzz factor (of 1e-5) replacing the line

(equal (nth i br_pts) (nth 0 (nth j ptslist)))

with a custom small function.
The good news is that ((tr-2ends)) really works fine for almost all types of elements {lines, lwpolylines, splines, arcs},
the only problem is with "heavy" polylines, as we have 2 new entity names, the one from "entlast" is the rightmost polyline, so what is remaining is to get the other entity name, assign them to a list with the right arrangement, then proceed with the same approach as before,

taking in consideration to keep this list updated & always with the right arrangement.
["Break" is needed twice and with the 2 directions, not just with the rightmost element only]
For doing so, I do have in mind the "inefficient" way of stepping forwardly through the elements within the drawing until there's a match with the result of "entlast", then just picking up the one element before it, in other words,
the question is, Can we directly step backward within the drawing database ?

0 Likes
Message 6 of 11

Kent1Cooper
Consultant
Consultant

@serag.hassouna wrote:

....
the only problem is with "heavy" polylines, as we have 2 new entity names, the one from "entlast" is the rightmost polyline, so what is remaining is to get the other entity name.... Can we directly step backward within the drawing database ?


 

I still don't understand why you're Breaking at one point, instead of just Breaking off the whole end as in my earlier Reply, so you don't have two new entity names to deal with at all.

But if you have a legitimate reason to want to find the second-to-last entity in the drawing, you can do that by taking advantage of the fact that (entdel) applied to an entity name removes  it, and then (entdel) again on the same entity name brings it back!

 

(setq elast (entlast))

(entdel elast); remove it [temporarily], so the 2nd-to-last one will now be (entlast):

(setq 2ndtolast (entlast)); save the 2nd-to-last one to a variable

(entdel elast); bring the temporarily-removed one back

 

Then proceed, with the '2ndtolast' variable holding the entity name of the 2nd-to-last object in the drawing.

 

HOWEVER, it seems risky to me to work on the assumption that '...the one from "entlast" is the rightmost...', which depends on that object always having been drawn in the same direction.  Especially if you're expanding to work with other entity types, I doubt very much that you can count on that.

Kent Cooper, AIA
0 Likes
Message 7 of 11

Kent1Cooper
Consultant
Consultant
Accepted solution

@Kent1Cooper wrote:

....  Try finding the distances from the start of the object to the 2 points, and if pt1 is closer to the start, Break the object from the start to pt1, and again from the end to pt2.  Or the other pairing if pt2 is closer to the start.  Then each Break will only take an end off, and won't leave two  objects.

 

... with a "heavy" Polyline ... the left-over piece gets a new entity name.  So you would need to check whether the object-to-be-shortened is one of those, and if so, put in a

 

(setq ename (entlast))

 

after the first Break....


 

The approach suggested above is so simple, I don't understand why people are getting so convoluted about it!  Keeping a few elements from your original [and lightly tested, but on a variety of entity types], I think your entire original code window content in Message 1 can be replaced with just this:

 

;tr-2ends: this function takes a curve, and 2 points that belong to it, then removes any segment
;that isn't located between those 2 points.
(defun tr-2ends (ename pt1 pt2 / estart eend pt1first isHeavy)
  (vl-load-com)
  (setq
    estart (vlax-curve-getStartPoint ename)
    eend (vlax-curve-getEndPoint ename)
    pt1first (< (distance estart pt1) (distance estart pt2)); pt1 closer to start?
    isHeavy (member '(0 . "POLYLINE") (entget ename))
); setq (command "_.break" ename "_none" estart "_none" (if pt1first pt1 pt2)) (if isHeavy (setq ename (entlast))); because of new entity name (command "_.break" ename "_none" eend "_none" (if pt1first pt2 pt1)) (princ) );end defun [tr-2ends]

 

No need for fuzz factors in (equal) functions, or finding the next-to-last object, or calculating bulge factors, or any of those shenanigans.

 

[EDIT:  Adjusted the accounting for a "heavy" Polyline from what was originally posted.]

Kent Cooper, AIA
Message 8 of 11

dlanorh
Advisor
Advisor

Beaten to it, but my 2 cents anyway. It's almost the same but accounts for points in any order. Robot tongue

 

(vl-load-com) ;load Activex support

(defun tr-2ends (ename pt1 pt2 / ent_lst obj spt ept b1 b2)
  
  (setq ent_lst (entget ename)
        e_type (cdr (assoc 0 ent_lst))
        obj (vlax-ename->vla-object ename)
        spt (vlax-curve-getstartpoint obj)
        ept (vlax-curve-getendpoint obj)
        b1 (vlax-curve-getparamatpoint obj pt1)
        b2 (vlax-curve-getparamatpoint obj pt2)
  );setq
  (if (> b2 b1)
    (progn
      (command "_break" ename pt2 ept)
      (if (= e_type "POLYLINE")
        (setq ename (entlast))
      )  
      (command "_break" ename pt1 spt)
    )
    (progn
      (command "_break" ename pt1 ept)
      (if (= e_type "POLYLINE")
        (setq ename (entlast))
      )  
      (command "_break" ename pt2 spt)
    )
  )
)  

(defun c:test ( / ent p1 p2)
  (setq ent (car (entsel "select line object : "))
        p1 (getpoint "select break point 1 : ")
        p2 (getpoint "select breakpoint 2 : ")
  )
  (tr-2ends ent p1 p2)
)  

I am not one of the robots you're looking for

Message 9 of 11

Kent1Cooper
Consultant
Consultant

@dlanorh wrote:

.... It's almost the same but accounts for points in any order.


 

So does mine -- am I missing something?

Kent Cooper, AIA
0 Likes
Message 10 of 11

dlanorh
Advisor
Advisor

No you are correct. I was just having a senior moment when editing my post. Robot Embarassed

I am not one of the robots you're looking for

0 Likes
Message 11 of 11

serag.hassouna
Advocate
Advocate

Well, my reasons for breaking at the same point is

  1. To experience the effect of "Break" command on the entity names of the old element and the new element[s].
  2. An assumption that using "Break" as you've suggested can result into "2" curves, one of them is so small and is tightly related to the fuzz issue that happened in the first place,
    one of the trials on the new version did really produce that

one result of the trials on the new version (tr-2ends2)one result of the trials on the new version (tr-2ends2)

However, this assumption is less probable to happen.

Any way, your last response to me and the response of dlanorh has helped me to get this "smaller" version of tr-2ends.

Here it is:-

;|tr-2ends2: does the same as tr-2ends but with the approach suggested by Mr.Kent Cooper
& with the hint from dlanorh to use the parameter at breaking points as an arrangement criteria|;
;it returns the entity name of the remaining part of the curve.
(defun tr-2ends2 (ename pt1 pt2 / isheavy obj spt ept b1 b2 br_pts ret)
(vl-load-com) ;load Activex support

(setq isheavy (eq "POLYLINE" (cdr (assoc 0 (entget ename)))))

(setq obj (vlax-ename->vla-object ename))
(setq
spt (vlax-curve-getstartpoint obj)
ept (vlax-curve-getendpoint obj)
);setq

;*Arrange points*
(setq
b1 (vlax-curve-getParamAtPoint obj pt1) ;Parameter from start to breaking point no.1
b2 (vlax-curve-getParamAtPoint obj pt2) ;Parameter from start point to breaking point no.2
br_pts (list pt1 pt2)
);setq
(if (< b2 b1)
(setq br_pts (reverse br_pts))
);if

;*Trim the curve and return the entity name of the remaining part*
(if
isheavy
(progn
(command "Break" ename (nth 0 br_pts) spt)
(command "Break" (entlast) (nth 1 br_pts) ept)
(setq ret (entlast))
);progn, then part, [for heavy polyline]
(progn
(command "Break" ename (nth 0 br_pts) spt)
(command "Break" ename (nth 1 br_pts) ept)
(setq ret ename)
);progn, else part, [for other types]
);if

(progn ret);what the function returns
);end defun [tr-2ends2]

Also, not using the distance between points as a criteria for their arrangement is duo to the effect of the y coordinate,
if the 1st breaking point is so lower or higher than the 2nd breaking point, that will make it more distant away, and the program will not correctly recognize their arrangement.