Jump to content

Need idea how to get y coordinate of pline at given x


ziele_o2k

Recommended Posts

Hey!

It's have been a while sice my last post here :)

I would like to speed up my routine and I don't have any new ideas..

Problem description: i have road profile which is pline with arcs and line segments. I would like to get levels (y-coordinate) every 1 m.

My current solution:

  1. Generate vertical xline every 1 m (1 cad unit)
  2. Get intersection point between each xline and road profile
  3. save it to list
  4. delete xline.

This works just fine but it is very slow. Maybe someone can help me with this.

Examplme dwg file with road profile in attachment.

 

my current solution (simplified):

(defun c:get_pline_data (/ variant_to_list pline_ent pline_obj division_distance road_start_x road_end_x cur_x xline_obj intersection point res_y) 
    (defun variant_to_list (var) 
        (vlax-SafeArray->List (vlax-Variant-Value var))
    )
    (setq pline_ent (car (entsel "\nSelect pline")))
    (setq pline_obj (vlax-ename->vla-object pline_ent))
    (setq division_distance 50) ; should be 1 unit but for this example it is set to 50
    (setq road_start_x (car (vlax-curve-getstartpoint pline_ent)))
    (setq road_end_x (car (vlax-curve-getendpoint pline_ent)))
    (setq cur_x road_start_x)
    (while (<= cur_x road_end_x) 
        (setq xline_obj (cd:ACX_AddXline (cd:ACX_ASpace) (list cur_x 0) (/ pi 2)))
        (setq intersection (vla-IntersectWith pline_obj xline_obj acextendnone))
        (vla-delete xline_obj)
        (setq point (variant_to_list intersection))
        (setq res_y (cons (cadr point) res_y))
        (setq cur_x (+ cur_x division_distance))
    )
    (princ)
)


;subroutines
(defun cd:ACX_AddXline (Space Ps Pe) 
    (vla-AddXline Space 
                  (vlax-3d-point (trans Ps 1 0))
                  (vlax-3d-point 
                      (cond 
                          ((numberp Pe)
                           (trans (polar Ps Pe 1) 1 0)
                          )
                          ((listp Pe)
                           (trans (list (car Pe) (cadr Pe) (caddr Ps)) 1 0)
                          )
                      )
                  )
    )
)
(defun cd:ACX_ASpace () 
    (if (= (getvar "CVPORT") 1) 
        (vla-item (cd:ACX_Blocks) "*Paper_Space")
        (cd:ACX_Model)
    )
)
(defun cd:ACX_Model () 
    (or 
        *cd-ModelSpace*
        (setq *cd-ModelSpace* (vla-get-ModelSpace (cd:ACX_ADoc)))
    )
    *cd-ModelSpace*
)
(defun cd:ACX_Blocks ()
  (or
    *cd-Blocks*
    (setq *cd-Blocks* (vla-get-blocks (cd:ACX_ADoc)))
  )
  *cd-Blocks*
)
(defun cd:ACX_ADoc ()
  (or
    *cd-ActiveDocument*
    (setq *cd-ActiveDocument*
      (vla-get-ActiveDocument (vlax-get-acad-object))
    )
  )
  *cd-ActiveDocument*
)

 

example_pline.dwg

Edited by ziele_o2k
Link to comment
Share on other sites

This will be enough and should work, but not tested...

 

