Need help with stacking fractions in lisp

Need help with stacking fractions in lisp

cclinton
Contributor Contributor
429 Views
9 Replies
Message 1 of 10

Need help with stacking fractions in lisp

cclinton
Contributor
Contributor

I posted something similar a while back, but I could never get it to work the way I wanted, so I had Gemini write me a lisp routine to calculate glass size from a window. I pick two points to define a rectangle, add a user defined dimension and an angle of rotation, and the routine inserts the value as text in the center of the rectangle. So far, the routine works great, and I get nearly the result I want. However I wish it could diagonally stack the fractions similar to our office standards. I understand that the routine just uses TEXT, no MTEXT, so no stacked fractions, but as I understand it, it is much more difficult to use MTEXT with stacked fractions in a routine. Is it possible for one of you awesome users to look at the code and suggest a way to make this work with stacked fractions? Thanks in advance for all your help.

;; GS - Creates single-line Text with calculated width and height.
;;
;; This routine prompts the user for two points to define a rectangle
;; and then an additional distance. It now also prompts for a rotation
;; angle. It creates a Text entity at the center of the rectangle,
;; displaying the calculated dimensions with the specified rotation.
;;
(defun C:GS ( / pt1 pt2 width height ext_dist final_width final_height
                           display_width_string display_height_string mid_pt rotation_angle text_rot_angle
                           old_aunits)
  (vl-load-com) ; Loads Visual Lisp extensions

  ;; Save the current angular units to restore later
  (setq old_aunits (getvar "aunits"))

  ;; Temporarily set angular units to degrees (0) for user input
  (setvar "aunits" 0)

  ;; 1. Get the two points to define the virtual rectangle
  (setq pt1 (getpoint "\nSelect first corner of rectangle: "))
  (setq pt2 (getcorner pt1 "\nSelect opposite corner: "))

  ;; 2. Calculate the width and height of the rectangle
  (setq width (abs (- (car pt1) (car pt2))))
  (setq height (abs (- (cadr pt1) (cadr pt2))))

  ;; 3. Get the user-defined distance to add
  (setq ext_dist (getdist "\nSpecify distance to add to width and height: "))

  ;; 4. Add the distance to the width and height
  (setq final_width (+ width ext_dist))
  (setq final_height (+ height ext_dist))

  ;; 5. Get the rotation angle from the user using GETREAL
  (setq rotation_angle (getreal "\nSpecify text rotation angle in degrees: "))

  ;; 6. Convert the final dimensions to a string with fractions using a custom function
  (setq display_width_string (GS:rtos-frac final_width))
  (setq display_height_string (GS:rtos-frac final_height))

  ;; 7. Construct the full Text content string, now with inch marks
  (setq text_string (strcat display_width_string "\"" " x " display_height_string "\""))

  ;; 8. Calculate the midpoint of the virtual rectangle for Text placement
  (setq mid_pt (list (+ (car pt1) (/ (- (car pt2) (car pt1)) 2.0))
                     (+ (cadr pt1) (/ (- (cadr pt2) (cadr pt1)) 2.0))
                     0.0
                   )
  )

  ;; 9. Create the Text entity using the command function
  ;; Pass the rotation angle directly. It will be interpreted correctly in degrees.
  (command "_.text" "_J" "_MC" mid_pt 2.25 rotation_angle text_string)
  
  ;; Restore the original angular units
  (setvar "aunits" old_aunits)

  (princ) ; Clean exit
)

;; A helper function to convert a real number to a string with reduced fractions.
;; This function does not use any system variables and is fully self-contained.
(defun GS:rtos-frac (num / int_part frac_part numerator denominator common_divisor)
  (setq int_part (fix num))
  (setq frac_part (- num int_part))
  (if (>= frac_part 0.001) ; Check if there is a fractional part
    (progn
      (setq denominator 16) ; Use a common denominator, e.g., 16ths
      (setq numerator (GS:round-custom (* frac_part denominator)))
      (setq common_divisor (GS:gcd numerator denominator))
      (setq numerator (/ numerator common_divisor))
      (setq denominator (/ denominator common_divisor))
      (strcat
        (if (/= int_part 0) (itoa int_part) "") ; Add integer part if it's not 0
        " "
        (itoa numerator) "/" (itoa denominator)
      )
    )
    (itoa int_part) ; If no fractional part, just return the integer
  )
)

