Segmentation des ARCS

Transformation ARC en Segments Droits


Par abus de langage on parle de discrétisation, mais il n'est pas question d'usage de la langue sur ce site, je vous rassure on va parler programmation et non sémantique.
J'utiliserai le terme "segmentation" pour parler de cette transformation d'arcs en une succession de segment droits, ce choix de mot me semblant plus approprié.
Dans cet article je ne parle que des arcs, les polylignes avec segments courbes seront traitées aussi soyez rassurés mais je ferai un article à part.

On trouve, sur la toile mondiale, une grande abondance de routines qui proposent de faire cette transformation mais bien souvent elles se "contentent" de diviser l'arc original en créant une multitude de segments plus ou moins utiles.

J'ai voulu traiter la chose différemment en proposant  une autre approche que la simple "division" de l'arc.
Je suis parti de l'idée qu'on pouvait diviser l'arc en proposant trois méthodes, en partant de la valeur de la flèche, de la corde ou de la déviation angulaire.

De cette façon les arcs sont divisés en fonction de leur géométrie propre et non en fonction d'un nombre de segments écrit "en dur" dans le programme.
Ainsi les arcs "plats" seront moins divisés que ceux plus fortement courbés.

Une autre chose qui m'a semblée importante est que le nombre de divisions de l'arc soit un nombre entier, autrement dit que tous les segments obtenus soient de longueur identique, ce qui, toujours de mon point de vue, me semble plus approprié.


Visuel de la case DCL :
Gardant le principe d'apprentissage de ce site pour ce programme j'ai fait quelque chose avec une case de dialogue qui permet de choisir les options, quelles sont-elles ?

 

Dans le cas d'une utilisation plus quotidienne ces "fioritures" pourront être expurgées et les options appelées en ligne de commande.

Cela me semble clair mais je vais expliciter mes propositions de choix.

Méthode à utiliser :
Flèche : c'est la valeur de la FLÈCHE qui va déterminer la précision de la segmentation.
Corde : c'est la valeur de la CORDE qui va déterminer la précision de la segmentation.
Déviation : c'est la valeur de la DÉVIATION (angulaire) qui va déterminer la précision de la segmentation .

Trois méthodes de segmentation

Passons au programme ...


Dans un premier temps il va être question de lancer la case DCL.
Dès qu'elle est apparue, le premier souci s'est posé, je voulais qu'en fonction du choix de méthode (boutons radio), flèche, corde ou déviation le texte de la case de texte en face de la zone de saisie change suivant le choix.
Vous remarquerez que ce texte change effectivement en fonction de ce choix et c'est grâce à la fonction : ModifTextDCL que cet artifice est rendu possible.

Dans la case de saisie il faut donc rentrer une valeur, je n'ai mis qu'une case de texte pour les trois choix et c'est pour ça que je souhaitais une modification du texte accompagnant la saisie, elle sera stockée dans la variable "valeur" qui sera distribuée pour les cas de flèche, corde ou déviation en fonction du choix précédent sur la première ligne.
Nota : je travaille en grades mais il est aisé de modifier, je pense améliorer en tenant compte de l'unité angulaire courante du dessin en cours.

Résultat attendu : il faudra "allumer" le bouton radio de ce qu'on souhaite comme restitution graphique, polyligne 2D, 3D ou des lignes.

Pour finir avec la case de dialogue :
La case à cocher Effacer entité support, si elle est cochée, supprimera l'arc ou le cercle originel.

Les divers calculs de flèche, de corde, de déviation se font dans les conditions (cond) en fonction de l'analyse des valeurs renvoyées par les boutons radio de la case DCL.


La fonction qui permet de calculer les points de passage des segments est : segment-arc-div et elle attend trois arguments.
Ces trois arguments de la fonction de division de l'arc sont :
ent qui devra être un descriptif d'entité (ename)
div qui devra être un nombre entier (integer)
ChoixDessinDCL qui devra être un nombre entier (integer) 0 ou 1,
0
signifiant que c'est une polyligne 2D qui sera dessinée et le 1 une polyligne 3D.