(defun c:get_pline_data (/ pline_ent division_distance road_start_x road_end_x cur_x point ) ;;; res_y - global variable
  (setq pline_ent (car (entsel "\nSelect pline")))
  (setq division_distance 50) ; should be 1 unit but for this example it is set to 50
  (setq road_start_x (car (setq point (vlax-curve-getstartpoint pline_ent))))
  (setq road_end_x (car (vlax-curve-getendpoint pline_ent)))
  (setq cur_x road_start_x)
  (while (<= cur_x road_end_x) 
    (setq point (vlax-curve-getclosestpointtoprojection pline_ent point (list 0.0 1.0 0.0)))
    (setq res_y (cons (cadr point) res_y))
    (setq cur_x (+ cur_x division_distance))
    (setq point (mapcar '+ (list division_distance 0.0 0.0) point))
  )
  (princ)
)

 

HTH.

M.R.

Edited by marko_ribar
  • Like 1
Link to comment
Share on other sites

This might help:

 

(vlax-curve-getPointAtDist curve-obj dist)

 

Work out the start point and end point of your polyline (in case your initial 0m distance isn't the polyline start point)

Work out the distance along the polyline - so you can loop that many times.

Work out if your start point is the start of end of the polyline. for example, 1m from the 'start' of the polyline might be 50m from where you want it to start. Reverse the polyline, create a temporary or do some sums to work out the true polyline distance

Loop along the polyline incrementing dist by your distance each repeat

 

http://docs.autodesk.com/ACD/2014/ENU/index.html?url=files/GUID-F41FB58A-4645-404E-98B7-D4978A9A790B.htm,topicNumber=d30e620554

Link to comment
Share on other sites

35 minutes ago, marko_ribar said:

This will be enough and should work, but not tested...

 

(defun c:get_pline_data (/ pline_ent division_distance road_start_x road_end_x cur_x point ) ;;; res_y - global variable
  (setq pline_ent (car (entsel "\nSelect pline")))
  (setq division_distance 50) ; should be 1 unit but for this example it is set to 50
  (setq road_start_x (car (setq point (vlax-curve-getstartpoint pline_ent))))
  (setq road_end_x (car (vlax-curve-getendpoint pline_ent)))
  (setq cur_x road_start_x)
  (while (<= cur_x road_end_x) 
    (setq point (vlax-curve-getclosestpointtoprojection pline_ent point (list 0.0 1.0 0.0)))
    (setq res_y (cons (cadr point) res_y))
    (setq cur_x (+ cur_x division_distance))
    (setq point (mapcar '+ (list division_distance 0.0 0.0) point))
  )
  (princ)
)

 

HTH.

M.R.

DIVISION DISTANCE 50:

Elapsed milliseconds / relative speed for 4 iteration(s):
    (GET_PLINE_DATA PLINE_ENT DIVISION_D...).....1204 / 2.96 <fastest>
    (GET_PLINE_DATA_MARCO PLINE_ENT DIVISION_...).....3562 / 1 <slowest>

 

DIVISION DISTANCE 5:

Elapsed milliseconds / relative speed for 1 iteration(s):
    (GET_PLINE_DATA PLINE_ENT DIVISION_D...).....3125 / 3.05 <fastest>
    (GET_PLINE_DATA_MARCO PLINE_ENT DIVI...).....9546 / 1 <slowest>

 

 

Link to comment
Share on other sites

marko_ribar's code give me idea for new approach. After quick benchmark I have:

Elapsed milliseconds / relative speed for 16 iteration(s):

    (NEW_APPROACH PLINE_ENT DIVISION_DIS...).....1047 / 8.51 <fastest>
    (GET_PLINE_DATA_ENTDEL PLINE_ENT DIV...).....4219 / 2.11
    (GET_PLINE_DATA PLINE_ENT DIVISION_D...).....4781 / 1.86
    (GET_PLINE_DATA_MARCO PLINE_ENT DIVI...).....8906 / 1 <slowest>

 

comparsion to my first approach:

Elapsed milliseconds / relative speed for 16 iteration(s):
    (NEW_APPROACH PLINE_ENT DIVISION_DIS...).....1031 / 2.8 <fastest>
    (GET_PLINE_DATA PLINE_ENT DIVISION_D...).....2890 / 1 <slowest>

Link to comment
Share on other sites

What i'm doing in new approach:

  1. get coordinates of all road lwpline points
  2. create new lwpline where all coordinate y is 0 and x is from road lwpline
  3. get list of params at each division distance from horizontal lwpline
  4. get road lwpline point at each param from step 3 

done :) 

 

code for my new approach:

(defun c:new_approach ( / pline_ent division_distance get_pline_coordinates pline_coordinates temp_pline pline_obj road_start_x road_end_x cur_x params 
                     res
                    ) 

    (defun get_pline_coordinates (enx) 
        (if (setq enx (member (assoc 10 enx) enx)) 
            (cons 
                (cdr (assoc 10 enx))
                (get_pline_coordinates (cdr enx))
            )
        )
    )
    (setq pline_ent (car (entsel "\nSelect pline")))
    (setq division_distance 50)
    (setq pline_coordinates (get_pline_coordinates (entget pline_ent)))
    (setq temp_pline (cd:ACX_AddLWPolyline 
                         (cd:ACX_ASpace)
                         (mapcar 
                             (function 
                                 (lambda (%) 
                                     (list (car %) 0)
                                 )
                             )
                             pline_coordinates
                         )
                         nil
                     )
    )
    (setq pline_obj (vlax-ename->vla-object pline_ent))
    (setq road_start_x (car (vlax-curve-getstartpoint pline_ent)))
    (setq road_end_x (car (vlax-curve-getendpoint pline_ent)))
    (setq cur_x road_start_x)
    (while (<= cur_x road_end_x) 
        (setq params (cons (vlax-curve-getparamatdist temp_pline cur_x) params))
        (setq cur_x (+ cur_x division_distance))
    )
    (vla-delete temp_pline)
    (setq res (mapcar 
                  (function 
                      (lambda (%) 
                          (cadr (vlax-curve-getPointAtParam pline_obj %))
                      )
                  )
                  params
              )
    )
    (princ)
)

;subroutines

(defun cd:ACX_AddLWPolyline (Space Pts Closed / obj) 
    (setq Pts (apply 
                  (quote append)
                  (mapcar 
                      (function 
                          (lambda (%) 
                              (list (car %) (cadr %))
                          )
                      )
                      (mapcar 
                          (function 
                              (lambda (%) 
                                  (trans % 1 (trans '(0 0 1) 1 0 T))
                              )
                          )
                          Pts
                      )
                  )
              )
    )
    (setq obj (vla-AddLightweightPolyline Space 
                                          (vlax-make-variant 
                                              (vlax-safearray-fill 
                                                  (vlax-make-safearray vlax-vbdouble (cons 0 (1- (length Pts))))
                                                  Pts
                                              )
                                          )
              )
    )
    (if Closed (vla-put-closed obj Closed))
    obj
)
(defun cd:ACX_ASpace () 
    (if (= (getvar "CVPORT") 1) 
        (vla-item (cd:ACX_Blocks) "*Paper_Space")
        (cd:ACX_Model)
    )
)
(defun cd:ACX_Model () 
    (or 
        *cd-ModelSpace*
        (setq *cd-ModelSpace* (vla-get-ModelSpace (cd:ACX_ADoc)))
    )
    *cd-ModelSpace*
)
(defun cd:ACX_Blocks ()
  (or
    *cd-Blocks*
    (setq *cd-Blocks* (vla-get-blocks (cd:ACX_ADoc)))
  )
  *cd-Blocks*
)
(defun cd:ACX_ADoc ()
  (or
    *cd-ActiveDocument*
    (setq *cd-ActiveDocument*
      (vla-get-ActiveDocument (vlax-get-acad-object))
    )
  )
  *cd-ActiveDocument*
)

 

Edited by ziele_o2k
Link to comment
Share on other sites

Hi  ziele_o2k,

 

I am not a Civil engineer but this was an interesting problems with equally innovative solutions. Your idea of using a 'flat' pline to find parameters was also interesting.

 

I an not aware of the methodology you use to measure speed and can't really interpret the speed test results.
However, it was surprising to me that solution 1 was faster than solution 2. I was under impression that using AutoLISP functions is faster than creating (and deleting) geometry. Does this also mean using vlax-curve functions work slower than creating geometry? This needs to be verified.

 

Here is my function (modified New_Approach)

- It does away with creating / deleting temp geometry.

- Uses simple Trigonometry instead of curve parameters. It does not use any vlax-curve (or any other vl-) functions.

- No. of functions that are needed is reduced.

 

I expect it to further reduce the time because of these factors. You can test to see if this is correct.

 

By the way, this function works even when the road pline does not start at (0, 0). (You can try the function by moving the pline away from 0,0.)
And it is possible to modify this function to include arc segments in the pline. Could require some efforts though, in adding and identifying the arcs.

 

Hope you find it useful.

 

(defun c:pLineY	(/	       ntLst	     road_start
		 road_end      division_distance
		 kounter       BasePt0	     BasePt1
		 kounter       pline_coordinates
		 NewY	       NoOfVerices   OverReach
		 road_start_X  road_end_X    TotDst
		 cur_x	       yPline
		)
;;;;;;;;;;;----------------------
  (defun get_pline_coordinates (enx)
    (if	(setq enx (member (assoc 10 enx) enx))
      (cons
	(cdr (assoc 10 enx))
	(get_pline_coordinates (cdr enx))
      )
    )
  )
;;;;;;;;;;;;;;; ----------------
  (defun getY ()
;;;; Gets corresponding y on the pline
    (setq overReach nil)
    (while (and	(> totDst (- (car basePt1) road_start_X))
		(= overReach nil)
	   )
      (setq kounter (1+ kounter))
;;;
      (if (= kounter (1- noOfVertices))
	(setq overReach T)
	(setq basePt1 (nth (1+ kounter) pline_coordinates))
      )
;;;;      
    )
;;;;;;;;;;;
    (if	(= overReach nil)
      (progn
	(setq basePt0 (nth kounter pline_coordinates))
;;;;;;;;
;;;; y = (y2 - y1) / (x2 - x1) * (X -x1) + y1
	(setq yPline (+	(* (/ (- (cadr basePt1) (cadr basePt0))
			      (- (car basePt1) (car basePt0))
			   )
			   (- cur_x (car basePt0))
			)
			(cadr basePt0)
		     )
	)
      )
    )
;;;;;;
  )
;;;;;;;;;;;; ------------------
;;;;;; Execution starts here
  (setq	pline_coordinates
	 (get_pline_coordinates
	   (entget (car (entsel "\nSelect pline: ")))
	 )
  )

;;;;;;;;;;;; 
  (setq	road_start   (nth 0 pline_coordinates)
	road_start_X (car road_start)
	res	     (list road_start_X)
	road_end     (last pline_coordinates)
	road_end_X   (car road_end)
  )
;;;;
;;;   (setq division_distance (getreal "\nDivision diatance: "))
  (setq division_distance 50)
  (setq totDst division_distance)
  (setq cur_x (+ totDst road_start_X))
  (if (< totDst (- road_end_X road_start_X))
;;;; If dst > width of line exit
    (progn
;;;;;;;;;
      (setq kounter 0)
      (setq noOfVertices (length pline_coordinates))
      (while (< kounter (1- noOfVertices))
	(setq basePt0 (nth kounter pline_coordinates))
	(setq basePt1 (nth (1+ kounter) pline_coordinates))
	(setq newY (gety))
	(if newY
	  (setq res (cons newY res))
	)
	(if (< kounter (1- noOfVertices))
	  (setq	kounter	(1+ kounter)
		totDst	(+ division_distance totDst)
		cur_x	(+ division_distance cur_x)
	  )
	)
      )
    )
  )
;;;;;  (reverse res)
)

 

Edited by SanganakSakha
  • Like 1
Link to comment
Share on other sites

I wouldn't worry too much about the speed of a LISP - we are CAD designers and not computer programmers, the time parameter I want to know is: is it quicker than doing it manually, if it is then the LISP is good. You'd need to be doing a lot of calculations on a lot of entities to really make any difference, and whether that time saving is more than the time spent optimising the code I am not sure.

 

However. always good to see other code and different ways to do things

Link to comment
Share on other sites

On 9/9/2023 at 6:43 AM, ziele_o2k said:

DIVISION DISTANCE 50:

Elapsed milliseconds / relative speed for 4 iteration(s):
    (GET_PLINE_DATA PLINE_ENT DIVISION_D...).....1204 / 2.96 <fastest>
    (GET_PLINE_DATA_MARCO PLINE_ENT DIVISION_...).....3562 / 1 <slowest>

 

DIVISION DISTANCE 5:

Elapsed milliseconds / relative speed for 1 iteration(s):
    (GET_PLINE_DATA PLINE_ENT DIVISION_D...).....3125 / 3.05 <fastest>
    (GET_PLINE_DATA_MARCO PLINE_ENT DIVI...).....9546 / 1 <slowest>

 

 

What are you using to benchmark? In my mind their is no way your code that creates xlines to get intersection points is faster than using the projection? I tried to benchmark the code but yours errors out.

 

Got it to work .. interesting that yours is slightly faster! I also did not check that the results were equal :)
 

Benchmarking ....Elapsed milliseconds / relative speed for 2 iteration(s):

    (GET_PLINE_DATA PLINE_ENT)...........1125 / 1.12 <fastest>
    (GET_PLINE_DATA_MARCO PLINE_ENT).....1265 / 1 <slowest>

---- Benchmark Utility: In memory of Michael Puckett ----

 

Edited by ronjonp
Link to comment
Share on other sites

Here are some VBA solutions, they may at least provide a new method for you to follow.

 

I'll try to look later, I haven't tried any of the methods yet. Hopefully later today.

 

Is the speed important because you have very many of these?

 

Solved: Get coordinates every 1 meter from the polyline

 

Solved: How to get y coordinate on line at x value (VBA)

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...