Najmniejszy opisany prostokąt - LISP


Iskra

Rekomendowane odpowiedzi

Zaraz strzelanie. Są co najmniej dwie drogi obejścia nieszczęśliwego ESC. Dlaczego o jednej i (tylko jednej) właśnie już teraz ma się dowiedzieć Iskra i każdy inny? Oczywiście może, ale (w mojej opinii) ważniejszym jest skoncentrowanie się na szerszym spojrzeniu na zagadnienie poprawnego wyboru obiektu. Określeniu warunków, i ich sprawdzeniu itp. Obsługa błędów, ogólnie rzecz biorąc jest zwykle "wisienką na torcie".
Nadal nie jestem przekonany czy
 

HasArcSegment

jest czytelniejsze niźli
 

(zerop (apply '+ (mapcar 'abs (cd:DXF_massoc 42 EntityList))))

biorąc pod uwagę, że mogę dodać komentarz właśnie:
 

; Ma sgementy łukowe?

a działanie i tak (w obu przypadkach) trzeba prześledzić w ten sam sposób.
Oczywiście to moje subiektywne zdanie.
 

Odnośnik do komentarza
Udostępnij na innych stronach

Użyłem tego sposobu bo z niego korzystam i sprawdza się. Skoro są inne nie zabraniam ich przedstawienia. Chce natomiast wskazać na to że jakoś to trzeba ogarnąć. Można użyć entsel i musieć pamiętać, że w przyszłości trzeba będzie tą wisienkę na tort położyć albo użyć Biblioteka:Entsel i  mieć temat załatwiony.

A w kwestii czytelności to właśnie o to chodzi że tylko w drugim przypadku trzeba prześledzić kod i na jego podstawie wyciągać wnioski. Korzystając z nazwanej funkcji można śledzić ten kod jeśli to właśnie jego dotyczy zmiana, którą chcemy wprowadzić, albo szukać innej części kodu, gdzie chcemy coś zmienić.
Na początku powołałem się na Roberta C. Martina. Jakiś czas temu czytałem :http://helion.pl/ksiazki/agile-programowanie-zwinne-zasady-wzorce-i-praktyki-zwinnego-wytwarzania-oprogramowania-w-c-robert-c-martin-micah-martin,agile.htm I inne tego samego autora. Przykłady są akurat w C#, ale to zupełnie nieistotne koncepcje są uniwersalne.

Ta lektura zmieniła moje spojrzenie na programowanie. Polecam każdemu kto zajmuje się, lub chciałby w przyszłości zajmować się programowaniem.  Ważna kwestia jest taka, że pisząc kod musimy tak samo analizować HasArcSegment i  (zerop (apply '+ (mapcar 'abs (cd:DXF_massoc 42 EntityList))))

Ale kod pisze się tylko raz, a czyta wiele razy. I właśnie wtedy jasne stają się korzyści z funkcji i z używania dobrze dobranych nazw funkcji. Wyobraźmy sobie funkcję na 300,  1000 linii kodu (sam takie pisałem i pisali takie też Ci od których się uczyłem). Trzeba w takim kodzie, dokonać prostej zmiany, możemy przeczytać i przeanalizować dużą część zanim znajdziemy to co potrzeba, jeśli tą samą funkcję podzielimy na kilka mniejszych, wyszukanie tej  linijki kodu, którą trzeba zmienić trwa dużo krócej właśnie dlatego, że nie trzeba śledzić każdej linijki, a tylko te, które są bezpośrednio powiązane z celem zmiany.

Oczywiście możemy przyjąć że znamy program i pamiętamy gdzie co jest i nie musimy czytać żeby znaleźć. Ja nie mam tak dobrej pamięci. Przyznaję, że jeśli muszę coś zmienić w kodzie który pisałem lata temu, muszę to analizować i często robi mi się przykro że wtedy nie pisałem tak jak teraz.
Cały rozdział jest też poświęcony komentarzom  w kodzie, polecam.

Odnośnik do komentarza
Udostępnij na innych stronach

Chyba się nie rozumiemy do końca. W mojej opinii budowanie funkcji (a bibliotecznych szczególnie) ma na celu (między innymi) skracanie kodu. Przykłady które zaprezentowałeś idą w przeciwnym kierunku. "Moje" ośmiolinijkowe <and> (z jedną tylko funkcją lokalną) zastępujesz sześcioma funkcjami (już nie liczę ilości linii). Na koniec twierdzisz że jest to bardziej czytelne. Pozornie tak (są nazwy), w istocie śledzenie kodu jest jednak dużo trudniejsze. Zobacz. Przykładowa funkcja biblioteczna (stosunkowo krótka) może nas informować jednocześnie czy Polilinia jest zamknięta, czy jest PLINEGEN i czy zawiera szegmenty łukowe:
 

(defun GetPolyProp (Ent / d)  
  (setq d (entget Ent))  
  (+    
    (cdr (assoc 70 d))    
    (if (zerop (apply '+ (mapcar 'abs (cd:DXF_massoc 42 d)))) 0 256)  
  )
)