;; A helper function to calculate the Greatest Common Divisor (GCD) of two integers
(defun GS:gcd (a b)
  (if (= b 0)
    a
    (GS:gcd b (rem a b))
  )
)

;; Custom rounding function for older Lisp versions
(defun GS:round-custom (n)
  (+ (fix n) (if (>= (rem n 1) 0.5) 1 0))
)

 

0 Likes
Accepted solutions (2)
430 Views
9 Replies
Replies (9)
Message 2 of 10

CADaSchtroumpf
Advisor
Advisor

In your function (defun C:GS, replace:

  (command "_.text" "_J" "_MC" mid_pt 2.25 rotation_angle text_string)

by

 

(command "_.mtext" mid_pt "_J" "_MC" "_H" 2.25 "_R" rotation_angle "_W" 0.0 text_string "")

 and in your function (defun GS:rtos-frac, replace:

 

      (strcat
        (if (/= int_part 0) (itoa int_part) "") ; Add integer part if it's not 0
        " "
        (itoa numerator) "/" (itoa denominator)
      )

by

      (strcat
        "\\A1;"
        (if (/= int_part 0) (itoa int_part) "") ; Add integer part if it's not 0
        " {\\H0.7x;\\S"
        (itoa numerator)
        "/"
        (itoa denominator)
        ";}\""
      )
Message 3 of 10

cclinton
Contributor
Contributor

Thank you so much. It nearly works, just a few tweaks that I can't seem to find in the code:

Fractions should be diagonally stacked.

There is an extra " for the inch mark.

No space before or after the "x" between the dimensions.

Remove space between the whole number and the fraction.

0 Likes
Message 4 of 10

ayla74108
Observer
Observer

Hi cclinton,

It looks like your routine is working great for single-line text, but as you mentioned, TEXT in AutoLISP doesn’t support diagonal or stacked fractions like MTEXT does. To achieve properly stacked fractions, you’d need to switch from using _.text to creating an MTEXT object with _.mtext instead. MTEXT supports formatting codes, including \F for fractions, so you can format your strings to display stacked fractions.

A high-level approach would be:

Replace the command "_.text" call with command "_.mtext" and specify the insertion point.

Construct your text string with MTEXT formatting codes for stacked fractions, e.g., "\\F1/2;" to display ½.

Keep the midpoint calculation as you have it, so the MTEXT is centered.

You can also specify rotation with MTEXT via the rotation angle parameter.

It will take some adjustments in your GS:rtos-frac function to output MTEXT-compatible fraction codes instead of plain n/d strings. Once that’s done, your routine should be able to place diagonally stacked fractions automatically.

0 Likes
Message 5 of 10

CADaSchtroumpf
Advisor
Advisor
Accepted solution

OK, try with this changes:

  (setq text_string (strcat display_width_string "x" display_height_string))

and

      (strcat
        "\\A1;"
        (if (/= int_part 0) (itoa int_part) "") ; Add integer part if it's not 0
        "{\\H0.7x;\\S"
        (itoa numerator)
        "#"
        (itoa denominator)
        ";}\""
      )
Message 6 of 10

cclinton
Contributor
Contributor

Excellent! Thank you so much for all your help! Those code changes worked. I've been fighting this for a long time, so happy it works!

Here's the code for those who may be interested in this:

;; GS - Creates single-line Text with calculated width and height.
;;
;; This routine prompts the user for two points to define a rectangle
;; and then an additional distance. It now also prompts for a rotation
;; angle. It creates a Text entity at the center of the rectangle,
;; displaying the calculated dimensions with the specified rotation.
;;
(defun C:GS ( / pt1 pt2 width height ext_dist final_width final_height
                           display_width_string display_height_string mid_pt rotation_angle text_rot_angle
                           old_aunits)
  (vl-load-com) ; Loads Visual Lisp extensions

  ;; Save the current angular units to restore later
  (setq old_aunits (getvar "aunits"))

  ;; Temporarily set angular units to degrees (0) for user input
  (setvar "aunits" 0)

  ;; 1. Get the two points to define the virtual rectangle
  (setq pt1 (getpoint "\nSelect first corner of rectangle: "))
  (setq pt2 (getcorner pt1 "\nSelect opposite corner: "))

  ;; 2. Calculate the width and height of the rectangle
  (setq width (abs (- (car pt1) (car pt2))))
  (setq height (abs (- (cadr pt1) (cadr pt2))))

  ;; 3. Get the user-defined distance to add
  (setq ext_dist (getdist "\nSpecify distance to add to width and height: "))

  ;; 4. Add the distance to the width and height
  (setq final_width (+ width ext_dist))
  (setq final_height (+ height ext_dist))

  ;; 5. Get the rotation angle from the user using GETREAL
  (setq rotation_angle (getreal "\nSpecify text rotation angle in degrees: "))

  ;; 6. Convert the final dimensions to a string with fractions using a custom function
  (setq display_width_string (GS:rtos-frac final_width))
  (setq display_height_string (GS:rtos-frac final_height))

  ;; 7. Construct the full Text content string, now with inch marks
  (setq text_string (strcat display_width_string "x" display_height_string))

  ;; 8. Calculate the midpoint of the virtual rectangle for Text placement
  (setq mid_pt (list (+ (car pt1) (/ (- (car pt2) (car pt1)) 2.0))
                     (+ (cadr pt1) (/ (- (cadr pt2) (cadr pt1)) 2.0))
                     0.0
                   )
  )

  ;; 9. Create the Text entity using the command function
  ;; Pass the rotation angle directly. It will be interpreted correctly in degrees.
  (command "_.mtext" mid_pt "_J" "_MC" "_H" 2.25 "_R" rotation_angle "_W" 0.0 text_string "")
  
  ;; Restore the original angular units
  (setvar "aunits" old_aunits)

  (princ) ; Clean exit
)

;; A helper function to convert a real number to a string with reduced fractions.
;; This function does not use any system variables and is fully self-contained.
(defun GS:rtos-frac (num / int_part frac_part numerator denominator common_divisor)
  (setq int_part (fix num))
  (setq frac_part (- num int_part))
  (if (>= frac_part 0.001) ; Check if there is a fractional part
    (progn
      (setq denominator 16) ; Use a common denominator, e.g., 16ths
      (setq numerator (GS:round-custom (* frac_part denominator)))
      (setq common_divisor (GS:gcd numerator denominator))
      (setq numerator (/ numerator common_divisor))
      (setq denominator (/ denominator common_divisor))
      (strcat
        "\\A1;"
        (if (/= int_part 0) (itoa int_part) "") ; Add integer part if it's not 0
        "{\\H0.7x;\\S"
        (itoa numerator)
        "#"
        (itoa denominator)
        ";}\""
      )
    )
    (itoa int_part) ; If no fractional part, just return the integer
  )
)

;; A helper function to calculate the Greatest Common Divisor (GCD) of two integers
(defun GS:gcd (a b)
  (if (= b 0)
    a
    (GS:gcd b (rem a b))
  )
)

;; Custom rounding function for older Lisp versions
(defun GS:round-custom (n)
  (+ (fix n) (if (>= (rem n 1) 0.5) 1 0))
)
0 Likes
Message 7 of 10

cclinton
Contributor
Contributor

CADaSchtroumph, upon further use, I noticed a small error in the code and I can't figure out where to edit it. When the result of running the routine results in a whole number, the inch symbol is omitted. When there are fractions, it works great. Any thoughts on this? Thanks in advance.

;; GS - Creates single-line Text with calculated width and height.
;;
;; This routine prompts the user for two points to define a rectangle
;; and then an additional distance. It now also prompts for a rotation
;; angle. It creates a Text entity at the center of the rectangle,
;; displaying the calculated dimensions with the specified rotation.
;;
(defun C:GS ( / pt1 pt2 width height ext_dist final_width final_height
                           display_width_string display_height_string mid_pt rotation_angle text_rot_angle
                           old_aunits)
  (vl-load-com) ; Loads Visual Lisp extensions

  ;; Save the current angular units to restore later
  (setq old_aunits (getvar "aunits"))

  ;; Temporarily set angular units to degrees (0) for user input
  (setvar "aunits" 0)

  ;; 1. Get the two points to define the virtual rectangle
  (setq pt1 (getpoint "\nSelect first corner of rectangle: "))
  (setq pt2 (getcorner pt1 "\nSelect opposite corner: "))

  ;; 2. Calculate the width and height of the rectangle
  (setq width (abs (- (car pt1) (car pt2))))
  (setq height (abs (- (cadr pt1) (cadr pt2))))

  ;; 3. Get the user-defined distance to add
  (setq ext_dist (getdist "\nSpecify distance to add to width and height: "))

  ;; 4. Add the distance to the width and height
  (setq final_width (+ width ext_dist))
  (setq final_height (+ height ext_dist))

  ;; 5. Get the rotation angle from the user using GETREAL
  (setq rotation_angle (getreal "\nSpecify text rotation angle in degrees: "))

  ;; 6. Convert the final dimensions to a string with fractions using a custom function
  (setq display_width_string (GS:rtos-frac final_width))
  (setq display_height_string (GS:rtos-frac final_height))

  ;; 7. Construct the full Text content string, now with inch marks
  (setq text_string (strcat display_width_string "x" display_height_string))

  ;; 8. Calculate the midpoint of the virtual rectangle for Text placement
  (setq mid_pt (list (+ (car pt1) (/ (- (car pt2) (car pt1)) 2.0))
                     (+ (cadr pt1) (/ (- (cadr pt2) (cadr pt1)) 2.0))
                     0.0
                   )
  )

  ;; 9. Create the Text entity using the command function
  ;; Pass the rotation angle directly. It will be interpreted correctly in degrees.
  (command "_.mtext" mid_pt "_J" "_MC" "_H" 2.25 "_R" rotation_angle "_W" 0.0 text_string "")
  
  ;; Restore the original angular units
  (setvar "aunits" old_aunits)

  (princ) ; Clean exit
)

;; A helper function to convert a real number to a string with reduced fractions.
;; This function does not use any system variables and is fully self-contained.
(defun GS:rtos-frac (num / int_part frac_part numerator denominator common_divisor)
  (setq int_part (fix num))
  (setq frac_part (- num int_part))
  (if (>= frac_part 0.001) ; Check if there is a fractional part
    (progn
      (setq denominator 16) ; Use a common denominator, e.g., 16ths
      (setq numerator (GS:round-custom (* frac_part denominator)))
      (setq common_divisor (GS:gcd numerator denominator))
      (setq numerator (/ numerator common_divisor))
      (setq denominator (/ denominator common_divisor))
      (strcat
        "\\A1;"
        (if (/= int_part 0) (itoa int_part) "") ; Add integer part if it's not 0
        "{\\H0.7x;\\S"
        (itoa numerator)
        "#"
        (itoa denominator)
        ";}\""
      )
    )
    (itoa int_part) ; If no fractional part, just return the integer
  )
)

;; A helper function to calculate the Greatest Common Divisor (GCD) of two integers
(defun GS:gcd (a b)
  (if (= b 0)
    a
    (GS:gcd b (rem a b))
  )
)

;; Custom rounding function for older Lisp versions
(defun GS:round-custom (n)
  (+ (fix n) (if (>= (rem n 1) 0.5) 1 0))
)

0 Likes
Message 8 of 10

paullimapa
Mentor
Mentor
Accepted solution

change this line in the code:

   (itoa int_part) ; If no fractional part, just return the integer

to this line:

    (strcat (itoa int_part) "\"") ; If no fractional part, just return the integer

Paul Li
IT Specialist
@The Office
Apps & Publications | Video Demos
Message 9 of 10

cclinton
Contributor
Contributor

Perfect, thank you so much!

0 Likes
Message 10 of 10

paullimapa
Mentor
Mentor

you are welcome...cheers!!!


Paul Li
IT Specialist
@The Office
Apps & Publications | Video Demos
0 Likes