-
Compteur de contenus
12 247 -
Inscription
-
Dernière visite
-
Jours gagnés
209
Type de contenu
Profils
Forums
Calendrier
Blogs
Tout ce qui a été posté par (gile)
-
Salut, Comme dit plus haut, les blocs "Uxxx" sont des blocs anonymes générés par AutoCAD pour les blocs dynamiques dont les propriétés ont été modifiées, il faut donc ajouter à ton filtre les blocs dont le nom commence par "U" et traiter ensuite le jeu de sélection pour ne conserver que ceux dont le nom "effectif" est celui recherché. Dans ton cas : (if (setq ss (ssget '((2 . "niveau_coupe,`*U*")))) (repeat (setq i (sslength ss)) (setq br (ssname ss (setq i (1- i)))) (if (/= "niveau_coupe" (strcase (getpropertyvalue (getpropertyvalue br "DynamicBlockTableRecord") "Name"))) (ssdel br ss) ) ) ) ss ne contient maintenant que les blocs dont le nom effectif est "niveau_coupe". On peut aussi en faire une fonction réutilisable : (defun ssgetdyn (nomBloc / ss i br) (setq nomBloc (strcase nomBloc)) (if (setq ss (ssget (list (cons 2 (strcat nombloc ",`*U*"))))) (repeat (setq i (sslength ss)) (setq br (ssname ss (setq i (1- i)))) (if (/= nomBloc (strcase (getpropertyvalue (getpropertyvalue br "DynamicBlockTableRecord") "Name"))) (ssdel br ss) ) ) ) ss ) dans ton cas tu fais simplement : (ssgetdyn "niveau_coupe")
-
Un petit ATTSYNC et tout devrait rentrer dans l'ordre.
-
@ zebulon_, ce n'est pas la première fois que tu m’impressionnes en choisissant des algorithmes auxquels je n'aurais jamais pensé. Merci de me (nous) ouvrir l'esprit.
-
Creer un Arc tangent à deux Elements et qui passe par un point donne
(gile) a répondu à un(e) sujet de lecrabe dans Routines LISP
Salut, J'utilise aussi ces méthodes de résolution graphique mais avec les objets "non graphiques" de l'espace de nom Autodesk.AutoCAD.Geometry de l'API .NET. Il s'agit d'objets purement géométriques (pas de représentation dans AutoCAD) comme des points des vecteurs, des matrices, des segments de droite, des droites, des arcs, etc... Ces objets fournissent de nombreuses propriétés et méthodes qui facilitent grandement les choses. De plus,comme ces objets existent en 2D ou 3D et qu'ici le problème est en 2D, utiliser les objets 2D allège les calculs. Dans le code donné à la réponse #45, la méthode Update() de la classe ArcJig est exécutée à chaque déplacement du curseur, cette méthode appelle les méthodes SetAxis() et GetTangentArcs() pour recalculer les arcs tangents en fonction de la position du curseur et mettre à jour l'arc (graphique) affiché dans AutoCAD. -
Pour revenir sur ma première idée, une autre méthode qui reformate la liste pour obtenir une liste de paires pointées triée par 'car' croissant : (("A" . "B") ("B" . "C") ("B" . "D") ("D" . "E") ("F" . "G")) (defun grouper_locaux (lst / massoc merge group) ;; (massoc "B" '(("A" . "B") ("B" . "C") ("B" . "D") ("D" . "E") ("F" . "G"))) => ("C" "D") (defun massoc (key alst) (if (setq alst (member (assoc key alst) alst)) (cons (cdar alst) (massoc key (cdr alst))) ) ) ;; (group "A" '(("A" . "B") ("B" . "C") ("B" . "D") ("D" . "E") ("F" . "G"))) => ("A" "B" "C" "D" "E") (defun group (a l) (apply 'append (cons (list a) (mapcar '(lambda (x) (group x l)) (massoc a l)))) ) ;; (merge '(("A" . "B") ("B" . "C") ("B" . "D") ("D" . "E") ("F" . "G"))) => (("A" "B" "C" "D" "E") ("F" "G")) (defun merge (lst) (if lst ((lambda (l) (cons l (merge (vl-remove-if '(lambda (x) (member (car x) l)) lst))) ) (group (caar lst) lst) ) ) ) (merge (vl-sort (mapcar '(lambda (p) (if (< (car p) (cadr p)) (cons (car p) (cadr p)) (cons (cadr p) (car p)) ) ) lst ) '(lambda (x1 x2) (< (car x1) (car x2))) ) ) )
-
La question idiote du jour
(gile) a répondu à un(e) sujet de coyotte dans ObjectARX/DBX, C++, .NET, RealDWG
Je suppose que tu veux parler d'une entité ligne (type Line en .NET AutoCAD). La méthode, à mon avis, la plus simple est d'utiliser un des objets géométrique fournit dans l'espace de nom Autodesk.AutoCAD.Geometry : le type LineSegment3d qui a une propriété MidPoint Point3d midPt = new LineSegment3d(line.StartPoint, line.EndPoint).MidPoint; mais sinon, c'est quand même assez simple à implémenter : Point3d midPt = new Point3d( (line.StartPoint.X + line.EndPoint.X) / 2.0, (line.StartPoint.Y + line.EndPoint.Y) / 2.0, (line.StartPoint.Z + line.EndPoint.Z) / 2.0); -
La question idiote du jour
(gile) a répondu à un(e) sujet de coyotte dans ObjectARX/DBX, C++, .NET, RealDWG
Une droite est par définition infinie... Pourrais-tu préciser de quoi tu parles exactement quand tu dis droite. -
Ou, dans un style impératif : (defun grouper (lst / sub tmp acc flag) (while (or lst tmp) (if lst (cond ((null sub) (setq sub (car lst) lst (cdr lst) flag T ) ) ((member (caar lst) sub) (if (member (cadar lst) sub) (setq lst (cdr lst)) (setq sub (cons (cadar lst) sub) lst (cdr lst) flag T ) ) ) ((member (cadar lst) sub) (if (member (caar lst) sub) (setq lst (cdr lst)) (setq sub (cons (caar lst) sub) lst (cdr lst) flag T ) ) ) (T (setq tmp (cons (car lst) tmp) lst (cdr lst) ) ) ) (cond (flag (setq lst tmp tmp nil flag nil ) ) (T (setq acc (cons sub acc) lst tmp tmp nil sub nil flag nil ) ) ) ) ) (cons sub acc) )
-
Quelques commentaires pour expliquer le fonctionnement de la fonction récursive 'group'. ;; fonction récursive de groupement de paires ;; Arguments ;; lst : liste de paires à traiter ;; sub : liste courante d'éléments groupés ;; tmp : liste de paires temporairement rejetées ;; acc : liste des listes d'élements groupés (résultat) ;; flag : drapeau indiquant si sub a été modifiée (defun group (lst sub tmp acc flag) ;; si lst n'est pas vide (if lst (cond ;; si sub est vide, on relance avec lst sans la première paire, sub = la première paire et flag = T ((null sub) (group (cdr lst) (car lst) tmp acc T)) ;; si le premier élément de la première paire de lst est présent dans sub ((member (caar lst) sub) ;; si le second élément de la première paire est présent dans lst, (if (member (cadar lst) sub) ;; alors, on relance avec la liste privée de la première paire (group (cdr lst) sub tmp acc flag) ;; sinon, on l'ajoute dans sub, on passe flag à T (group (cdr lst) (cons (cadar lst) sub) tmp acc T) ) ) ;; si le second élément de la première paire de lst est présent dans sub ((member (cadar lst) sub) ;; si le premier élément de la première paire est présent dans lst, (if (member (caar lst) sub) ;; alors, on relance avec la liste privée de la première paire (group (cdr lst) sub tmp acc flag) ;; sinon, on l'ajoute dans sub, on passe flag à T (group (cdr lst) (cons (caar lst) sub) tmp acc T) ) ) ;; sinon, on ajoute la première paire à tmp ;; et on relance avec la liste privé de la première paire (T (group (cdr lst) sub (cons (car lst) tmp) acc flag)) ) ;; si lst est vide (cond ;; si temp est vide (condition d'arrêt), on ajoute sub à acc et on renvoie acc ((null tmp) (cons sub acc)) ;; si flag = T (sub a été modifiée), on relance en passant tmp à la place lst avec flag = nil (flag (group tmp sub nil acc nil)) ;; sinon, on ajoute sub à acc et on relance en passant tmp à la place lst avec flag = nil (T (group tmp nil nil (cons sub acc) nil)) ) ) ) Rappel : pour suivre la pile des appels d'une fonction récursive, dans la console de l'éditeur Visual LISP, on appelle la fonction 'trace' avec le nom de la fonction : (trace group)on lance la fonction 'group' : (group '(("A" "B") ("B" "C") ("D" "E") ("F" "G") ("B" "D")) nil nil nil nil)et on peut voir la pile des appels dans la fenêtre 'Suivi'.
-
Celle-ci semble fonctionner. (defun grouperpaires (lst / group) (defun group (lst sub tmp acc flag) (if lst (cond ((null sub) (group (cdr lst) (car lst) tmp acc T)) ((member (caar lst) sub) (if (member (cadar lst) sub) (group (cdr lst) sub tmp acc flag) (group (cdr lst) (cons (cadar lst) sub) tmp acc T) ) ) ((member (cadar lst) sub) (if (member (caar lst) sub) (group (cdr lst) sub tmp acc flag) (group (cdr lst) (cons (caar lst) sub) tmp acc T) ) ) (T (group (cdr lst) sub (cons (car lst) tmp) acc flag)) ) (cond ((null tmp) (cons sub acc)) (flag (group tmp sub nil acc nil)) (T (group tmp nil nil (cons sub acc) nil)) ) ) ) (group lst nil nil nil nil) ) (grouperpaires '(("A" "B") ("B" "C") ("D" "E") ("F" "G") ("B" "D"))) retourne : (("F" "G") ("E" "D" "C" "A" "B")) (grouperpaires '(("J/02/110" "J/02/111") ("J/02/109" "J/02/110") ("J/02/108" "J/02/109") ("B/02/107" "J/02/108") ("A/02/003" "A/02/002"))) retourne : (("A/02/003" "A/02/002") ("B/02/107" "J/02/108" "J/02/109" "J/02/110" "J/02/111"))
-
Creer un Arc tangent à deux Elements et qui passe par un point donne
(gile) a répondu à un(e) sujet de lecrabe dans Routines LISP
Petite amélioration, ARCTAN fonctionne sur le plan XY du SCU courant quel que soit celui-ci. Le lien réponse #43 est mis à jour (désinstaller ArcExtension avant d'installer la nouvelle version). -
Salut, Si le critère de tri/groupement correspond bien aux trois derniers caractères des chaînes ("109" dans "J/02/109" ou "002" dans "A/02/002"), le code ci-dessous devrait fonctionner. Il utilise une fonction pour extraire le critère de tri/groupement des chaînes*, et nécessite de trier en ordre croissant à la fois les membres de chaque paire et les paires dans la liste pour faciliter le groupement. (defun grouperlocaux (lst / critere grouper) ;; extrait le critère de tri/groupement de la chaîne (defun critere (str) (atoi (substr str 6))) ;; groupe les paires dans des listes (defun grouper (lst tmp acc) (cond ((null lst) (cons (reverse tmp) acc)) ((null tmp) (grouper (cdr lst) (list (car lst)) acc)) ((= (critere (caar lst)) (critere (cadar tmp))) (grouper (cdr lst) (cons (car lst) tmp) acc) ) (T (grouper lst nil (cons (reverse tmp) acc))) ) ) ;; groupe les paires de la liste après les avoir triées en ordre croissant (grouper (setq lst (vl-sort (mapcar '(lambda (p) (vl-sort p '(lambda (a B) (< (critere a) (critere B)))) ) lst ) '(lambda (a B) (< (critere (car a)) (critere (car B))) ) ) ) nil nil ) )
-
Creer un Arc tangent à deux Elements et qui passe par un point donne
(gile) a répondu à un(e) sujet de lecrabe dans Routines LISP
L'intérêt principal, c'est la puissance, pas tant en terme de rapidité d'exécution qu'en possibilités (APIs) offertes. Ici, par exemple, j'utilise les méthodes et propriétés d'objets purement géométriques (Point2d, Vector2d, Line2d, CircularArc2d, etc) qui facilitent grandement les différentes opérations tout en allégeant considérablement les besoins en ressource. Tu peux lire ce sujet. J'utilise Visual Studio Community 2017, l'IDE gratuit de Microsoft reconnu (même par des anti M$) comme étant le meilleur IDE du moment. Les plugins AutoCAD sont compilé en DLL, mais on peut aussi compiler des EXE, des MSI etc. -
Salut, ;; Met les splines sélectionnées en mode "sommets de contrôle" (defun c:SPLCTRL (/ ss i elst flag) (if (setq ss (ssget '((0 . "SPLINE")))) (repeat (setq i (sslength ss)) (setq elst (entget (ssname ss (setq i (1- i)))) flag (assoc 70 elst) ) (entmod (subst (cons 70 (logand (cdr flag) (~ 1024))) flag elst)) ) ) (princ) ) ;; Met les splines sélectionnées en mode "ajustement" (defun c:SPLFIT (/ ss i elst flag) (if (setq ss (ssget '((0 . "SPLINE")))) (repeat (setq i (sslength ss)) (setq elst (entget (ssname ss (setq i (1- i)))) flag (assoc 70 elst) ) (entmod (subst (cons 70 (logior (cdr flag) 1024)) flag elst)) ) ) (princ) )
-
Creer un Arc tangent à deux Elements et qui passe par un point donne
(gile) a répondu à un(e) sujet de lecrabe dans Routines LISP
Pour ceux que ça intéresse, le code avec quelques commentaires. La classe Commands où est définie la commande ARCTAN et des méthodes pour la sélection des segments. using System.Collections.Generic; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Geometry; using Autodesk.AutoCAD.GraphicsInterface; using Autodesk.AutoCAD.Runtime; using AcAp = Autodesk.AutoCAD.ApplicationServices.Core.Application; using AcDb = Autodesk.AutoCAD.DatabaseServices; [assembly: CommandClass(typeof(Gile.AutoCAD.ArcExtension.Commands))] namespace Gile.AutoCAD.ArcExtension { public class Commands { static TransientManager transientMgr; static List<Drawable> drawables; /// <summary> /// Commande pour dessiner un arc tangent à deux entités rectilignes. /// </summary> [CommandMethod("ARCTAN")] public static void ArcTangent() { var doc = AcAp.DocumentManager.MdiActiveDocument; var db = doc.Database; var ed = doc.Editor; var ucs = ed.CurrentUserCoordinateSystem; transientMgr = TransientManager.CurrentTransientManager; drawables = new List<Drawable>(); try { string errMsg; if (!TrySelectSegment(ed, "\nSélectionnez le premier segment: ", out Line2d line1, out errMsg)) { ed.WriteMessage(errMsg); return; } if (!TrySelectSegment(ed, "\nSélectionnez le second segment: ", out Line2d line2, out errMsg)) { ed.WriteMessage(errMsg); return; } if (line1.IsColinearTo(line2)) { ed.WriteMessage("Les segments sont colinéaires"); return; } using (var tr = db.TransactionManager.StartTransaction()) using (var arc = new Arc()) { var jig = new ArcJig(arc, line1, line2); var status = PromptStatus.Keyword; while (status == PromptStatus.Keyword) { var result = ed.Drag(jig); status = result.Status; if (status == PromptStatus.OK && arc.EndAngle - arc.StartAngle != 0.0) { var curSpace = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite); curSpace.AppendEntity(arc); tr.AddNewlyCreatedDBObject(arc, true); } } tr.Commit(); } } catch(System.Exception ex) { ed.WriteMessage($"\n{ex.Message}\n{ex.StackTrace}"); } finally { if (drawables.Count > 0) { foreach (var drawable in drawables) { transientMgr.EraseTransient(drawable, new IntegerCollection()); drawable.Dispose(); } } } } /// <summary> /// Invite l'utilisateur à sélectionner une entité ou un segment linéaire. /// </summary> /// <param name="ed">Editeur courant.</param> /// <param name="message">Message d'invite.</param> /// <param name="line2d">Droite directrice du segment (output).</param> /// <param name="errMsg">Message d'erreur (output).</param> /// <returns>true, si la sélection est valide ; false, sinon.</returns> static bool TrySelectSegment(Editor ed, string message, out Line2d line2d, out string errMsg) { line2d = null; errMsg = null; var peo = new PromptEntityOptions(message); peo.SetRejectMessage("\nL'objet séléctionné n'est pas une droite, une demi droite, une ligne ou une polyligne."); peo.AddAllowedClass(typeof(Line), true); peo.AddAllowedClass(typeof(AcDb.Polyline), true); peo.AddAllowedClass(typeof(Xline), true); peo.AddAllowedClass(typeof(Ray), true); var per = ed.GetEntity(peo); if (per.Status != PromptStatus.OK) { errMsg = "*Annuler*"; return false; } var id = per.ObjectId; var ucs = ed.CurrentUserCoordinateSystem; var plane = new Plane(); using (var tr = new OpenCloseTransaction()) { if (id.ObjectClass == RXObject.GetClass(typeof(Xline))) { var xline = (Xline)tr.GetObject(id, OpenMode.ForRead); if (xline.BasePoint.Z != 0.0 || xline.UnitDir.Z != 0) { errMsg = "La droite n'est pas sur le plan XY du SCG"; return false; } line2d = new Line2d(xline.BasePoint.Convert2d(plane), xline.UnitDir.Convert2d(plane)); } else if (id.ObjectClass == RXObject.GetClass(typeof(Ray))) { var ray = (Ray)tr.GetObject(id, OpenMode.ForRead); if (ray.BasePoint.Z != 0.0 || ray.UnitDir.Z != 0) { errMsg = "La demi-droite n'est pas sur le plan XY du SCG"; return false; } line2d = new Line2d(ray.BasePoint.Convert2d(plane), ray.UnitDir.Convert2d(plane)); AddTransientRay(ray.BasePoint, ray.UnitDir.Negate()); } else if (id.ObjectClass == RXObject.GetClass(typeof(Line))) { var line = (Line)tr.GetObject(id, OpenMode.ForRead); if (line.StartPoint.Z != 0.0 || line.EndPoint.Z != 0.0) { errMsg = "La ligne n'est pas sur le plan XY du SCG"; return false; } line2d = new Line2d(line.StartPoint.Convert2d(plane), line.EndPoint.Convert2d(plane)); AddTransientRay(line.StartPoint, line.EndPoint.GetVectorTo(line.StartPoint).GetNormal()); AddTransientRay(line.EndPoint, line.StartPoint.GetVectorTo(line.EndPoint).GetNormal()); } else { var pline = (AcDb.Polyline)tr.GetObject(id, OpenMode.ForRead); if (pline.Normal != Vector3d.ZAxis || pline.Elevation != 0) { errMsg = "La polyligne n'est pas sur le plan XY du SCG"; return false; } var pt = pline.GetClosestPointTo(per.PickedPoint.TransformBy(ucs), false); int index = (int)pline.GetParameterAtPoint(pt); if (pline.GetSegmentType(index) != SegmentType.Line) { errMsg = "Le segment sélectionné n'est pas linéaire"; return false; } line2d = pline.GetLineSegment2dAt(index).GetLine(); AddTransientRay(pline.GetPoint3dAt(index), pline.GetPoint3dAt(index + 1).GetVectorTo(pline.GetPoint3dAt(index)).GetNormal()); AddTransientRay(pline.GetPoint3dAt(index + 1), pline.GetPoint3dAt(index).GetVectorTo(pline.GetPoint3dAt(index + 1)).GetNormal()); } return true; } } /// <summary> /// Ajoute une demi-droite transitoire. /// </summary> /// <param name="point">Point de départ de la demi-droite.</param> /// <param name="vector">Direction de la demi-droite.</param> static void AddTransientRay(Point3d point, Vector3d vector) { var ray = new Ray() { BasePoint = point, UnitDir = vector }; ray.ColorIndex = 8; drawables.Add(ray); transientMgr.AddTransient(ray, TransientDrawingMode.Highlight, 128, new IntegerCollection()); } } } La classe ArcJig, c'est là que tout se passe. using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Geometry; namespace Gile.AutoCAD.ArcExtension { /// <summary> /// Fournit les méthodes pour afficher dynamiquement un arc tangent à deux segments passant par la position du curseur. /// </summary> class ArcJig : EntityJig { Arc arc; Point3d dragPt; Line2d line1, line2, axis; int index = 0; /// <summary> /// Crée une nouvelle instance de ArcJig. /// </summary> /// <param name="arc">Instance de Arc à mettre à jour dynamiquement.</param> /// <param name="line1">Première droite de tangence.</param> /// <param name="line2">Seconde droite de tangence.</param> public ArcJig(Arc arc, Line2d line1, Line2d line2) : base(arc) { this.arc = arc; this.line1 = line1; this.line2 = line2; axis = new Line2d(); } /// <summary> /// Invite l'utilisateur à spécifier un point ou à faire défiler les arcs possibles. /// </summary> /// <param name="prompts">Aquisition des données.</param> /// <returns>Le résultat de l'invite.</returns> protected override SamplerStatus Sampler(JigPrompts prompts) { var options = new JigPromptPointOptions("\nSpécifiez un point sur l'arc ou [arc Suivant]: ", "Suivant"); options.Keywords.Default = "Suivant"; options.UserInputControls = UserInputControls.NullResponseAccepted; var result = prompts.AcquirePoint(options); if (result.Status == PromptStatus.Keyword) { index++; return SamplerStatus.OK; } else { if (result.Value.IsEqualTo(dragPt)) return SamplerStatus.NoChange; dragPt = result.Value; return SamplerStatus.OK; } } /// <summary> /// Met à jour l'arc en fonction du résultat à l'invite. /// </summary> /// <returns>true.</returns> protected override bool Update() { var point = dragPt.Convert2d(new Plane()); var arcs = GetTangentArcs(point); if (arcs == null) { arc.StartAngle = arc.EndAngle = 0.0; } else { var arc2d = arcs[index % arcs.Length]; arc.Center = new Point3d(arc2d.Center.X, arc2d.Center.Y, 0.0); if (arc2d.Radius > 0.0) arc.Radius = arc2d.Radius; arc.StartAngle = arc2d.StartAngle + arc2d.ReferenceVector.Angle; arc.EndAngle = arc2d.EndAngle + arc2d.ReferenceVector.Angle; } return true; } /// <summary> /// Définit l'axe de symétrie entre les deux droites en fonction de la position du point. /// </summary> /// <param name="point">Point déterminant le secteur de l'axe.</param> void SetAxis(Point2d point) { if (line1.IsOn(point) || line2.IsOn(point)) return; if (line1.IsParallelTo(line2)) { var pt = line1.PointOnLine; var segment = new LineSegment2d(pt, line2.GetClosestPointTo(pt).Point); axis = new Line2d(segment.MidPoint, line1.Direction); } else { var intersPt = line1.IntersectWith(line2)[0]; if (intersPt.IsEqualTo(point)) return; var dir = intersPt.GetVectorTo(point); var dir1 = line1.Direction; var dir2 = line2.Direction; if (IsClockWise(dir, dir1)) { if (IsClockWise(dir, dir2)) axis = new Line2d(intersPt, dir1 + dir2.Negate()); else axis = new Line2d(intersPt, dir1 + dir2); } else { if (IsClockWise(dir, dir2)) axis = new Line2d(intersPt, dir1 + dir2); else axis = new Line2d(intersPt, dir1 + dir2.Negate()); } } } /// <summary> /// Evalue si les vecteurs tournent en sens horaire. /// </summary> /// <param name="v1">Premier vecteur.</param> /// <param name="v2">Second vecteur.</param> /// <returns>true, si le sens de rotation est le sens horaire ; false, sinon.</returns> bool IsClockWise(Vector2d v1, Vector2d v2) => v1.X * v2.Y - v2.X * v1.Y < 0.0; /// <summary> /// Calcule les arcs tangents aux deux droites passant par le point. /// </summary> /// <param name="point">Point de passage des arcs.</param> /// <returns>Une collection d'arcs ou null en cas d'impossibilité géométrique</returns> CircularArc2d[] GetTangentArcs(Point2d point) { SetAxis(point); var perp = axis.GetPerpendicularLine(point); var inters = axis.IntersectWith(perp); if (inters == null) return null; var cen = inters[0]; inters = perp.IntersectWith(line1); if (inters == null) return null; var pt1 = inters[0]; inters = perp.IntersectWith(line2); if (inters == null) return null; var pt2 = inters[0]; var tol = new Tolerance(1e-8, 1e-6); double radius; CircularArc2d arc2d; if (point.IsEqualTo(pt1, tol)) { cen = line1.GetPerpendicularLine(pt1).IntersectWith(axis)[0]; radius = cen.GetDistanceTo(pt1); if (radius == 0.0) return null; var vector = axis.Direction * radius; pt2 = pt1.Mirror(axis); arc2d = new CircularArc2d(pt1, cen - vector, pt2); if (arc2d.IsClockWise) return new[] { new CircularArc2d(pt2, cen - vector, pt1), new CircularArc2d(pt1, cen + vector, pt2) }; else return new[] { arc2d, new CircularArc2d(pt2, cen + vector, pt1) }; } if (point.IsEqualTo(pt2, tol)) { cen = line2.GetPerpendicularLine(pt2).IntersectWith(axis)[0]; radius = cen.GetDistanceTo(pt2); if (radius == 0.0) return null; var vector = axis.Direction * radius; pt1 = pt2.Mirror(axis); arc2d = new CircularArc2d(pt1, cen - vector, pt2); if (arc2d.IsClockWise) return new[] { new CircularArc2d(pt2, cen - vector, pt1), new CircularArc2d(pt1, cen + vector, pt2) }; else return new[] { arc2d, new CircularArc2d(pt2, cen + vector, pt1) }; } if (point.IsEqualTo(cen)) { radius = pt1.GetDistanceTo(cen); } else { var c1 = new CircularArc2d(cen, cen.GetDistanceTo(point)); var seg = new LineSegment2d(cen, pt1); var c2 = new CircularArc2d(seg.MidPoint, seg.Length / 2.0); inters = c1.IntersectWith(c2); if (inters == null) return null; radius = pt1.GetDistanceTo(inters[0]); } pt2 = pt1 - line1.Direction * radius; pt1 += line1.Direction * radius; arc2d = new CircularArc2d(pt1, point, pt1.Mirror(axis)); if (arc2d.IsClockWise) return new[] { new CircularArc2d(pt1.Mirror(axis), point, pt1), new CircularArc2d(pt2, point, pt2.Mirror(axis)) }; else return new[] { arc2d, new CircularArc2d(pt2.Mirror(axis), point, pt2) }; } } } -
Creer un Arc tangent à deux Elements et qui passe par un point donne
(gile) a répondu à un(e) sujet de lecrabe dans Routines LISP
Revenons à nos moutons. Tout d'abord un grand bravo à ceux qui s'accrochent en LISP. Je pense que challenge est difficile mais très intéressant. De mon côté j'ai un peu avancé, ça fonctionne désormais avec les droites, les demi-droites, les lignes et les segments droits de polyligne (toujours dans le plan XY du SCG). J'ai aussi essayé de rendre le code plus robuste pour pouvoir gérer tout éventualité et préparer le terrain pour les arcs (même s'il n'est pas sûr que je le fasse). Plutôt que de déterminer l'arc généré en fonction des points de sélection des segments (comme semble le faire Microstation) j'ai préféré proposer la possibilité de parcourir les différentes solutions (uniquement 2 pour les segments droits) en validant l'option par défaut : "arc Suivant". Pour ceux qui veulent tester, télécharger et installer ArcExtensionSetup.msi. (Nouvelle version compatible avec AutoCAD 2025+) -
Creer un Arc tangent à deux Elements et qui passe par un point donne
(gile) a répondu à un(e) sujet de lecrabe dans Routines LISP
Juste un petit bémol,ce ne sont pas tant les langages supportés que les APIs et les fonctionnalités exposées qui importent. Par exemple, l'API COM d'AutoCAD (la seule utilisable par le VBA AutoCAD) présente, à mon sens de graves lacunes (absence d'objets Point2D ou Point3d, pitoyable gestion des jeux de sélection, ...). Heureusement, en LISP on peut mixer les accès à l'API COM (fonctions vla-*) avec du "pur AutoLISP" pour palier à ces lacunes. Et donc, à mon sens, ce qui fait la force d'AutoCAD, c'est de proposer, avec AutoLISP, un environnement de programmation à la fois très bien intégré et accessible au plus grand nombre (malgré les spécificités du langage LISP). -
Creer un Arc tangent à deux Elements et qui passe par un point donne
(gile) a répondu à un(e) sujet de lecrabe dans Routines LISP
Merci pour les compliments, mais je ne suis pas sûr d'arriver à faire tout ce qu'il faudrait implémenter pour imiter correctement ce que fait Microstation (je me suis mis la barre assez haut : fonctionnement dynamique dans tous les plans avec des lignes polylignes, arcs et cercles). J'ai un peu avancé, toujours avec des segments droits (ligne ou polyligne), toujours dans le plan XY et avec la possibilité de changer le sens de l'arc par validation de d'une option, mais il reste du boulot... C'est, bien sûr, écrit en C#/.NET. Je pense qu'en LISP on peut écrire (plus rapidement) quelque chose qui fonctionne sans fioritures, ni effets visuels. -
Creer un Arc tangent à deux Elements et qui passe par un point donne
(gile) a répondu à un(e) sujet de lecrabe dans Routines LISP
Salut, C'est juste un début. Pour l'instant, ça ne fonctionne qu'avec des lignes sur le plan XY du SCG. Le sens de l'arc dépend de la position du curseur par rapport au premier point cliqué. -
Arguments de la methode Run
(gile) a répondu à un(e) sujet de Fraid dans Pour aller plus loin en LISP
Salut, Regarde ici. -
Fonctions C# et espace lisp séparé
(gile) a répondu à un(e) sujet de D.DUHEM dans ObjectARX/DBX, C++, .NET, RealDWG
Salut, Même dans ce sens : .NET appelé depuis AutoLISP la liaison n'est pas terrible (dans l'autre sens, c'est souvent désastreux). Comme beaucoup de ceux qui ont commencé .NET en venant d'AutoLISP, j'ai essayé d'utiliser des passerelles entre les deux environnements mais je n'ai jamais été pleinement satisfait (par exemple il n'est pas possible depuis une méthode LispFunction, de générer proprement des erreurs AutoLISP en cas de nombre ou de type d'argument incorrect). J'ai donc fini par renoncer à utiliser ce type d'interaction bien avant d'essayer des solution aussi complexes que celles que tu évoques. À mon sens, le plus simple est finalement de ré-écrire la partie LISP en .NET. -
Salut, Tu peux utiliser la fonction rem qui renvoie le reste d'une division. (if (zerop (rem a B)) (prompt (strcat "\n" (itoa a) " est multiple de " (itoa B))) (prompt (strcat "\n" (itoa a) " n'est pas multiple de " (itoa B))) ) PS: attention à la manière dont tu formules tes questions, par définition, un nombre premier n'est divisible que par lui même et par 1. ;)
-
longueur totale vers champs de texte
(gile) a répondu à un(e) sujet de jireu dans LISP et Visual LISP
Salut, Je pense que comme beaucoup, tu pars dans la mauvaise direction. Tu penses qu'il faut utiliser un LISP pour faire ce que tu veux (ce n'est pas sûr). Tu cherches un LISP qui te sembles correspondre à ce que tu souhaites et tu veux l'adapter malgré le fait que tu ne comprennes pas vraiment comment fonctionne le code (là c'est l'échec assuré). Si tu veux utiliser un champ, commence par creuser ce qu'on peut faire directement avec les champs (sans programmation). Voir ce sujet et aussi celui-là. Si tu veux utiliser un LISP (avec ou sans utilisation d'un champ), et si tu veux le faire par toi-même et il faut accepter de commencer par écrire (tout seul) des routines plus simples pour acquérir les bases plutôt que d'utiliser des bouts de code que tu ne comprends pas. -
Supprimer plusieurs attribut de blocs différents
(gile) a répondu à un(e) sujet de flo54 dans LISP et Visual LISP
Qu'un AutoCAD "vertical" fasse plus de chose qu'un AutoCAD "standard", c'est précisément ce qu'on lui demande, mais qu'il perde des fonctionnalités par rapport à un AutoCAD de base est plus surprenant. Mais il me semble qu'on avait déjà noté ce genre de chose avec MAP/Civil justement. -
Supprimer plusieurs attribut de blocs différents
(gile) a répondu à un(e) sujet de flo54 dans LISP et Visual LISP
Ça doit être être une question de paramétrage d'AutoCAD (variable système) parce que chez moi ça fonctionne sur toutes les versions d'AutoCAD 2013 à 2019.