Certains renseignements existent en natif dans les codes DXF sans avoir besoin de calculs et sont accessibles par la fonction (assoc) :
dans le cas de l' entité ARC :
Le rayon est dans le code 40
Le centre est dans le code 40

D'autres n'existent pas dans les codes DXF et ont besoin d'être calculés.
Le principe est de calculer la variable DIV qui va donner le nombre de divisions et pour ce faire je me base sur une longueur d'arc (calculée en fonction du choix de méthode). Le résultat est un nombre que je vais arrondir à l'entier supérieur.

Je vais donc travailler avec une valeur de division plus petite que celle demandée (flèche, corde ou déviation) pour ne restituer que des segments égaux.

C'est pour cette raison que la valeur demandée est un maximum à ne pas dépasser, je travaillerai en dessous de cette valeur.

Par exemple, la valeur MAXIMALE de flèche renseignée est de 0.10, il est tout à fait envisageable qu'avec cette flèche il ne soit pas possible de réaliser des segments égaux et que ce soit une valeur de 0.08 qui soit restituée lors du dessin des segments égaux, je travaille donc avec la valeur la plus grande mais toujours en dessous de celle demandée pour "égaliser" les segments.

Pour terminer j'ai ajouté une case (alert) qui récapitule les changements effectués.
Là aussi cette fioriture peut être supprimée pour une utilisation quotidienne.

J'ai écrit ça pour son côté didactique.

Voici le fichier DCL sauvegardé sous le nom Segment-Arc.DCL :

Segment_Arc : dialog {
label = " Segmentation des arcs ";
spacer;
: radio_row { label = "Méthode à utiliser ? ";
                        : radio_button { label = "Flèche"; key = "rb1"; }
                        : radio_button { label = "Corde"; key = "rb2"; }
                        : radio_button { label = "Déviation"; key = "rb3"; }
}
: row { : edit_box
             { key = "Edit1"; alignment = left; }
             : text { key = "Prompt"; label = "en attente du choix ..."; }
}
spacer;
: radio_row { label = "Résultat graphique attendu ? ";
                        : radio_button {label = "Polyligne 2D"; key = "rb4"; }
                        : radio_button {label = "Polyligne 3D"; key = "rb5"; }
                        : radio_button {label = "Lignes "; key = "rb6"; }
}
spacer;
: row { fixed_width = true;
            : toggle { key = "EffEnt"; label = "Effacer entité support ? ";}
}
: text { label = "++++++++++++++++++++++++++++++++++" ;}
: text { label = "écrit par Didier Aveline pour www.da-code.fr" ;}
ok_cancel ;
}

