Jump to content

Automatic numbering in air flow order


Jozef13

Recommended Posts

;;This
(defun getlast (mylist) (nth (- (length mylist) 1) mylist))
;; Could be
(defun getlast (mylist) (car (reverse mylist)))

 

Or even:

(last mylist)

;)

Link to comment
Share on other sites

  • Replies 31
  • Created
  • Last Reply

Top Posters In This Topic

  • Jozef13

    14

  • ronjonp

    8

  • Emmanuel Delay

    6

  • Lee Mac

    3

This is probably somewhere in the middle -

(defun 3d-coord->2d-pt-lst ( lst / rtn )
   (repeat (/ (length lst) 3)
       (setq rtn (cons (list (car lst) (cadr lst)) rtn)
             lst (cdddr lst)
       )
   )
   (reverse rtn)
)

Link to comment
Share on other sites

This is probably somewhere in the middle -

(defun 3d-coord->2d-pt-lst ( lst / rtn )
   (repeat (/ (length lst) 3)
       (setq rtn (cons (list (car lst) (cadr lst)) rtn)
             lst (cdddr lst)
       )
   )
   (reverse rtn)
)

 

List of 3888:

3888 Benchmarking .............Elapsed milliseconds / relative speed for 1024 iteration(s):

 

(3D-COORD->TO2DPT-LST L).......1656 / 34.30

(3D-COORD->PT-LSTRJP L)........1922 / 29.55

(3D-COORD->2D-PT-LSTLM L)......2000 / 28.40

(3D-COORD->PT-LST L)..........56797 / 1.00

_1$

 

Also changed my previous code from:

(defun 3d-coord->pt-lstrjp (lst / r)
 (while lst (setq r (cons (list (list (car lst) (cadr lst))) r)) (setq lst (cdddr lst)))
 (apply 'append (reverse r))
)
To
(defun 3d-coord->pt-lstrjp (lst / r)
 (while lst (setq r (cons (list (car lst) (cadr lst)) r)) (setq lst (cdddr lst)))
 (reverse r)
)

:oops:

 

And after that change:

Benchmarking .............Elapsed milliseconds / relative speed for 1024 iteration(s):

 

(3D-COORD->PT-LSTRJP L)........1281 / 43.97

(3D-COORD->TO2DPT-LST L).......1437 / 39.20

(3D-COORD->2D-PT-LSTLM L)......1844 / 30.55

(3D-COORD->PT-LST L)..........56328 / 1.00

_$

Link to comment
Share on other sites

I fixed the 2D polyline problem. It's an old style of data; each point also stores a z-coordinate (completely useless, since polylines are flat, so you only need 1 z-coordinate) with a function that will not win a beauty prize.

 

I put the Text on a point along the polyline (I need such point anyway, to insert the block)

 

I tested it on your Test1.dwg . It works fine, I think.

 

;; @file 
;;  - Detect a tree structure
;;  - Number the branches
;;  (- add stuff to attributes, not really included in this file)
;; @see http://www.cadtutor.net/forum/showthread.php?104740-Automatic-numbering-in-air-flow-order

;; settings
(setq textheight 50)
(setq zerodistance 0.000001)
         
(defun Text (pt hgt str)
 (entmakex (list (cons 0 "TEXT")
                 (cons 10  pt)
                 (cons 40 hgt)
                 (cons 1  str))))
                 


;;; 2d-coord->pt-lst
;; Converts a 2d coordinates flat list into a 2d point list
;;; (2d-coord->pt-lst '(1.0 2.0 3.0 4.0)) -> ((1.0 2.0) (3.0 4.0))
(defun 2d-coord->pt-lst (lst)
 (if lst
   (cons 
     (list (car lst) (cadr lst))
     (2d-coord->pt-lst (cddr lst))
   )
 )
)

;; no doubt this can be written more elegantly.
;; this does what 2d-coord->pt-lst does, except every third coordinate is ignored
(defun 3d-coord->pt-lst (lst / i result)
 (setq 
   i 0
   result (list)
 )
 (repeat (fix (/ (length lst) 3 ))
   (setq result (append result (list (list (nth (* 3 i) lst) (nth (+ 1 (* 3 i)) lst)) )))
   (setq i (+ i 1))
 )
 result
)


(defun getLast (mylist)
 (nth (- (length mylist) 1) mylist)
)

(setq indexHandled (list))
(defun BTS (ss pt0 / i pts d1 d2 mypoint mystring lst)
 (setq i 0)
 (repeat (sslength ss)
     (setq pl (vlax-ename->vla-object (ssname ss i)))

     ;; get its vertices list
     (setq lst (vlax-get pl 'coordinates))

     (if (= "AcDb2dPolyline" (vla-get-ObjectName pl))
       (setq pts (3d-coord->pt-lst lst))
       (setq pts (2d-coord->pt-lst lst))
     )

     (setq d1 (distance (setq mypoint (nth 0 pts)) pt0) )
     (setq d2 (distance (setq mypoint (getLast pts)) pt0) )
     
     ;; counting ascending
     ;;(setq mystring (rtos (length indexHandled) 2 0))
     ;; counting descending
     (setq mystring (rtos (- (sslength ss) (length indexHandled)) 2 0))
   
   (if (not (member i indexHandled)) (progn
     (if (< d1 zerodistance) (progn
       (setq indexHandled (append indexHandled (list i)))
       
       ;; the Text is put on a point on the polyline.  Somewhere in the middle, but not exactly
       (Text (vlax-curve-getPointAtDist pl (/ d2 2)) textheight mystring)
       
       (bts ss (getLast pts))
     ))
     (if (< d2 zerodistance) (progn
         (setq indexHandled (append indexHandled (list i)))
       ;;(Text mypoint textheight mystring)
       (Text (vlax-curve-getPointAtDist pl (/ d1 2)) textheight mystring)
       (bts ss (nth 0 pts))
     ))
   ))
   (setq i (+ i 1 ))
 )
)

(defun c:BTS ( / ss index trunk pt0)
 (setq indexHandled (list))  ;; reset which branches have been handled yet
 (setq ss (ssget ":L" '((0 . "LWPOLYLINE,POLYLINE"))))
 (setq pt0 (osnap (getpoint "\nSelect the trunk <osnap on the base of the trunk>: ") "_end"))
 (BTS ss pt0)
 (princ)
)

 

Thank you, it works very well with both polyline and 2d polyline as I tested on my larger scheme (in attachment).Test-3.dwg

Link to comment
Share on other sites

And after that change:

 

I'm quite surprised that the while loop performing the same operations would be significantly faster than repeat, given that the test expression is evaluated for each iteration of a while loop whereas the loop control is evaluated only once for repeat... perhaps compiling & optimising tilts the balance. :huh:

Link to comment
Share on other sites

Now it inserts the block dn_cu, and fills in attribute CU.

Just the 1 number, not the "number - root-number"

 

I substituted a few functions, seeing the suggestions here above

 

;; @file 
;;  - Detect a tree structure
;;  - Number the branches
;;  (- add stuff to attributes, not really included in this file)
;; @see http://www.cadtutor.net/forum/showthread.php?104740-Automatic-numbering-in-air-flow-order

;; settings
(setq textheight 50)
(setq zerodistance 0.000001)
         
(defun Text (pt hgt str)
 (entmakex (list (cons 0 "TEXT")
                 (cons 10  pt)
                 (cons 40 hgt)
                 (cons 1  str))))
                 

;; @see http://www.lee-mac.com/attributefunctions.html#vlsetattributevalue
(defun LM:vl-setattributevalue ( blk tag val )
   (setq tag (strcase tag))
   (vl-some
      '(lambda ( att )
           (if (= tag (strcase (vla-get-tagstring att)))
               (progn (vla-put-textstring att val) val)
           )
       )
       (vlax-invoke blk 'getattributes)
   )
)

;; insert block
(defun ib ( blk ins / doc)
 (setq doc (vla-get-activedocument (vlax-get-acad-object)))
    ;; (= (strcase dwg) (strcase (vla-get-fullname (setq doc (vla-get-activedocument (vlax-get-acad-object))))))
 (vla-insertblock
   (vlax-get-property doc (if (= 1 (getvar 'cvport)) 'paperspace 'modelspace))
   (vlax-3D-point (trans ins 1 0))
   blk
   1.0 1.0 1.0
   (angle '(0.0 0.0) (trans (getvar 'ucsxdir) 0 (trans '(0.0 0.0 1.0) 1 0 t) t))
 )
)            

;;; 2d-coord->pt-lst
;; Converts a 2d coordinates flat list into a 2d point list
;;; (2d-coord->pt-lst '(1.0 2.0 3.0 4.0)) -> ((1.0 2.0) (3.0 4.0))
(defun 2d-coord->pt-lst (lst)
 (if lst
   (cons 
     (list (car lst) (cadr lst))
     (2d-coord->pt-lst (cddr lst))
   )
 )
)

;; this does what 2d-coord->pt-lst does, except every third coordinate is ignored
;; @see http://www.cadtutor.net/forum/showthread.php?104740-Automatic-numbering-in-air-flow-order/page3
(defun 3d-coord->pt-lstrjp (lst / r)
 (while lst (setq r (cons (list (car lst) (cadr lst)) r)) (setq lst (cdddr lst)))
 (reverse r)
)

(setq indexHandled (list))
(defun BTS (ss pt0 / i pts d1 d2 mypoint mystring lst blk ip)
 (setq i 0)
 (repeat (sslength ss)
     (setq pl (vlax-ename->vla-object (ssname ss i)))

     ;; get its vertices list
     (setq lst (vlax-get pl 'coordinates))

     (if (= "AcDb2dPolyline" (vla-get-ObjectName pl))
       (setq pts (3d-coord->pt-lstrjp lst))
       (setq pts (2d-coord->pt-lst lst))
     )

     (setq d1 (distance (setq mypoint (nth 0 pts)) pt0) )
     (setq d2 (distance (setq mypoint (last pts)) pt0) )
     
     ;; counting ascending
     ;;(setq mystring (rtos (length indexHandled) 2 0))
     ;; counting descending
     (setq mystring (rtos (- (sslength ss) (length indexHandled)) 2 0))
   
   (if (not (member i indexHandled)) (progn
     (if (< d1 zerodistance) (progn
       (setq indexHandled (append indexHandled (list i)))
       
       ;; the Text is put on a point on the polyline.  Somewhere in the middle, but not exactly
       ;;(Text (vlax-curve-getPointAtDist pl (/ d2 2)) textheight mystring)
       (setq ip (vlax-curve-getPointAtDist pl (/ d2 2)))
       (setq blk (ib "dn_cu" ip))
       (LM:vl-setattributevalue
         blk
         "CU"
         mystring
       )
       
       (bts ss (last pts))
     ))
     (if (< d2 zerodistance) (progn
         (setq indexHandled (append indexHandled (list i)))
       ;;(Text (vlax-curve-getPointAtDist pl (/ d1 2)) textheight mystring)
       (setq ip (vlax-curve-getPointAtDist pl (/ d1 2)))
       (setq blk (ib "dn_cu" ip))
       (LM:vl-setattributevalue
         blk
         "CU"
         mystring
       )
       
       (bts ss (nth 0 pts))
     ))
   ))
   (setq i (+ i 1 ))
 )
)

(defun c:BTS ( / ss index trunk pt0)
 (setq indexHandled (list))  ;; reset which branches have been handled yet
 (setq ss (ssget ":L" '((0 . "LWPOLYLINE,POLYLINE"))))
 (setq pt0 (osnap (getpoint "\nSelect the trunk <osnap on the base of the trunk>: ") "_end"))
 (BTS ss pt0)
 (princ)
)

Edited by Emmanuel Delay
Link to comment
Share on other sites

Now it inserts the block dn_cu, and fills in attribute CU.

Just the 1 number, not the "number - root-number"

 

I substituted a few functions, seeing the suggestions here above

 

;; @file 
;;  - Detect a tree structure
;;  - Number the branches
;;  (- add stuff to attributes, not really included in this file)
;; @see http://www.cadtutor.net/forum/showthread.php?104740-Automatic-numbering-in-air-flow-order

;; settings
(setq textheight 50)
(setq zerodistance 0.000001)
         
(defun Text (pt hgt str)
 (entmakex (list (cons 0 "TEXT")
                 (cons 10  pt)
                 (cons 40 hgt)
                 (cons 1  str))))
                 

;; @see http://www.lee-mac.com/attributefunctions.html#vlsetattributevalue
(defun LM:vl-setattributevalue ( blk tag val )
   (setq tag (strcase tag))
   (vl-some
      '(lambda ( att )
           (if (= tag (strcase (vla-get-tagstring att)))
               (progn (vla-put-textstring att val) val)
           )
       )
       (vlax-invoke blk 'getattributes)
   )
)

;; insert block
(defun ib ( blk ins / doc)
 (setq doc (vla-get-activedocument (vlax-get-acad-object)))
    ;; (= (strcase dwg) (strcase (vla-get-fullname (setq doc (vla-get-activedocument (vlax-get-acad-object))))))
 (vla-insertblock
   (vlax-get-property doc (if (= 1 (getvar 'cvport)) 'paperspace 'modelspace))
   (vlax-3D-point (trans ins 1 0))
   blk
   1.0 1.0 1.0
   (angle '(0.0 0.0) (trans (getvar 'ucsxdir) 0 (trans '(0.0 0.0 1.0) 1 0 t) t))
 )
)            

;;; 2d-coord->pt-lst
;; Converts a 2d coordinates flat list into a 2d point list
;;; (2d-coord->pt-lst '(1.0 2.0 3.0 4.0)) -> ((1.0 2.0) (3.0 4.0))
(defun 2d-coord->pt-lst (lst)
 (if lst
   (cons 
     (list (car lst) (cadr lst))
     (2d-coord->pt-lst (cddr lst))
   )
 )
)

;; this does what 2d-coord->pt-lst does, except every third coordinate is ignored
;; @see http://www.cadtutor.net/forum/showthread.php?104740-Automatic-numbering-in-air-flow-order/page3
(defun 3d-coord->pt-lstrjp (lst / r)
 (while lst (setq r (cons (list (car lst) (cadr lst)) r)) (setq lst (cdddr lst)))
 (reverse r)
)

(setq indexHandled (list))
(defun BTS (ss pt0 / i pts d1 d2 mypoint mystring lst blk ip)
 (setq i 0)
 (repeat (sslength ss)
     (setq pl (vlax-ename->vla-object (ssname ss i)))

     ;; get its vertices list
     (setq lst (vlax-get pl 'coordinates))

     (if (= "AcDb2dPolyline" (vla-get-ObjectName pl))
       (setq pts (3d-coord->pt-lstrjp lst))
       (setq pts (2d-coord->pt-lst lst))
     )

     (setq d1 (distance (setq mypoint (nth 0 pts)) pt0) )
     (setq d2 (distance (setq mypoint (last pts)) pt0) )
     
     ;; counting ascending
     ;;(setq mystring (rtos (length indexHandled) 2 0))
     ;; counting descending
     (setq mystring (rtos (- (sslength ss) (length indexHandled)) 2 0))
   
   (if (not (member i indexHandled)) (progn
     (if (< d1 zerodistance) (progn
       (setq indexHandled (append indexHandled (list i)))
       
       ;; the Text is put on a point on the polyline.  Somewhere in the middle, but not exactly
       ;;(Text (vlax-curve-getPointAtDist pl (/ d2 2)) textheight mystring)
       (setq ip (vlax-curve-getPointAtDist pl (/ d2 2)))
       (setq blk (ib "dn_cu" ip))
       (LM:vl-setattributevalue
         blk
         "CU"
         mystring
       )
       
       (bts ss (last pts))
     ))
     (if (< d2 zerodistance) (progn
         (setq indexHandled (append indexHandled (list i)))
       ;;(Text (vlax-curve-getPointAtDist pl (/ d1 2)) textheight mystring)
       (setq ip (vlax-curve-getPointAtDist pl (/ d1 2)))
       (setq blk (ib "dn_cu" ip))
       (LM:vl-setattributevalue
         blk
         "CU"
         mystring
       )
       
       (bts ss (nth 0 pts))
     ))
   ))
   (setq i (+ i 1 ))
 )
)

(defun c:BTS ( / ss index trunk pt0)
 (setq indexHandled (list))  ;; reset which branches have been handled yet
 (setq ss (ssget ":L" '((0 . "LWPOLYLINE,POLYLINE"))))
 (setq pt0 (osnap (getpoint "\nSelect the trunk <osnap on the base of the trunk>: ") "_end"))
 (BTS ss pt0)
 (princ)
)

 

Thank you very much,

I have also tried to modify your code to insert block instead of text but it was working very, very slow.

Your code works super fast. Thank you and people above again.

Is it posible to modify it to rotate blocks perpendicular to polyline segment ?

And one next, could you modify it to put the number 1 to the furthermost branch ?

It is not necesary but I use to do it in that way. Do not loose your time if it is difficult.

Link to comment
Share on other sites

Rotating should work.

 

The other request is much more tricky. We see patterns in a glance that computers don't see.

What my algorithm sees: it looks at the other side of the branch, then it only sees nodes and branches. One branch goes left, the other goes right (or whatever direction, it doesn't look at the direction)... Then the same function is set loose on the first branch, then the second, ... same for each branch within the branch...

Recursive functions work backwards. (very often) The first result they know of is the last that they will return. Decisions are being made at the branching, not at the end node.

 

Think of a windows browser/explorer, or search function. It will be done on the basis of your folders hierarchy. Not on the depth of the subfolders, not on the files, ...

 

 

Maybe somebody else has an idea on how to approach this??

Edited by Emmanuel Delay
Link to comment
Share on other sites

Rotating should work.

 

The other request is much more tricky. We see patterns in a glance that computers don't see.

What my algorithm sees: it looks at the other side of the branch, then it only sees nodes and branches. One branch goes left, the other goes right (or whatever direction, it doesn't look at the direction)... Then the same function is set loose on the first branch, then the second, ... same for each branch within the branch...

Recursive functions work backwards. (very often) The first result they know of is the last that they will return. Decisions are being made at the branching, not at the end node.

 

Think of a windows browser/explorer, or search function. It will be done on the basis of your folders hierarchy. Not on the depth of the subfolders, not on the files, ...

 

 

Maybe somebody else has an idea on how to approach this??

Thank you.

Could you upload the code with rotating ?

Well, it looks difficult to put the number 1 to the furthermost branch.

I was thinking of measuring length of each branch step by step from end (or start) to the trunk to obtain total length of route then sort it descending and then number it in that order. Anyway I would like to place each polyline (branch) length to the attribute "L" in next step.

But as I told that is not necesary or important for my next calculations. It is just visual question.

Link to comment
Share on other sites

  • 3 weeks later...

I have modified the code to rotate block perpendicular to polyline segment.

I hope I didn't make a weak chain link in processing :unsure:

Therere is still second part to do, to add "root number" to "CU" attribute to obtain "number - root number" which represent flow from branch - to branch in direction from end into trunk.

But unfortunately I have no idea (or rather skills) how to process it.:cry:

;;;JL: blocks rotating added

;; @file 
;;  - Detect a tree structure
;;  - Number the branches
;;  (- add stuff to attributes, not really included in this file)
;; @see [url]http://www.cadtutor.net/forum/showthread.php?104740-Automatic-numbering-in-air-flow-order[/url]

;; settings
(setq textheight 50)
(setq zerodistance 0.000001)
         
(defun Text (pt hgt str)
 (entmakex (list (cons 0 "TEXT")
                 (cons 10  pt)
                 (cons 40 hgt)
                 (cons 1  str))))
                 

;; @see [url]http://www.lee-mac.com/attributefunctions.html#vlsetattributevalue[/url]
(defun LM:vl-setattributevalue ( blk tag val )
   (setq tag (strcase tag))
   (vl-some
      '(lambda ( att )
           (if (= tag (strcase (vla-get-tagstring att)))
               (progn (vla-put-textstring att val) val)
           )
       )
       (vlax-invoke blk 'getattributes)
   )
)

;; insert block
(defun ib (blk ins [color="red"]rot[/color] / doc)	;;;Modified by JL
 (setq doc (vla-get-activedocument (vlax-get-acad-object)))
    ;; (= (strcase dwg) (strcase (vla-get-fullname (setq doc (vla-get-activedocument (vlax-get-acad-object))))))
 (vla-insertblock
   (vlax-get-property doc (if (= 1 (getvar 'cvport)) 'paperspace 'modelspace))
   (vlax-3D-point (trans ins 1 0))
   blk
   1.0 1.0 1.0
;;;    (angle '(0.0 0.0) (trans (getvar 'ucsxdir) 0 (trans '(0.0 0.0 1.0) 1 0 t) t))
   [color="red"](+ rot (dtr 90) (angle '(0.0 0.0) (trans (getvar 'ucsxdir) 0 (trans '(0.0 0.0 1.0) 1 0 t) t)))	;;;Modified by JL[/color]
 )
)            

;;; 2d-coord->pt-lst
;; Converts a 2d coordinates flat list into a 2d point list
;;; (2d-coord->pt-lst '(1.0 2.0 3.0 4.0)) -> ((1.0 2.0) (3.0 4.0))
(defun 2d-coord->pt-lst (lst)
 (if lst
   (cons 
     (list (car lst) (cadr lst))
     (2d-coord->pt-lst (cddr lst))
   )
 )
)

;; this does what 2d-coord->pt-lst does, except every third coordinate is ignored
;; @see [url]http://www.cadtutor.net/forum/showthread.php?104740-Automatic-numbering-in-air-flow-order/page3[/url]
(defun 3d-coord->pt-lstrjp (lst / r)
 (while lst (setq r (cons (list (car lst) (cadr lst)) r)) (setq lst (cdddr lst)))
 (reverse r)
)

(setq indexHandled (list))
(defun BTS (ss pt0 / i pts d1 d2 mypoint mystring lst blk ip ang)
 (setq i 0)
 (repeat (sslength ss)
     (setq pl (vlax-ename->vla-object (ssname ss i)))

     ;; get its vertices list
     (setq lst (vlax-get pl 'coordinates))

     (if (= "AcDb2dPolyline" (vla-get-ObjectName pl))
       (setq pts (3d-coord->pt-lstrjp lst))
       (setq pts (2d-coord->pt-lst lst))
     )

     (setq d1 (distance (setq mypoint (nth 0 pts)) pt0) )
     (setq d2 (distance (setq mypoint (last pts)) pt0) )
     
     ;; counting ascending
     ;;(setq mystring (rtos (length indexHandled) 2 0))
     ;; counting descending
     (setq mystring (rtos (- (sslength ss) (length indexHandled)) 2 0))
   
   (if (not (member i indexHandled)) (progn
     (if (< d1 zerodistance) (progn
       (setq indexHandled (append indexHandled (list i)))
       
       ;; the Text is put on a point on the polyline.  Somewhere in the middle, but not exactly
       ;;(Text (vlax-curve-getPointAtDist pl (/ d2 2)) textheight mystring)
       (setq ip (vlax-curve-getPointAtDist pl (/ d2 2)))
[color="red"](setq ang  (angle '(0 0 0) (vlax-curve-getFirstDeriv pl (vlax-curve-getParamAtPoint pl ip))))	;;;Added by JL[/color]
       (setq blk (ib "dn_cu" ip ang))
       (LM:vl-setattributevalue
         blk
         "CU"
         mystring
       )
       
       (bts ss (last pts))
     ))
     (if (< d2 zerodistance) (progn
         (setq indexHandled (append indexHandled (list i)))
       ;;(Text (vlax-curve-getPointAtDist pl (/ d1 2)) textheight mystring)
       (setq ip (vlax-curve-getPointAtDist pl (/ d1 2)))
[color="red"](setq ang  (angle '(0 0 0) (vlax-curve-getFirstDeriv pl (vlax-curve-getParamAtPoint pl ip))))	;;;Added by JL[/color]
       (setq blk (ib "dn_cu" ip ang))
       (LM:vl-setattributevalue
         blk
         "CU"
         mystring
       )
       
       (bts ss (nth 0 pts))
     ))
   ))
   (setq i (+ i 1 ))
 )
)

(defun c:BTS ( / ss index trunk pt0)
 (setq indexHandled (list))  ;; reset which branches have been handled yet
 (setq ss (ssget ":L" '((0 . "LWPOLYLINE,POLYLINE"))))
 (setq pt0 (osnap (getpoint "\nSelect the trunk <osnap on the base of the trunk>: ") "_end"))
 (BTS ss pt0)
 (princ)
)

[color="red"];;;*** Degree To Radian
(defun dtr (a)
 (* pi (/ a 180.0))
)
;;;*** Radian To Degree
(defun rtd (a)
 (/ (* a 180.0) pi)
)[/color]

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...