ziele_o2k Posted September 9, 2023 Posted September 9, 2023 (edited) 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: Generate vertical xline every 1 m (1 cad unit) Get intersection point between each xline and road profile save it to list 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 September 9, 2023 by ziele_o2k Quote
marko_ribar Posted September 9, 2023 Posted September 9, 2023 (edited) 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 September 9, 2023 by marko_ribar 1 Quote
Steven P Posted September 9, 2023 Posted September 9, 2023 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 Quote
ziele_o2k Posted September 9, 2023 Author Posted September 9, 2023 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> Quote
ziele_o2k Posted September 9, 2023 Author Posted September 9, 2023 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> Quote
ziele_o2k Posted September 9, 2023 Author Posted September 9, 2023 (edited) What i'm doing in new approach: get coordinates of all road lwpline points create new lwpline where all coordinate y is 0 and x is from road lwpline get list of params at each division distance from horizontal lwpline 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 September 9, 2023 by ziele_o2k Quote
SanganakSakha Posted September 12, 2023 Posted September 12, 2023 (edited) 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 September 12, 2023 by SanganakSakha 1 Quote
Steven P Posted September 12, 2023 Posted September 12, 2023 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 Quote
ronjonp Posted September 12, 2023 Posted September 12, 2023 (edited) 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 September 12, 2023 by ronjonp Quote
SLW210 Posted September 13, 2023 Posted September 13, 2023 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) Quote
Recommended Posts
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.