-------------------------------------------------------------------------------------------
Fonction pour dessiner une polyligne 3d depuis une liste de points :
(defun da:DessPoly3D (ptlist / pt)
; dessin de polyligne 3D depuis liste de sommets
(entmake (list
                  
'(0 . "POLYLINE") '(100 . "AcDbEntity")
                   '(100 . "AcDb3dPolyline") '(70 . 8)
                  )
)
(repeat (length ptlist)
   (setq pt (car ptlist)
             ptlist (cdr ptlist)
   )
   (entmake (list
                     '(0 . "VERTEX") '(100 . "AcDb3dPolylineVertex")
                      (cons 10 pt '(70 . 32)
                     )
   )
) ; fin de repeat
(entmake '((0 . "SEQEND")))
) ; fin de defun da:DessPoly3D
-------------------------------------------------------------------------------------------

(defun da:DessPoly2D (listesommets)
; dessin de polyligne 2D depuis liste de sommmets
  (entmake
    (append
      (list
        '(0 . "LWPOLYLINE")
        '(100 . "AcDbEntity")
        '(100 . "AcDbPolyline")
        (if z (cons 38 z))
        (cons 90 (length listesommets))
        '(70 . 0)
        )
      (mapcar '(lambda (p) (cons 10 p)) listesommets)
      ) ;_ Fin de append
    ) ;_ Fin de entmake
  ) ; fin de defun da:DessPoly2D
;------------------------------------------------------------
(defun Segment-Arc-Div (ent div ChoixDessinDCL /
						rayon long div LongArcDiv n)
  (setq n 1
        long (vlax-curve-getDistAtParam ent
			   (vlax-curve-getEndParam ent)
			   )
        LongArcDiv (/ long div)
        )
  (repeat div
    (setq listsom (cons
(vlax-curve-getpointatdist ent (* LongArcDiv n))
                        listsom
                        );fin de CONS
          )
    (setq n (1+ n))
    ) ;_ Fin de repeat

  (setq listsom (reverse listsom))
  (setq listsom (cons
				  (vlax-curve-getstartpoint ent)
				  listsom);fin de CONS
		) ;_ Fin de setq

  (cond
    ((= ChoixDessinDCL 0) (da:DessPoly2D listsom))
    ((= ChoixDessinDCL 1) (da:DessPoly3D listsom))
    )
  (princ)
  ) ;_ Fin de defun

(defun ModifTextDCL (val option)
 ; fonction qui va modifier un texte dans une case DCL en fonction du bouton radio choisi
  (cond
    ((= option "fleche")
     (set_tile "Prompt" "Valeur de la FLECHE MAXIMALE") ;_ Fin de set_tile
     (setq rb1 "1" rb2 nil rb3 nil ))
    ((= option "corde")
     (set_tile "Prompt" "Valeur de la CORDE MAXIMALE") ;_ Fin de set_tile
     (setq rb2 "1" rb1 nil rb3 nil ))
    ((= option "deviation")
     (set_tile "Prompt" "Valeur de la DEVIATION MAXIMALE") ;_ Fin de set_tile
     (setq rb3 "1" rb1 nil rb2 nil ))
    ) ;_ Fin de cond
  ) ;_ Fin de defun
;----------------------------------------------------------
(defun Segment-Arc-DCL (ent /
						rb1 rb2 rb3 rb4 rb5 rb6 effent rayon
						long fleche cordebrut div LongArcDiv
						devia centre ang listsom)
  
  (setq dcl_id (load_dialog "Segment-Arc.dcl"))
  
  (if (not (new_dialog "Segment-Arc.dcl" dcl_id))
	(progn (alert "\nErreur chargement DCL\n") (exit))
	)


  ;(mode_tile "BoutonOui" 1)

  (action_tile "rb1" "(setq rb1 $value)")
  (action_tile "rb2" "(setq rb2 $value)")
  (action_tile "rb3" "(setq rb3 $value)")
  (action_tile "rb4" "(setq rb4 $value)")
  (action_tile "rb5" "(setq rb5 $value)")
  (action_tile "rb6" "(setq rb6 $value)")


  (action_tile "EffEnt" "(setq EffEnt $value)")


  (action_tile "rb1" "(ModifTextDCL $value \"fleche\")")
  ;si choix fleche change le texte dans Prompt
  (action_tile "rb2" "(ModifTextDCL $value \"corde\")")
  ;si choix corde change le texte dans Prompt
  (action_tile "rb3" "(ModifTextDCL $value \"deviation\")")
  ;si choix déviation change le texte dans Prompt

  (action_tile "accept"
	           "(setq Valeur (get_tile \"Edit1\"))
	           (done_dialog)")
  ;Fin bouton OK  
  (action_tile "cancel"
	           "(done_dialog)")
  ;Fin bouton CANCEL
  
  (start_dialog)
  (unload_dialog dcl_id)


  ;analyse des boutons allumés
  (cond
    ((= rb1 "1") ; choix de la solution FLECHE
     (setq rayon      (cdr (assoc 40 (entget ent)))
           long       (vlax-curve-getDistAtParam ent (vlax-curve-getEndParam ent))
           fleche     (atof valeur)
           cordebrut  (sqrt
						(-
						  (* 8.0
							 (* rayon fleche)
							 )
						  (* 4.0
							 (* fleche fleche))
						  )
						)
		   
           div        (+
						(fix
						  (/ long cordebrut)
						  )
						1)
		   
           LongArcDiv (/ long div)
		   
           ) ;_ Fin de setq
    )


    ((= rb2 "1")
     (setq rayon (cdr
				   (assoc 40 (entget ent))
				   )
		   long (vlax-curve-getDistAtParam ent
				  (vlax-curve-getEndParam ent)
				  )
           cordebrut (atof valeur)
           div (+
				 (fix
				   (/ long cordebrut)
				   )
				 1)
           LongArcDiv (/ long div)
           ) ;_ Fin de setq
     ) ; fin de rb2=1

    ((= rb3 "1")                        ;deviation
     (setq devia  (atof valeur)
           rayon  (cdr (assoc 40 (entget ent)))
           centre (cdr (assoc 10 (entget ent)))
           ang    (abs (- (cdr (assoc 51 (entget ent))) (cdr (assoc 50 (entget ent)))))
           long   (vlax-curve-getDistAtParam ent (vlax-curve-getEndParam ent))
           div    (+ (fix (/ ang devia)) 1)
           ) ;_ Fin de setq
                                        ;) ;_ Fin de if
     )
    ) ;_ Fin de cond

  (cond ((= rb4 "1") (Segment-Arc-Div ent div 0))
        ((= rb5 "1") (Segment-Arc-Div ent div 1))
        ((= rb6 "1")
         (Segment-Arc-Div ent div 0)
         (command "_explode" (entlast))
         )
        ) ;_ Fin de cond

 ;calculs pour écrire commentaires de la commande
  
  (setq long2 (vlax-curve-getDistAtParam
				(entlast)
				(vlax-curve-getEndParam (entlast))
				)
		)
  (setq corde2 (distance
				 (nth 0 listsom)
				 (nth 1 listsom)
				 )
		)
  (setq fleche2 (/
				  (-
					(* rayon 2.0)
					(sqrt
					  (-
						(* 4.0
						   (* rayon rayon)
						   )
						(* corde2 corde2)
						)
					  )
					)
				  2.0 )
		)


  (if (= typent "CIRCLE")
	(setq typent "CERCLE")
	)
  
  (alert (strcat
		   typent
		   " divisé en "
		   (itoa div)
		   " Segments (égaux) de "
		   (rtos corde2 2 3)
		   " \nFlèche utilisée : "
		   (rtos fleche2 2 3)
		   " \nPerte de longueur : "
		   (rtos (abs (- long2 long)) 2 3)
		   " \nSoit : "
		   (rtos (* (/ (abs (- long2 long)) long) 100.0) 2 2)
		   " % de diminution de la longueur initiale."
            )
    ) ;_ Fin de alert

  (if (= effent "1") (entdel ent))
  
  (princ)
  )


(defun c:testarc (/ ent)
  (setq ent (car
			  (entsel "\nsélection Arc ou Cercle ...\n")
			  )
		)
  
  (setq z (last
			(cdr
			  (assoc 10  (entget ent))
			  )
			)
		)
  
  (if
    (or (= "ARC" (setq typent (cdr (assoc 0 (entget ent)))))
        (= "CIRCLE" (setq typent (cdr (assoc 0 (entget ent)))))
        ) 
     (Segment-Arc-DCL ent)
     (alert "ne fonctionne qu'avec un ARC ou un CERCLE")
     )
  
  
  )


(princ "\n\n Segment-Arc.LSP Chargé......Tapez TestArc pour l'éxecuter")
(princ)

Voila c'est terminé, j'espère que ces explications et ces exemples vont seront utiles et dans tous les cas de figure je suis toujours joignable via le formulaire de contact.
Amicalement,
Didier