Rotate AutoCAD's UCS30 Apr, 2001 By: Tony Hotchkiss Cadalyst
Rowen Delaforce e-mailed a request to rotate the UCS (user coordinate system) by selecting an object even if that object is part of a block or xref. He wanted to include polyline segments and lines that may or may not be part of a block. The UCS rotation lets you place dimensions with the text facing from left to right. My solution is UCS-ROT.LSP, which automatically rotates the UCS after you pick a single object. The object must be a line, polyline, or lwpolyline. Any of these objects can be part of a block or xref, regardless of how deeply the object is nested in the block. I tested the program in AutoCAD 2000, but it should work in all versions back to Release 12 because I used only AutoLISP functions that appear in Release 12.
How to use UCS-ROT.LSP
|Figure 1. UCS-ROT.LSP asks you to select an object.|
Download UCS-ROT.LSP from Cadalyst's CAD Tips site and save it in your support directory for AutoCAD 2000. Load the program with AutoCAD's Tools|Application menu.
AutoCAD prompts you to enter UCR to start the program. Next, the prompt shown in figure 1 appears: Select a line or polyline.
The object snap is set automatically to Nearest for the selection. You should pick a line or polyline segment (even if it is part of a block); otherwise the routine displays: You must select a line or polyline, try again.
|Figure 2. Then the routine rotates the UCS icon.|
Once you select a suitable object, the UCS icon rotates as shown in figure 2.
If you select an arc segment of a polyline or lwpolyline, an alert box appears (figure 3), but the UCS still rotates to line up with the start and end points of the arc segment.
Rowen Delaforce had already discovered that it's relatively simple to do this job if you make two picks. Just pick the endpoints of any line or poly-line segment, then find the angle between those points. This works regardless of whether a block reference is involved.
Figure 3. The routine alerts you if you select an arc segment.
To solve the problem with only one user pick is considerably more involved, especially if you select lwpolylines. The old-style polylines are easy to handle if you use (nentselp) for the selection, because (nentselp) returns the entity type VERTEX.
The function (do-vertex) takes the selected vertex entity and uses (entnext) to find the next vertex. The required angle is simply the angle between these two vertices. If the selected vertex is the last one in the polyline, the next entity is the (sequend) entity. It's a simple matter to extract the name of the header polyline and use (entnext) on it to find the first vertex of the polyline. The code to do this is:
(setq en1 (nth 0 edata) ; search for next vertex
|v1 (dxf 10 en1)|
|en2 (entnext en1)|
|en2typ (dxf 0 en2)|
|) ;_ setq|
|(if (= en2typ "SEQEND")|
|) ;_ if|
|(setq bulg (dxf 42 en1))|
|(if (/= 0.0 bulg)|
|) ;_ if|
|(setq ang (angle v1 v2))|
Note that this code also checks for arc segments and notifies you of any found.
If the selected entity is part of a block, the routine finds the combined angle of all of the nested blocks and adds it to the entity angle. The (do-block) function finds this angle as follows:
|(defun do-block (edat / bnlist blen i insname ang)|
|(setq bnlist (nth 3 edat)|
|) ;_ setq|
|) ;_ repeat|
|) ;_ do-block|
The data returned by (nentselp) contains a list of all nested blocks that include the selected entity. That list is used in the above repeat loop to produce the total angle.
An lwpolyline is complicated to deal with in this context because the routine can't identify any single vertex as an entity. You must obtain a list of vertices and test each pair to determine that the pick point lies on the segment. This is further compli cated if the entity is part of a block. The data returned by (nentselp) contains the pick point and the matrix that relates the position and orientation of the nested lwpolyline entity with respect to the WCS (world coordinate system).
Use of that matrix is somewhat lengthy, so I used a simple translation and rotation to relate the picked point of the nested entity to that of the block table definition of the entity. The base point of blocks in the block table is at zero, so the translation distance is simply the distance between the block reference insertion point and zero. The picked point is at some angle alpha with respect to the block reference insertion point. The routine subtracts the block rotation angle from the alpha angle to place the picked point on the correct segment of the lwpolyline as defined in the block table.
The code thickens
Well, I warned you that this is a little complicated! After the routine establishes the translated and rotated point position, it tests each segment of the lwpolyline to see if the point lies on the segment. The function (test-segment), which tests both line and arc segments, is almost identical to the function of the same name that I wrote for a previous AutoLISP Solutions column that calculated the length of any individual lwpolyline segment. The only difference here is that the function returns the angle of the segment instead of its length.
I needed no special code to deal with external references, because they are blocks with a flag to denote the xref. Note that I used the world coordinate system to define the blocks. UCS-ROT.LSP starts by setting the UCS to world coordinates.
Send in the problems
As usual, if you have any comments or questions, please contact me. And keep those requests for custom AutoLISP code coming in.