#|
|# ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; This is an example of a user command that will emulate all existing ;;; ;; extrude capabilities, plus add a new option. The new option :draft ;;; ;; allows the user to specify if the extruded result should be drafted. ;;; ;; It is possible to make a mill command out of this file by exchanging ;;; ;; all occurances of "extrude" with "mill", and changing the ;;; ;; "add_loft :part" statement to "remove_loft :parts". ;;; ;; ;;; ;; NOTE: THIS COMMAND CAN NOT YET SUPPORT NESTED LOOPS IN A PROFILE ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (in-package :acme_warehouse) (use-package :oli) (sd-defdialog 'draft_extrude :dialog-title "extrude" :variables '((*FEEDBACK* :initial-value nil) (*START-STATE* :initial-value (sd-set-model-checkpoint)) (*CURR-WP* :initial-value (sd-inq-curr-wp)) (*MODE* :initial-value (sd-inq-geometry-mode)) (*START-ELEM*) ;this can go after we get loop info (*END-ELEM*) ;this can go after we get loop info (A_PART :value-type :part-incl-new :prompt-text "Identify part to be modified") (WP :value-type :wp-with-profile :title "Workplane" :prompt-text "Identify workplane to extrude" :check-function #'find-start-end-elem :after-input (update-pers :wp)) ("-") (DISTANCE :value-type :distance :prompt-text "Specify distance to extrude" :after-input (update-pers :distance)) (TO_PART :value-type :part :prompt-text "Specify part to extrude to" :after-input (update-pers :to_part)) (DRAFT :value-type :angle :prompt-text "Enter draft angle" :initial-optional t :after-input (draft-feedback :show) :end-input-feedback (draft-feedback :hide)) ("-") (DIRECTION :value-type :wp-normal :title "Reverse") (KEEP_WP :value-type :boolean) ) :mutual-exclusion '(distance to_part) :ok-action ;keep_wp=t, to_part=nil '(progn (when (and DRAFT (= DRAFT 0)) (setq DRAFT nil)) (extrude-doer) ) :local-functions '( (update-pers (item) ;function to manage interaction of distance and part options (case item (:distance (setq TO_PART nil)) (:to_part (setq DISTANCE nil) (setq DRAFT nil) (sd-set-variable-status 'DRAFT :enable nil) (draft-feedback :hide)) (:wp (draft-feedback :show)) ) ) (draft-feedback (mode) ;function to display or hide draft feedback (case mode (:hide (destroy-draft-feedback)) (:show (if (and DRAFT (= DRAFT 0)) (setq DRAFT nil) ;else (create-draft-feedback)) ) ) ) (destroy-draft-feedback () (sd-end-direction-feedback *FEEDBACK*) (setq *FEEDBACK* nil)) (find-start-end-elem (wp &aux eg sel-list s-elem e-elem start-coord) ;find all real geo on current wp (sd-call-cmds (current_wp wp)) (setq sel-list (sd-call-cmds (get_selection :focus_type *sd-edge-2d-seltype* :curr_wp_only :select :all_2d))) ;check if wp has more than one element (when (< (length sel-list) 2) (return-from find-start-end-elem (values :error "Must have more than one element on this wp")) ) ;select the start and end element (setq s-elem (first sel-list)) (setq eg (sd-inq-edge-geo s-elem :dest-space wp)) (setq start-coord (sd-gpnt3d-to-2d (sd-edge-start-pnt eg))) ;find elem attached to start pt. (setq e-elem (sd-call-cmds (get_selection :focus_type *sd-edge-2d-seltype* :curr_wp_only :select :by_vertex_2d start-coord))) ;if we can not find an element, then reject this WP (unless e-elem (return-from find-start-end-elem (values :error "Internal error in getting start/end elem")) ) (if (equal (first e-elem) s-elem) (setq e-elem (second e-elem)) (setq e-elem (first e-elem))) ;save start and end element for later use (setq *start-elem* s-elem) (setq *end-elem* e-elem) (return-from find-start-end-elem :ok) ) (create-draft-feedback () (let ((eg (sd-inq-edge-geo *END-ELEM* :dest-space :global)) start-arrow normal tangent half-p geo-data) ;if there is feedback, destroy it first. (when *FEEDBACK* (destroy-draft-feedback)) ;find mid point of end-elem (setq half-p (/ (+ (sd-edge-s-end eg) (sd-edge-s-start eg)) 2.0)) (setq geo-data (sd-inq-edge-pnt *END-ELEM* :s half-p :coordinates t :tangent t :dest-space :global)) ;calculate feedback coordinates (setq start-arrow (getf geo-data :coordinates)) (setq tangent (getf geo-data :tangent)) (setq normal (sd-vec-cross-product tangent (sd-inq-wp-w-dir WP))) ;draw feedback (setq *FEEDBACK* (sd-start-direction-feedback :disc nil :point start-arrow :direction normal)) ) ) (extrude-doer () (if draft ;use the lofting approach (extrude-with-draft) ;use the standard extrude approach (standard-extrude) ) ) (display-error (&optional (mesg nil) (cleanup nil)) ;cleanup first since user can terminate while message is displayed (when cleanup (cleanup)) (if mesg (sd-display-error (format nil "~A~%~A" mesg (sd-inq-error-obj :message))) (sd-display-error (sd-inq-error-obj :message))) ) (cleanup () (sd-return-to-model-checkpoint *START-STATE*) ) (standard-extrude () (let ((reverse nil) (cmd-opt `(:part ,A_PART :wp ,WP))) ;check if we need to use the :reverse option (unless (plusp (sd-vec-scalar-product direction (sd-inq-wp-w-dir WP))) (setq reverse t)) (if to_part (setq cmd-opt (nconc cmd-opt `(:to_part ,TO_PART))) (setq cmd-opt (nconc cmd-opt `(:distance ,DISTANCE)))) (when reverse (nconc cmd-opt '(:reverse))) (if KEEP_WP (setq cmd-opt (nconc cmd-opt '(:keep_wp :yes))) (setq cmd-opt (nconc cmd-opt '(:keep_wp :no)))) ;do the extrude (sd-call-cmds (apply #'extrude cmd-opt) :failure (cleanup)) ) ) (extrude-with-draft (&aux select-list) (let (equid-list wp-set draft-wp cp-wp start-match-coord end-match-coord) ;create workplane set with copy of original wp and new offset wp (multiple-value-setq (wp-set draft-wp cp-wp) (create-draft-wp-set)) (sd-call-cmds (current_wp cp-wp)) (sd-call-cmds (geometry_mode :real)) ;find all real geometry on the cp-wp (setq select-list (sd-call-cmds (get_selection :focus_type *sd-edge-2d-seltype* :curr_wp_only :select :all_2d) :failure (progn (display-error "unable to locate geometry" t) (return-from extrude-with-draft)))) ;calculate and perform offset (multiple-value-setq (start-match-coord end-match-coord) (create-equidistance-loop select-list)) ;if no match coordinate is found, terminate (unless start-match-coord (return-from extrude-with-draft)) ;find new elements created by equidistance (setq equid-list (sd-call-cmds (get_selection :focus_type *sd-edge-2d-seltype* :curr_wp_only :select :start :all_2d :remove select-list :select_done))) ;check if wp is valid ;this should be done before projecting, to avoid ;projecting self-interseting curves (unless (sd-basic-wp-profile-check-p cp-wp) (display-error "Draft angle is too great" t) (return-from extrude-with-draft)) ;project new geo onto new workplane as real geo (sd-call-cmds (current_wp draft-wp)) (sd-call-cmds (project equid-list)) (sd-call-cmds (delete_2d equid-list)) ;create matchline (let ((start-match-pt (create-match-pt start-match-coord cp-wp)) (end-match-pt (create-match-pt end-match-coord draft-wp))) (sd-call-cmds (create_match_line start-match-pt end-match-pt)) ) ;loft with new body or existing body (sd-call-cmds (add_loft :part a_part :tool wp-set :keep_tool :no) :failure (progn (display-error nil t) (return-from extrude-with-draft))) ;do we keep the original wp or not (unless keep_wp (sd-call-cmds (delete_3d wp))) ) ) (create-equidistance-loop (edge-list) (let* ( (s-elem *START-ELEM*) (e-elem *END-ELEM*) (offset (* distance (sin draft))) (eg (sd-inq-edge-geo s-elem :dest-space WP)) (start-coord (sd-gpnt3d-to-2d (sd-edge-start-pnt eg))) (half-p (/ (+ (sd-edge-s-end eg) (sd-edge-s-start eg)) 2.0)) (mid-coord1 (sd-gpnt3d-to-2d (getf (sd-inq-edge-pnt s-elem :s half-p :coordinates t :dest-space wp) :coordinates))) mid-coord2 normal tangent offset-vec geo-data coord-on-equid equid-elem) ;use old start-elem to find coordinates and select this start-elem on cp-wp (setq s-elem (sd-call-cmds (get_selection :focus_type *sd-edge-2d-seltype* :curr_wp_only :select mid-coord1))) ;find coordinate info for end element (setq eg (sd-inq-edge-geo e-elem :dest-space WP)) (setq half-p (/ (+ (sd-edge-s-end eg) (sd-edge-s-start eg)) 2.0)) (setq geo-data (sd-inq-edge-pnt e-elem :s half-p :coordinates t :tangent t :dest-space wp)) (setq mid-coord2 (sd-gpnt3d-to-2d (getf geo-data :coordinates))) ;calculate catch pt for offsetted end-element (setq tangent (sd-gpnt3d-to-2d (getf geo-data :tangent))) (setq normal (sd-gpnt3d-to-2d (sd-vec-cross-product tangent 0,0,1))) (setq offset-vec (sd-vec-scale normal (abs offset))) (setq coord-on-equid (sd-vec-add mid-coord2 offset-vec)) ;create equdistance contour (sd-call-cmds (equidistance_2d :construction :off :intersections :off :distance offset start-coord s-elem mid-coord1 :side coord-on-equid) :failure (progn (display-error "Failed offset:" t) (return-from create-equidistance-loop nil))) ;calculate match-coord (setq equid-elem (sd-call-cmds (get_selection :focus_type *sd-edge-2d-seltype* :curr_wp_only :select (if (plusp offset) coord-on-equid (sd-vec-subtract mid-coord2 offset-vec))))) ;check if we got an element (unless equid-elem (display-error "Catching of offset element failed" t) (return-from create-equidistance-loop nil) ) ;pick the end of this element that is closests to original (let ((len1) (len2)) ;find the corresponding element on equidistant profile to start-elem (setq eg (sd-inq-edge-geo (first equid-elem) :dest-space wp)) (setq len1 (sd-vec-length (sd-vec-subtract start-coord (sd-edge-start-pnt eg)))) (setq len2 (sd-vec-length (sd-vec-subtract start-coord (sd-edge-end-pnt eg)))) (if (> len2 len1) (setq coord-on-equid (sd-gpnt3d-to-2d (sd-edge-start-pnt eg))) (setq coord-on-equid (sd-gpnt3d-to-2d (sd-edge-end-pnt eg)))) ) (values start-coord coord-on-equid) ) ) (create-draft-wp-set () (let (dist (draft-wp "draft-wp") (cp-wp "cp-wp") (wp-set-name (sd-gen-obj-basename :wpset)) (wp-set nil)) ;create a new workplane set (sd-call-cmds (create_wpset :name wp-set-name)) (setq wp-set (sd-pathname-to-obj (format nil "/~A" wp-set-name))) ;create a shared copy of the original (sd-call-cmds (create_workplane :share :source WP :name cp-wp :owner wp-set :par_wp :ref_wp WP :offset 0)) (setq cp-wp (sd-inq-curr-wp)) ;create new offset workplane (if (plusp (sd-vec-scalar-product direction (sd-inq-wp-w-dir wp))) (setq dist distance) (setq dist (- distance))) (sd-call-cmds (create_workplane :new :name draft-wp :owner wp-set :par_wp :ref_wp wp :offset dist)) (setq draft-wp (sd-inq-curr-wp)) (values wp-set draft-wp cp-wp) ) ) (create-match-pt (match-coord match-wp) ;change wp to cp-wp (sd-call-cmds (current_wp match-wp)) (sd-call-cmds (get_selection :focus_type *sd-match-vertex-2d-seltype* :curr_wp_only :select match-coord)) ) ) ) ;