Wywołanie:
 

(GetPolyProp (car (entsel)))

Stosuję jedną funkcję (tylko jedno wywołanie), i mam jednocześnie trzy informacje. Takie podejście (skracanie kodu) uważam za lepsze jeśli chodzi o czytelność. Oczywiście, abyśmy się zrozumieli - w żadnym wypadku nie deprecjonuję innych rozwiązań.

Edytowane przez kojacek
Odnośnik do komentarza
Udostępnij na innych stronach

Tymczasem wróćmy (bośmy nieco "zboczyli"... ;) ) do wyboru polilinii. Krótko jeszcze o "zadaniu domowym". Spójrzmy na obrazek porównujący prostokąt z "kokardką":
 post-451-0-12369900-1445091161.png
Pożądany przez nas czworokąt będący prostokątem można sprawdzić też w ten sposób:
Weźmy dwa sąsiednie boki (np. 1-2 i 2-3). Wybierzmy najdłuższy z nich (tu będzie to 1-2). I teraz sprawdzamy: w prostokącie ten najdłuższy z boków musi być krótszy od długości przekątnej (np. 1-4), dodatkowo oczywiście to (przekątne) jednocześnie muszą być sobie równe. I to chyba w zupełności wystarczy?
Kod "zadania domowego":

(defun SelRect (/ e d rect-p)
  (defun rect-p (p / d)
    (if                                          ; jezeli
      (not (zerop (distance (car p)(cadddr p)))) ; 1 i 4 pkt sie nie pokrywaja
      (and                                       ; sprawdz
        (equal                                   ; czy jest rowna
          (setq d (distance (car p)(caddr p)))   ; dlugosc 1 przekatnej
          (distance (cadr p)(cadddr p))          ; i 2 przekatnej?
          0.001
        )
        (<                                       ; i najwiekszy
          (max                                   ; bok z 1 i 2
            (distance (car p)(cadr p))           ; jest
            (distance (cadr p)(caddr p))         ; mniejszy
          )
          d                                      ; od przekatnej
        )
      )                                          ; T  (spelnia warunki)
    )                                            ; nil (nie spelnia)
  )
  (if    
    (and      
      (setq e (entsel "\nWskaż prostokąt: "))                    ; jest wybor
      (= (cdr (assoc 0 (setq d (entget (car e))))) "LWPOLYLINE") ; to LWPoly
      (= 1 (logand 1 (cdr (assoc 70 d))))                        ; jest zamknieta
      (zerop (apply '+ (mapcar 'abs (cd:DXF_massoc 42 d))))      ; nie ma seg. lukowych
      (= (cdr (assoc 90 d)) 4)                                   ; ma 4 wierzcholki
      (rect-p (cd:DXF_massoc 10 d))                              ; jest prostokatem
    )
    (princ "\nOk")
    (princ "\nŹle. ")
  )
)

wzbogacony o komentarze, które pozwolą łatwiej prześledzić działanie.

Odnośnik do komentarza
Udostępnij na innych stronach

W końcu udało mi się przysiąć i trochę nadrobić zaległości. Ostatni dni mi wypadły ze wzgledu na 2 dniowe szkolenie.

 

Kod kojacka działa bez zarzutu. Mała tylko uwaga a właściwie pytanie dotyczy czy zawsze przed nazwą funkcji

trzeba wpisywać c:. Bez tego nie mogłem uruchomić funkcji.

Teraz jest

(defun SelRect (/ e d rect-p)

zamiast:

(defun c:SelRect (/ e d rect-p)

Po za tym wszystko działało. Ja przynajmniejnie znalazłem żadnych wyjątków.

 

Co do kodu kruszyńskiego nie udało mi się go odpalić. Szukałem błedów ale bezskutecznie.

Autocad wskazuje zbyt małą ilość argumentów.

Wczytano pomyślnie test1.LSP.
Polecenie: ; błąd: zbyt mało argumentów: (IF (AND (NOT (ZEROP (DISTANCE (CAR P) 
(CADDDR P)))) (EQUAL (DISTANCE (CAR P) (CADDR P)) (DISTANCE (CADR P) (CADDDR 
P)) 0.001)))

Co do metody wydaje mi się że samo sprawdzenie lepiej byłoby zrobić w jednym bloku i uzyskać odpowiedź.

Przy każdej linijce wpisując komentarz co sprawdzamy. Ewentualnie wypisać w linii definiowania funkcji wpisać

komentarz z poszczególnymi warunkami w kolejności w jakiej póżniej występują.

 

Analizując kod kryszyńskiego trochę mnie zmyliło sprawdzenie warunku ilości wierzchołków.

	(defun IsPolyline ( / ) 
		(= (cdr (assoc 0 EntityList)) "LWPOLYLINE")  
	)
	(defun IsClosed ( / ) 
		(= 1 (logand 1 (cdr (assoc 70 EntityList))))
	 )
	(defun NumberOfVertex ( / ) 
		(cdr (assoc 90 EntityList))
	)
	(defun HasArcSegment (EntityList / ) 
		(zerop (apply '+ (mapcar 'abs (cd:DXF_massoc 42 EntityList))))
	)

IsPolyline oraz IsClose jest wykonywana w całości i zwraca T lub nil

NumberOfVertex zwraca ilość wierzchołków o dopiero pod koniec sprawdza czy ilośc się zgadza.

Jeżeli od początku nastawiamy się na sprawdzenie czemu od razu nie wpisać całego warunku.

	(defun NumberOfVertex ( / ) 
		(= 4 (cdr (assoc 90 EntityList)))

Z drugiej strony nazywanie funkcji tak jak zrobił to kruszyński bardzo mi się spodobało. Podczas pisania

zajmuje wiecej czasu ale samo czytanie później kodu jest dużo łatwiejsze. Nie wymaga rozpisywania wszystkich wprowadzonych zmiennych lub funkcji.

Przy większych programach lub w przypadku gdy funkcja będzie wykorzystywana kilkukrotnie jest to duze udogodnienie przy programowaniu.

 

Mam też coś co mi siedzi w głowie od paru dni i nie mogę znaleźć rozwiązania.

Jak sprawdzić kierunek dla polilinii zamkniętej. Chodzi mi o to aby dowolny wielobok z polilinii był rysowany w kierunku przeciwny do kierunku wskazówek zegara.

Nie jest to potrzebne do programu "najmniejszy opisany prostokąt" ale skoro już wałkujemy tą polilinię, pomyślałem, że dorzucę tutaj powyższe zagadnie.

Maszyna CNC na której pracuje musi mieć podany zawsze keriunek jak wyżej. Chodzi o to że maszyna frezuje zawsze po prawej stronie od wskazanej ścieżki. Jezeli kierunek jest odwrócony wytnie zły element. w Autocadzie nie widać jaki jaki kierunek ma Polilinia, dopiero jak podczytam dxf to wyskakują mi błędy.

 

Pierwsza myśl była aby znaleźć środek ciężkości figury i sprawdzić kąty między odcinkami. Jeżeli byłyby dodatnie (rosnące) to wtedy uznać że figura jest prawidłowa.

Problem jest jeżeli figura miałaby wklęśnięcia i środek ciężkości by wypadł poza obrys figury. Wtedy zczytywane kąty byłyby ujemne mimo prawidłowego kierunku.

Odnośnik do komentarza
Udostępnij na innych stronach

Funkcja z przedrostkiem C: to specjalna definicja funkcji lispowej - najoględniej mówiąc tworzy polecenie AutoCAD-a, zdefiniowane w LISP-ie. Wywołanie jej działa jak polecenie, można je wywołać z linii poleceń. Taka funkcja nie może mieć argumentów.
Natomiast "zwykłe" funkcje można wywołać w linii poleceń, ale trzeba je wywoływać w pełnej składni wymaganej przez interpreter LISP-a, czyli w nawiasach i z argumentami (jeśli funkcja je posiada). I tak, namaluj coś i w linii poleceń napisz:

(entlast)

funkcja zwróci ename ostatniego obiektu. Teraz wywołaj funkcję z argumentem np. :

(entget (entlast))

dostaniesz listę DXF danych tego obiektu.
Tak samo wywołujesz funkcję SelRect:

(SelRect)

 
Drugi problem. Kierunek polilinii. Potrzebujesz funkcji napisanej przez gile'a. Wygląda tak:

;; Clockwise-p  -  Gilles Chanteau (gile)
;; Returns T if p1,p2,p3 are clockwise oriented
(defun gc:clockwise-p (p1 p2 p3)
    (< (sin (- (angle p1 p3) (angle p1 p2))) -1e-14)
)

Zwraca T jeśli 3 punkty podane jako argumenty funkcji tworzą kąt zorientowany zgodnie z ruchem wskazówek zegara, lub nil w przeciwnym wypadku. 
Potem można już zdefiniować polecenie sprawdzające polilinię. Musi ona mieć co najmniej dwa wierzchołki. Funkcja może wyglądać tak:

(defun C:TESTCLP (/ e d p)
  (if
    (and
      (setq e (entsel "\nWybierz polilinie: "))
      (= (cdr (assoc 0 (setq d (entget (car e))))) "LWPOLYLINE")
    )
    (if
      (> (length (setq p (cd:DXF_massoc 10 d))) 2)
      (gc:clockwise-p (car p)(cadr p)(caddr p))
    )
  )
)
Odnośnik do komentarza
Udostępnij na innych stronach

Dołącz do dyskusji

Możesz dodać zawartość już teraz a zarejestrować się później. Jeśli posiadasz już konto, zaloguj się aby dodać zawartość za jego pomocą.

Gość
Dodaj odpowiedź do tematu...

×   Wklejono zawartość z formatowaniem.   Usuń formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Odnośnik został automatycznie osadzony.   Przywróć wyświetlanie jako odnośnik

×   Przywrócono poprzednią zawartość.   Wyczyść edytor

×   Nie możesz bezpośrednio wkleić grafiki. Dodaj lub załącz grafiki z adresu URL.

Ładowanie