Nate Holt's Blog

August 5, 2014

Toledo without water

Filed under: Electrical — nateholt @ 10:47 pm

dig_your_own_wellTotal city water shutdown this last weekend due to toxic algea bloom in Lake Erie near city’s water intake. Got me thinking. Decided to try to drill my own well after looking at a YouTube “how to”. Did it as an after dinner project this evening. Due to sandy soil, I was able to drill down 30 feet in about 90 minutes.  Used 2 inch PVC as the drill and casing. Went down 30 feet (that’s all the pipe I had… could have gone deeper). Then dropped 30 feet of 1.25″ PVC tied to a “well point” screen into the 2″ inch PVC. Poured some pea gravel down around the 2″ pipe, and then pulled the 2″ pipe out of the ground… leaving the 1.25 inch well pipe in place. Flat out fun!

I am waiting for arrival (probably next week) of an old-fashioned “pitcher pump” to attach to the 1.25″ pipe. We’ll see what happens and how good the “free” water is.

April 2, 2014

Zeke Holt

Filed under: Electrical — nateholt @ 11:37 pm

Zeke-Holt

 

 

 

 

 

 

 

 

 

 

Four-year-old Ezekiel Holt and his family have been living with us (wife and myself and Great-Grandpa Bill Holt) for the last seven months. At age two, their third child / my second grandson had developed a rare neurological disorder, Batten disease, for which there is no cure. Together, we helped support them as they cared for and loved Zeke during the last seven months of his life.

Various rememberances are given below.

God is good.

Grandpa Nate.

====

Zeke’s Christmas greeting to Grace Church (Toledo OH), December 22, 2013

Zeke_Nate_Christmas_Grace

 

 

 

 

 

 

Unless God intervenes, this could be Zeke’s last chance to offer Grace Church a Christmas greeting. Unfortunately, Zeke is a young man of few words. So Grandpa Nate is going to take a shot at filling in the gap.

God can take a small life lived on earth and “map” it to huge, wonderful, and totally unexpected things in eternity. Here on earth, while we grown-up mortals try to make sense of what has happened to Zeke… under the radar… lives are being touched, hearts are being softened, and plans re-ordered. What looks like a small, insignificant life is one that is going to have eternal significance.

Grandpa Nate’s hope is that, in heaven, a tall young man will walk up, perhaps with a bunch of other people in tow, reach out a strong steady hand, look me in the eye, and in a strong clear voice say “I thank God for the small life he laid out for me to live on earth. It was an honor. It was a privilege.”

====

Zeke’s Paint Brush Story – as told by Nate Holt

Funeral – Ezekiel Todd Holt – 29-March-2014

I am Grandpa Nate. My grandson is Zeke. I want to share the story of the moment I bonded with this young man.

Over the last couple years, I was making periodic trips from Toledo to Westerville (near Columbus) to help Andy and Breena upgrade their small home. A quick succession of four children popping up on the scene had maxed-out its capacity.

Zeke, child number 3, was beginning to exhibit issues of great concern, but the cause was not yet diagnosed.

During one of these Westerville work-visits, I was on my hands and knees in a corner, painting the lower part of a wall. Zeke scooted up. He didn’t stop at a respectful distance to watch me paint, he scooted right up into my work-zone of brush, bucket, and wall. Right in my face.

He reached for the brush as if to communicate that that he wanted to try it.

“Okay Zeke, let’s give it a shot.” I handed him the long handled brush. He dipped it into the bucket, just like he’d seen Grandpa Nate doing, and then slathered the paint on the wall, sorta randomly.

I saw potential in this young man. I took the brush from his hand and painted the outline of a small square shape on the wall.

“Zeke, since this is your very first time, let’s start small. Let’s paint and fill in this small box!” And together we did just that. He held the brush and dipped. I helped guide his hand to the target. Together we filled in the box.

He seemed to enjoy doing this, but I was in a bit of a hurry to get finished. I needed to head home at the end of the day, so I didn’t object when momma came, made a fuss of praising him, and then pulled him back away so I could finish.

But what happened next is what touched my heart and what sealed the deal between us.

As I continued to paint, crouched down low on elbows and knees, I suddenly felt two small arms reaching around me from behind. It was Zeke. He had scooted back. He wanted to somehow thank me. My bottom was the only part of me accessible to him at that point, so Zeke had wrapped his little arms around my bottom and just hugged.

The bond was complete. Zeke was now my Paint Buddy. And I wanted to be able to paint with him again.

Back in Toledo I bought a professional-grade, short stubby handled paint brush for Zeke.

IMG_0258

 

 

 

 

I kept it in my top dresser drawer until such time as Zeke’s health improved and we could paint again. And I planned ahead. On my own home project of building a cedar fence with Zeke’s great grandpa Bill, I left a small section of that 150-foot long fence unpainted. This was to be where Zeke, using his own / personal, professional-grade stubby paintbrush, would help Grandpa Nate and Great Grandpa Bill finish the job.

But Zeke’s health situation continued to deteriorate. The paintbrush I was saving for Zeke never left my dresser drawer. And I never got to paint with my Paint Buddy again.

But that brush is no longer in my dresser. It’s now in there [point to sealed casket]. It’s in Zeke’s tender little hand.

zeke_funeral_01

 

 

 

 

 

 

 

Zeke!… They say that Jesus is a carpenter.

Maybe he’ll have something that He’d like you and me to give a coat of paint…

We’ll be ready.

====

Video tribute here.

 

September 13, 2012

The last 20 minutes of the life of Miriam Grace Holt (Eulogy – mother)

Filed under: Electrical — nateholt @ 6:33 pm

Today my dad and my two brothers buried my mom. Here is my eulogy to this woman to whom I owe a huge debt of gratitude.

The last 20 minutes of the life of Miriam Grace Holt

Family members had spent a good part of last weekend in Miriam’s room. Though totally mute for many days now, she turned her gaze on me as I attempted to sing some simple songs to her, picking out the chords with difficulty on my ukulele.
I had no evidence that her body was processing any of this until I was leaving that first day [Friday] – as I stood over her to say goodbye, her frozen face flashed a grin. I reached out and put my hand on her head and said “Mom, I love you”.
But the steady deterioration was evident in the next visit [Saturday], and now, Sunday afternoon, the end surely had to be near.
The constant in all this was dad. He sat there by her side, holding her hand, speaking softly to her, reaching out and stroking her cheek, hoping to hear his name one last time.

Dad and family members spent the whole of that last Sunday afternoon in her room, intermittently in silence listening to her labored breathing, glancing at the golf tournament on the room’s TV, and holding her well-worn Bible in order to read Scripture to her in a shaky voice so close to tears.
There was little or no response from mom that afternoon, except for two times when mom suddenly raised her hand and held it to her forehead… as if to shade her eyes from the glare of a bright light.
By late afternoon we decided that dad needed rest. Though he protested, I drove him back to the house. Jill would consult with the hospice nurse and call us back if something changed.
That call came a few hours later at 9pm. Mom’s blood pressure had dropped and her gasps for breath were now much fainter. We drove back, sensing that this was our last time. Dad quickly was back at her side, holding her hand, speaking softly to her. We watched and listened in silence for the next two hours.
The last 20 minutes of mom’s life.
Now, after these initial two hours [about 11:20pm], her youngest son Dave had temporarily relieved dad. Dave was sitting at mom’s bedside, holding her hand.
I stood watch at the foot of her bed. Standing there and watching her weak breathing, I had a growing sense that the end was now at hand. I wanted to move quickly to her bedside and hold her other hand, but I was afraid that I would make a scene, that I wouldn’t be able to hold back the tears that were just waiting to burst forth, that this was just my imagination, just another false alarm.
I struggled with this over and over in my head. Finally, I just did it – I moved over to her bedside and knelt down. As Dave continued to hold her right hand, I place my hand over her left hand and leaned forward. I spoke softly into her ear.
“Mom… Dave is holding your hand and Nate is holding your other hand… if you’re ready, we can walk you home.”
My tears broke loose. I tried to whisper the 23rd Psalm into her ear but I’m sure I messed it up. I recited the words to the lullaby that she would sing to me each night as a child, in the darkness of my childhood bedroom as I would fall peacefully to sleep. There was no way that I could try to softly sing it into her ear now… I could barely say the words.
But her body was not ready to go home. Her breathing, though very faint, continued.
Dad came back and took Dave’s place. He took hold of her hand in both of his and held it to his face and wept quietly.
And now… the final five minutes of the life of Miriam Grace Holt.
Dad began to speak about the only girl he had ever loved. About the teachers they shared in grade school, the funny things that she had done… things that had captivated his young heart.
Mom’s eyes suddenly opened. Dad saw a tear forming. He reached out and touched that tear, and continued to express how she, as that young farm girl, had endeared herself to him. That he loved her so much.
I was still kneeling on the other side of her bed, listening to dad and watching mom’s shallow breathing. As dad continued to speak and hold her hand in his, mom’s breathing paused. Nothing. Then a faint breath. Then a much longer pause. And then one last breath. Then, no more.
She was safely home.
Though she loved her sons, her body wanted her husband Bill to hold her hand and to walk her home… walk her safely home to Jesus.
=================================================
I owe a huge debt of gratitude to this woman. I end with this poem.
Mom… thank you for…

Letting us boys play in the dirt,
And allowing us to rough-house,
For encouraging us to swing as high as we could get,
And trusting us to God… not stopping us taking risks.

Thank you for letting us jump off the roof and into the pool,
Riding bikes without helmets, and in cars with no seatbelts (how in the world did we survive?!!)
Thank you for stitching up our wounds with sewing needle and thread,
And for diagnosing our scraped knees and bloody fingers with the simple words: “You’ll live.”

Thank you for letting us build a seven-level tree-house (though not OSHA approved),
And for letting us climb trees all the way to the top,
For letting us boys go camping… spring, summer, dead of winter,
Spanking us when we deserved it, and praying for us a lot.

Thank you for making my school lunch sandwiches with delicious home-baked bread… (which I would then exchange for cash),
And that nickel for every Bible verse I worked at learning.
Thank you for teaching me the value of integrity, honesty, and accepting responsibility,
And for making us boys do our Saturday chores (I still love vacuuming).

Thank you for simple foods like meatloaf, casseroles, and weenie-beanies,
And for teaching us how to do without, the meaning of “Waste not, want not”.
Thank you for teaching me how to tithe,
And how to be generous, whether I had little or lot.

Thank you for dragging me to church when I hated to go
And for encouraging me to turn the TV off without a fight,
Thank you for sacrificing to send me to church camp every summer.
And as a child, thank you for singing that prayer song to me each night.

Thank you for attempting to raise us to be followers of Christ,
Thus extending the family faith to next generations
Not by just saying, but by doing
Through your prayers and encouragement and godly life lessons.

Thank you for teaching me the meaning of “You’re burning a hole in the day*”.
Turn the water on, then OFF… to rinse each dish… this was a childhood lesson,
And to save every penny, and to scrape every bit out of the jar,
Thanks for allowing me to personally re-experience the Great Depression.

Mom… we love you.

Nate Holt.
Reeb Funeral Home, Sylvania OH
September 13, 2012
 
* “Burning a hole in the day” = electric lights on in broad daylight ( ! )
 
============
 
Text of the funeral service, delivered by my son Andy, is here.
 
=============
Eulogy delivered by my daughter Rachel:
It’s been 25 minutes into this car ride and I’m starting to nod off in the back of my Dad’s minivan. Mom is in the passenger seat reading the newspaper and Andy got the captain’s chair (while I’m stuck in the back…) The next thing I know we are making that familiar turn onto the long driveway of a very familiar house. We’re in Gibsonburg, it’s most likely a holiday, and the family is getting together.Before I can even scramble out of the backseat, that sliding door is opening and out comes Grandma with a huge grin and a loud “Yoohoo!” ringing through the air. Grandpa is close behind greeting us with those familiar Holt side hugs.We walk through the breezeway, past the laundry machines with the Tupperware containers of cookies stacked ready for dessert, and into the bustling kitchen. Not surprising, Uncle Dave and Aunt Jill and crew are already there.Moving out of the kitchen I pass the piano but not without Grandma asking me to play a little something. More often than not, I refuse…I’m not sure why I would refuse, it made her so happy to hear me share her love for the piano.

The house is brimming with photos and memories, and it didn’t matter how often we visited – I still loved to look around. The piano had photos of all of us grandchildren. The bookcase had mementos from her past trips and children’s books I remember her reading us. The beat up Skipbo deck is set out on the card table. We all wonder if today we can finally beat Grandpa.

It’s time to eat. Time to do the dinner table dance – where we all hesitate around a few different spots until landing in a seat. My favorite was on the bench, but it was risky because those spots closest to Grandma meant you couldn’t get away with passing the veggies on by. Finally we are all seated and Grandma clears her throat, turns around to the cabinet and pulls out tonight’s reading. It could be a newspaper article, it could be a Bible passage. Whatever it is, it stands between Grandpa’s famous ham and me. I can still hear her voice as she shared with us something she picked out for this particular meal.

Sounds like a typical family gathering, but I wouldn’t trade it for the world. My grandma was a loving, joyful, smart woman. And I’m going to miss her.

=============
Here is Miriam Holt’s obituary
 
Miriam Grace Holt, age 87, surrounded by family and held by her husband’s hand, passed away peacefully early Monday morning, September 10, 2012 at Swan Creek Retirement Village.She was born in Sugar Ridge, OH, on April 20, 1925, the youngest of five born into the farm family of Ray and Mary Muir of Fostoria, OH. This farm-girl was a tom-boy who would try to keep up with her older brother in climbing trees, jumping out of haymows, hitting home-runs, and riding the plow horse without a saddle as fast as it could go. She met a boy, Bill Holt, in first grade at school in Bloomdale, OH. She married him in1947 shortly after he returned from WW2 pilot training and she graduated as a registered nurse from the Toledo Hospital School of Nursing. Together, they started a family in Bloomdale, bearing three sons, Nate, Steve, and Dave, and then built a home near Gibsonburg, OH where they resided for the next 56 years.Miriam was accustomed to dealing with hardships, having lost her father at age 7 and then helping her mother and siblings hold on to the family farm through the depths of the Great Depression.She encouraged her boys to be men of integrity, to excel, and to not be afraid to take risks. Since she was a nurse, ER treatment for her boys was unnecessary. She sewed up their lacerations with needle and thread and inspected their bruises and wounds with a prognosis of “You’ll live”.She was a woman of great faith. At age 17 on New Year’s Day 1943, she penned a resolution “… I have consecrated my life to my Savior, I must act accordingly. This includes daily reading and communication, witnessing, etc. – that He may live thro’ me, and I trust Him completely, and give Him back his tenth and more.”  She kept that promise – demonstrated in the way that she lived her life, in the way that she prayed for each of her sons and families they formed, her prayer and generous support for overseas missionaries, and her decades of active participation in the Fremont Alliance Church.

Miriam was honest, loyal, and resolute (some might describe it as a bit stubborn). She was fun-loving, kind, generous, and hard-working. She loved her sons, her husband, and her Savior. Though she has been promoted to glory, she will be missed.

March 11, 2011

Nate Holt – moving on

Filed under: Musings — nateholt @ 7:02 am

Out-of-the-blue phone call, another company wanting to recruit me. I initially resisted.

But it has been pretty tough since the lay-off almost two years ago. And issues probably common to most entrepreneurs… providing high-quality services but in some cases waiting months to get  paid (and even finding that many months of high-quality services are not going to be paid at all!) give a certain appeal to a steady paycheck and company-paid health insurance. But the key thing was this… the new job sounds complex and challenging… something that seems to appeal to my DNA. I just can’t help it.

In the next week or so I will be moving on. It is with sadness I bid farewell to AutoCAD Electrical.

Note: with “moving on”, it is awkward for me to maintain AutoCAD Electrical-related postings. These had to be deleted. Very sorry…

February 3, 2011

Energy-absorbing mailbox 2, Snow-plows 0

Filed under: Musings — nateholt @ 2:31 pm

It is looking like my modified mailbox may survive its second year against the county snow-plows. This is a tiny bright spot in an otherwise long and bitter Ohio winter.

Here is what the mailbox across the road looks like after the latest blast of snow. Not a pretty sight.

And here is my mailbox, still standing [so far!]. The difference may be the energy-absorbing mechanism I added to the design.

The idea is that the mass of snow-plow generated ice and snow, travelling 45-55 mph and striking the mail box assembly, causes the large gray hinged part of the mailbox to swing with the blow. This does two things: 1) As the hinged mailbox part rotates, the cross-sectional area exposed to the flying wall of snow/ice is reduced (side versus back).  Some of the trailing part of the wall of snow/ice will “miss” hitting the mailbox because the box will have partially rotated out of the way – exposing the back of the box instead of its maximum broad side. 2) The movement/acceleration of  the box as it rotates on the hinges absorbs energy along with the increasing resistance provided by the stretching spring. 

The net result is that the shock of a “broadside hit” is reduced and, “rolling with the punch”, the energy is dissipated over a longer period of time. The spring auto-resets the mailbox to its normal position… ready for the next snow-plow.

So far, so good…!

UPDATE: a commenter below asked whether the a violent rotation might fling out the contents of the mailbox. I made some snide comment, but this commenter has proven to be absolutely correct!  I need to adjust the door latch to be a bit stiffer…

We had another 6-inches of snow several days ago, and the mailboxes along my road were subsequently attacked by the snow-plows. Afterwards I found various pieces of my mail frozen to the road surface, extending to about 50 feet downstream from my still-standing mailbox. One key item, what was supposed to be a tax return, is pictured below, chiseled from the road’s surface ( ! )

January 31, 2011

Circuit Builder – switching from AWG to metric wire sizes – AutoCAD Electrical

Filed under: Electrical — nateholt @ 11:27 pm

How to define an alternate “electrical standard” to force Circuit Builder to default to metric wire sizes in its calculations.

Circuit Builder’s “Wire Size Lookup” dialog is a tool that can link into an “electrical standard” and guide you as you select motor size, motor power wire sizing, and appropriate ratings of the main disconnecting means – whether some type of breaker, fusible switch, or just fuses.

It can be launch from within Circuit Builder’s “Circuit Configuration” dialog as shown here:

The dialog opens and includes a number of different de-rating factors and settings. The results of the calculations default to AWG wire sizes. But one of the settings on the dialog can toggle calculations and display of metric wire sizes.

Let’s say you have a project that needs to use metric wire sizes rather than AWG. But you’d rather not have to remember to toggle to the “MM2″ metric option every time the “Wire Size Lookup” dialog displays. You’d like it to default to metric wire sizes.

Setting up an alternate standard that defaults to metric wire sizes

Here’s how to create an alternate standard that parallels the default National Electrical Code settings for wire calculations but defaults to using metric wire sizes instead of AWG sizes. This avoids having to hit the AWG/MM2 toggle each time the dialog displays.

1. Launch the built-in Access database editing tool as shown below.

2. The Electrical Standards Database Editor dialog opens. Expand the “Default” and “NEC” tree nodes to show all table names. Find the table named “OPT_NEC”. Right click and Copy / Paste this table. Name the new copy “OPT_NEC-MM2” as shown below.

3.  Once you hit OK, the tree structure will update and add in the new “NEC-MM2” entry as shown below. Expand it to display the copied table OPT_NEC-MM2 and click on it to open. Edit the two “name” records highlighted in red. 

Here’s the key change. Edit the record marked with a CODE field value of “W_STD”. Change its DEFAULT field value from “AWG” to “MM2″. Hit “Save” and exit out of the database editor.

Testing

Open up any project. Right click on the project name in the AutoCAD Electrical Project Manager and select it.

Now run Circuit Builder and start to “Configure” a motor circuit. On the “Motor Setup” right at the front end, hit the “Select Motor” as shown below and make some motor selection. This sets in motion the queries into the select standard in the Electrical Standards Database file.

Select any motor size in dialog above and hit OK. Now cross your fingers and pop open the “Wire Size Lookup” dialog as shown below.

The dialog should display. Metric wire sizes instead of AWG sizes should now calculate and display. 

If there are other defaults you’d like to change, take a look in your new copied OPT_NEC-MM2 table and adjust.

Other Circuit Builder tutorials:

Tutorial – Introduction to the Circuit Builder Tool (Part 1)
Circuit Builder – Creating an Alternate Electrical Standards Setting
Circuit Builder – Leveraging AutoCAD’s “Group” Concept
Circuit Builder Configure Mode – Adding a “Pick from Icon Menu” Selection
Circuit Builder – Embedding Custom Calls in the Spreadsheet

December 1, 2010

Win7 “Aero Themes” and AutoCAD 2010/2011 (??)

Filed under: Tips — nateholt @ 12:47 am

“Aero” theme selection potentially causing instability with AutoCAD 2010/2011 (??)

Win7 “Home Premium” has not been too friendly with AutoCAD 2010/2011 on my inexpensive laptop. It would go through periods of locking up every hour or so when AutoCAD was running, even when doing just the simplest tasks. Sometimes just sitting there… I would come back to the machine and it would be locked up.

A Google hit suggested that the Win7 “Aero” theme might be the culprit…

…it’s now been two “lock-up-free” days since I flipped my machine over to a generic theme!  Fingers-crossed…

August 24, 2010

Add sounds to your too-quiet AutoLisp utility

Filed under: Tips — nateholt @ 8:09 pm

Add audible feedback “sounds” to brighten your AutoLISP functions

Pretty much the totality of my AutoLISP customization has been mute. Last week I decided that it was time to make a little noise.

For example, if my function failed for some reason (operator input error or premature program exit), I wanted the tool to not just issue a little message to the command window… I wanted it to be accompanied with a clundge sound.

(acet-sys-beep …)

A search turned up a couple methods to embed some simple sounds into an AutoLISP function. The one way that seemed to work for me was using a call to “acet-sys-beep”  and this only after I installed the “Express tool” option that comes on the AutoCAD install CD.

But once in place, there were three different sounds I could generate from this call. If you want to experiment, type each of these at the “Command:” prompt (if you don’t hear a thing, you may need to check for existence of the AutoCAD “EXPRESSTOOLS” on your machine):

  • (acet-sys-beep 16) [Enter]
  • (acet-sys-beep 48) [Enter]
  • (acet-sys-beep 64) [Enter]

Sample Utility that invokes a couple different sounds

Here’s an example function showing how two different sounds can be tied into success and failure of a function. This tool just swaps tag-ID values between two picked AutoCAD Electrical components. If the components are different types, then an error dialog is accompanied by a bad sound. If success, then a more happy sound.

This is not rocket-science. But it makes my mundane utility just a bit more interesting.

The two places where the sound is invoked is highlighted below. Just to be safe, in both places there is a call to confirm that function (acet-sys-beep…) exists… before trying to call it.

July 11, 2010

QSELECT driving PROPERTIES command

Filed under: Tips — nateholt @ 8:12 pm

The AutoCAD PROPERTIES command can be driven by a filtered selection set created by the QSELECT (Quick Select) command or sub-dialog.

NOTE: 100+ other AutoCAD Electrical utilities indexed here.

Working on a potential customer’s sample drawing, I converted a complex collection of dumb graphics into an AutoCAD Electrical “smart” symbol. Works fine but the wire connection attributes ended up being very tiny because the graphics was scaled in metric units and the default wire connection attribute definition text size was coming in assuming imperial units. 

Normally would have just moved on… the converted symbol works great. But the wire connection points are hidden behind connection dots that are part of the symbol. I’d rather these wire connection attributes be big enough to at least extend a bit beyond the dot so that it is obvious that they’re there.

So, how to quickly adjust the text size of these 60+ wire connection attribute definitions? Could write a little AutoLISP utility… but how about using something already built into AutoCAD?

QSELECT and PROPERTIES command

The PROPERTIES command seemed like the right tool to make the actual text height change. But how to carefully select the 60-plus attribute definitions, almost all of which are hidden behind other graphics?

With a little tinkering, the “Quick Select” mode with filtering did the trick. Here’s the sequence:

This should bring up the “Quick Select” sub-dialog. If it does not, just type QSELECT [Enter] at the AutoCAD ‘Command:’ prompt. Select Attribute Definition as shown below.

And we want to filter on the attribute definition Tag names. The wire connection attributes expected by AutoCAD Electrical follow the naming convention X?TERM?? where the first “?” is a 1, 2, 4, or 8 and the last two form an incrementing number value (starting at “01”). So we’ll just enter in X?TERM?? where the “?” wild-card represents any single character.

That’s it. We hit OK and the “Quick Select” finds 62 attribute definitions that have a “Tag” value that wild-card matches string ‘X?TERM??’.

Now, the fun part. Adjust the text size in the PROPERTIES dialog and all 62 tiny attribute definitions resize and now reveal themselves!

July 6, 2010

Generic text/mtext/attrib/attdef Find/Replace utility – AutoLISP

Filed under: Tips — nateholt @ 4:53 pm

Enter substrings for “find” and “replace”… then pick/pick/pick on TEXT, MTEXT, ATTRIB, ATTDEF text string values to update.

Frustration setting in as I worked on AutoCAD libraries for an electrical sub-station design application… copy circuit to a new drawing name, open, then use an attribute edit command to change a substring value in a dozen or so attributes. A little Find/Replace tool would be so welcome right now!

After 20 minutes of this tediousness, decided it was time to either find an existing tool or create a quick one. Found previous posting for a drawing-wide MTEXT find/replace utility. Good place to start.

Solution

Here is the result. Seems to work.

The flow is 1) enter “Find” substring, 2) enter “Replace” substring, 3) start picking on text. The text can be attributes, text entities, attribute definitions, and even multi-line text (multi-line attributes not covered in this version).

Here is an overview of how the original version was modified.

Step 1 – Renamed the source “.lsp” file to “text_fr.lsp”

Step 2 – Got rid of the drawing-wide “ssget” and changed to a loop of individual text picks (below).

Step 3 – added two blocks of code to deal with Attributes/Attribute definitions and with straight Text entities (below).

That should do it. It seems to work for me… speeded-up my effort to copy and modify my folder of sub-circuits. 

Here’s a simple example of it in operation. Starting out with a sample drawing that contains the substring “DN100″ in three different places: an attribute that is part of a block insert instance, a single line of text, and a 3-line multi-line text entity just below it.

Let’s say we want to selectively change instances of “DN100″ to now be “CM99″.

With the utility APPLOADED (download revised version  here – see update note below) we launch by typing TEXT_FR [Enter] at the “Command:” prompt.

To use… just APPLOAD the text_fr.lsp file. Then

1. Type TEXT_FR [Enter] at the AutoCAD command prompt

2. Enter the “Find” substring, Enter the “Replace” substring

3. Start picking on individual Text, MText, Attribute or Attribute Definition entities

 

There it is. Seemed to work really well for me!

UPDATE: user note below correctly points out that if this tool used to find/replace attribute values for AutoCAD Electrical “TAG1″ attribute values, there is a good chance (especially if “IEC” tag mode is active) of confusion. This is because there is an Xdata tag name “VIA_WD_BASETAG” that is pushed out to parent components. This Xdata value carries the base copy of the overall component’s tag-ID. If the tag-ID is changed by our utility above, then this Xdata value needs to be processed as well. Here is a screenshot of the addition to our utility to deal with keeping this Xdata in synch. This code addition should lie dormant unless the tool is used on a “TAG*” attribute with AutoCAD Electrical in memory.

Update 2 – using the tool on ATTDEFs (as opposed to ATTRIBs) not working. Found that the utility was set to look at subrecord 1 instead of subrecord 2. Download revised version of tool here. I apologize for all the confusion this caused. – Nate.

June 12, 2010

My message to my daughter upon her marriage today

Filed under: Musings — nateholt @ 12:56 am

Late this afternoon I gave away my only daughter in marriage.  It was hard walking her down the asile. And she was so beautiful.

And it was even harder at the wedding reception, preparing for the final father-daughter dance. Here’s what I said to my daughter as I asked her for this last dance.

Nate.

11-June-2010

Rachel, you were my Special Angel, a nickname I attached to you when you were just a little girl.

You were the one who would spend hours with me in the evenings, quietly reading your arm-load of books, camped under my work table as I labored-away on my computer. More than once you would fall asleep under that table, amongst the scattered books. I would have to gather you up to carry you to bed.

You were special.

You were the one who would make me promise… PROMISE… to wake you up before I would leave the house on the many Monday mornings at 5am to catch the first flight out.  In the early morning darkness I would tap on your door and quietly call out your name. Five minutes later you would come padding down… doll and blanket in tow… and silently watch me finish my breakfast. Then, as I picked up my bag and headed out the back door, you would move to the front door. And as I backed the car out of the driveway, I would spy my Special Angel, illuminated by the porch light, standing there inside the glass front door. I would blink my headlights… you would raise your hand… and I would slowly drive away for yet another week of out-of-town work.

You were special.

How many thousands of times did we have bed-time prayers together? The routine was this: I would be working in my office on the computer. I would hear you brush your teeth. Next, the bathroom light switches off. A few moments later there is the gentle rustling of blankets and such as you get into bed and arrange your pillows and dolls. And finally, there’s the sound of the bedroom light switching off.  All is now quiet. Then I hear a little voice calling out for me… “Dad… I’m ready”. That was my signal. I would pause from my work and climb the stairs to your room. In the darkness, I would kneel down beside your bed and we’d have our prayer time together.

But as you grew up and moved into your later teen years, you sorta outgrew bed-time prayers thing with your dad. High-school, busy-ness, activities… growing from a little girl into a young woman.

But there was to be one more time… and of the thousands of times we had bed-time prayer together as you were growing up, this very last one is the one that is burned into my memory:

You were moving away. You were all packed up. You were leaving home the next morning to start college – hundreds of miles away. I think we both sensed that a chapter in our lives was ending.

That last night… before you were to leave home… as I worked at my computer, I heard you brushing your teeth. The bathroom light snaps off. The rustling sounds of you getting into bed. The bedroom light turning off. Quiet. I sit there at my computer. Thoughts of gratitude and thankfulness to God for bringing us to the end of this life’s chapter, but mixed in with a bit of sadness. I think back to the years of our bed-time prayers together, and how that routine thing now seemed so much more precious in the years since it had ended.

But there was to be this one last time. From the quietness, I hear your voice call out, just as it did some years before as a younger, littler girl… “Dad. I’m ready.” I climbed the stairs to your room, I’m glad it’s dark because tears are filling my eyes. That final bed-time prayer with my special angel, me weeping in the darkness… I will never forget.

Finally, I believe God’s hand has touched your life.

You were probably six or seven. We guys were all at the softball field for a game in our men’s church league. It was a hot, muggy summer evening. The men were loosening up… stretching, playing catch, taking practice swings.

As you stand there watching, I suddenly feel compelled to take three steps forward to stand between you and a player taking cuts with a baseball bat some distance away. Step… step… and on the third one, as I step in front of you, the bat slips from his grip and shoots through the air, barrel first. It strikes me in the thigh on that third step. We all jump back, everybody looks.

I think we probably went on to lose that game. And what everybody thought that evening was that our record was not getting any better and, oh yeah, Nate will probably wake up tomorrow with a nice-sized bruise on his thigh. But nobody saw what really happened that evening, and I didn’t say anything. I filed this away in my heart. Because I believed that on that hot, muggy summer evening, that I witnessed the Hand of God in and on your life… the barrel-end of that flying bat was thigh-high on me, but would have been forehead high on you.

God is good. And you are special. God’s hand is on you and Eric.

So, here we are this evening. I gave you away a couple hours ago. But will you dance with your dad this one last time?

 

============

Update: pics thanks to Remi Bowers, Sandy Felzer

Update: watching this advertisement brings back a small measure of the feelings I had that day…  Nate.

February 10, 2010

Align Attribute utility

Filed under: Tips — nateholt @ 10:18 am

Pick master attribute and then pick one or dozens of other attributes from multiple block instances to align with it.

Steve Kemp responded to a user request on the AutoCAD Electrical discussion group by developing this cool little utility. He passed it on to me for posting here.

Here is an example of how it might be used. Let’s say that you have a column of terminal block inserts that are wired to a PLC module block insert. But, for this one drawing, you’d like to relocate one of the attributes to the left-hand side of each terminal block instance.

Here’s how to do it with Steve’s tool. First, use normal AutoCAD grips to position the attribute of one instance to the desired position.

Next, APPLOAD Steve’s little AutoLISP utility here. Type AATTR [Enter] at the command line.

Now pick on the one relocated attribute. Choose “vertical” alignment (default).

Hit [Enter]… and there you go! Everything neatly aligned with the master.

Current limitations: as written, the utility does not process attributes that are set to “FIT” or “ALIGN” justification.

About Steve Kemp… in addition to applications development (contact him at skiflea@mchsi.com), Steve likes to surf. Though trapped in the heartland of the US, if he loads his boat down with 1600 or so pounds of dead weight so that it sits low in the water and then drives 9-11 MPH, it will throw up a wake large enough to surf!

October 15, 2009

‘ZOOM to HANDLE’ utility

Filed under: Tips — nateholt @ 6:26 pm

Quickly create a tool to find and zoom to a given entity using its handle number.

You know an entity/object’s “handle” number, but where in the world is it in your jumbled drawing?

Complex library creation and testing has been on my plate for a couple weeks. The new code spits out the AutoCAD handle of various entities as processing proceeds. Sometimes there’s a need to go back in and find a problem entity on the drawing. Have the handle reference but that’s it. What is it, where is it?

Manual Method – Crude with  no-frills

Here’s the crude, no-frills way. Open the entity and display its guts in the command window.

Let’s say that you’ve run your program in a “debugging” mode and find that there is some issue with object handle “74F” in the active drawing. What and where is this entity?

Type this AutoLISP expression at the “Command:” prompt

Command: (entget (handent “74F”)) [Enter]

It returns this block of data about this entity.

((-1 . <Entity name: 7ef152f8>) (0 . “LINE”) (330 . <Entity name: 7ef11c10>) (5 . “74F”) (100 . “AcDbEntity”) (67 . 0) (410 . “Model”) (8 . “0”) (100 .
“AcDbLine”) (10 6.09375 13.5 0.0) (11 6.12676 13.5 0.0) (210 0.0 0.0 1.0))

The “0” entry flags the entity type: “LINE” and the “10” and “11” parts give the XYZ start and end points of the LINE segment.

Crude, but it does work.

Better Method: auto-ZOOM to the entity

Here’s a little AutoLISP utility thrown together to help in troubleshooting. It zooms in on the target object given the typed-in handle.

The syntax is pretty simple. After the utility is APPLOADed, type ZH at the AutoCAD command prompt and then enter the handle number at the next prompt…

Command: zh [Enter]
Handle  =74F [Enter]

The utility opens up the entity. If it is a simple graphical entity like an arc, text, block insert, line, then it opens it to find “where” it is.

Entity type: LINE XY=(6.11026 13.5 0.0)

… and then it issues a ZOOM “Centered” command to display the object in the middle of the screen. It then tries to highlight it. Now you can zoom in or out with “I” or “O” keystrokes or use your mouse “wheel” to take a better look in context.

Here’s the handle “74F” example… it was an incomplete wire connection on the high side of a control transformer.

zh001

Here’s the utility itself. Cut and paste into an ASCII text file, let’s call it “zh.lsp”.

; ** 13-Oct-09 NEHolt - http://n8consultants.com
(defun c:zh ( / hdl xy enttype ed en xy2)
  ; ZOOM to entity "handle". Does not check model vs layout space.
  (while (/= (setq hdl (getstring (strcat
              "\nHandle " (if en "(or I=Zoom IN, O=Zoom out)" "") " ="))) "")
    (cond
      ((= (strcase hdl) "I")(command "_.ZOOM" "1.5x"))
      ((= (strcase hdl) "O")(command "_.ZOOM" "0.75x"))
      (T
        (if (setq en (handent hdl)) ; convert handle string to its "entity name"
          (progn ; appears to be a valid handle number
            (if (setq ed (entget en)) ; open up the entity
              (progn ; success, okay to continue
                (setq xy nil)
                (setq enttype (cdr (assoc 0 ed))) ; extract the entity type
                (princ "\nEntity type: ")(princ enttype)
                (if (= enttype "INSERT")
                  (progn ; this is a block INSERT instance
                    (princ " blk nam=")
                    (princ (cdr (assoc 2 ed))) ; display block name
                ) )
                ; Figure where entity is located
                (if (cdr (assoc 10 ed))
                  (progn
                    (setq xy (cdr (assoc 10 ed))) ; 10 subrec often gives a location value
                    (if (= enttype "LINE")
                      (progn ; LINE entity. Reset to zoom to line's midpoint
                        (setq xy2 (cdr (assoc 11 ed))) ; XY of other end
                        ; Calc midpoint coordinate
                        (setq xy (list (+ (car xy) (* 0.5 (- (car xy2) (car xy))))
                                       (+ (cadr xy) (* 0.5 (- (cadr xy2) (cadr xy))))
                                       (+ (caddr xy) (* 0.5 (- (caddr xy2) (caddr xy))))))
                ) ) ) )
                (if xy
                  (progn ; Non-blank XY extracted from picked entity. Go ahead and
                           ; display the value and ZOOM in to that point on the drawing.
                    (princ " XY=")(princ xy)
                    (command "_.ZOOM" "_C" xy 1.0) ; use ZOOM CENTER command
                    (redraw en 3) ; trigger entity to "highlight"
                ) )
        ) ) ) )
      )
    )
  )
  (command "_.REGEN") ; brute force - unhighlight everything
  (princ) ; quiet return
)

That should be it. APPLOAD this file and that should expose the utility to use on your active drawing.

UPDATE: typo introduced when the above program was cut/paste into this posting. Very sorry. Should be okay now. – Nate.

September 28, 2009

Nate’s Simple AutoLISP – Lesson 007

Filed under: Tutorials — nateholt @ 10:12 pm

Using the selection set function “ssget” to track down and remove instances of an invisible block.

This exercise will be short and [hopefully] sweet. The idea for this one came from personal experience… today.

Let’s say that you create a block instance consisting of just one attribute definition. You define its tag name to be “BY_CUSTOMER” and you leave the value blank, planning to fill in with different values later.

autolisp007a
You go ahead and block this single attribute definition with a block name of “CUSTOMER_FLAG”…

autolisp007b

 

… and you sprinkle a bunch of instances of this block across your drawing. You keep working away. A couple hours later you’re ready to go back and assign values to the BY_CUSTOMER attribute on each inserted instance of your CUSTOMER_FLAG block.

Hey… where are they? Don’t see them. You know you inserted them at multiple places in your drawing. But there’s nothing to pick on!

The block was just a single attribute and, unfortunately, you created it with a blank default value ( ! ).

Okay, let’s erase them and have a “do over”. But hey, how can I erase something I can’t select… something I can’t even see?!

Well, could try the QTEXT trick. Turn QTEXT “ON” and type REGEN. All blank attribute values now should display a tiny dot. But how can I tell which dot is which without picking one at a time and listing what it is?

Quick solution using the AutoLISP function “SSGET” with a filter

The solution uses AutoLISP and is easy… we can do it all right at the “Command:” prompt.

What we’ll do is make a “selection set” of all instances of this invisible / un-selectable block. We’ll do this by scanning ALL entities in our active drawing and filter out everything except blocks that carry the block name “CUSTOMER_FLAG”. A block instance carries the assigned block name in the “2” entity sub-record. We’ll make a really simple filter by just looking for entities that have “CUSTOMER_FLAG” as the value of their “2” subrecord.

Type this at the “Command:” prompt:

(setq ss (ssget “_X”  ‘((2 . “CUSTOMER_FLAG”))))  [Enter]

If any matches found, the message “<Selection set xx>” will display below the “Command:” prompt. What we’ve done here is defined a dummy variable we’re calling “ss” and it will carry the selection set returned by the call to “(ssget …)”.

Let’s see how many it found. Type this:

(sslength ss) [Enter]

… and it will display the number of instances of CUSTOMER_FLAG that were found in the drawing.

Okay, now let’s blow them away so we can PURGE our drawing. Type this:

(command “_ERASE” ss “”) [Enter]

… and they’re gone! The above just launches the normal AutoCAD ERASE command. We pass the selection set variable we created above, “ss” to this command along with an extra “” at the end to kick out of the “select objects” loop. Finally, launch the PURGE command. You should now see that block CUSTOMER_FLAG is a candidate for purging. Yes! Blow it away.

 

Caution

The above solution will probably work as expected 99.9% of the time. But there are some entities that “share” the “2” sub-record. For example, an ATTDEF entity uses the “2” subrecord to carry the attribute tag name. So if we had some orphaned ATTDEFs floating around in our drawing and they happened to have a tag name of “CUSTOMER_FLAG”, then these CUSTOMER_FLAG ATTDEFs would be erased along with the CUSTOMER_FLAG block instances.

To be really, really safe, we could redo our selection set filtering like this to make sure that we’re only dealing with block instances AND the block instance is named “CUSTOMER_FLAG”. The entity type is carried in sub-record type zero. Block insert entity type is “INSERT”. So, a fool-proof, command-prompt-entered query to build the selection set might look like this:

(setq ss (ssget “_X”  ‘((-4 . “<AND”)(0 . “INSERT”)(2 . “CUSTOMER_FLAG”)(-4 . “AND>”))))  [Enter]

September 26, 2009

Nate’s Simple AutoLISP – Lesson 006

Filed under: Tutorials — nateholt @ 3:17 pm

Experimenting with attribute definition order, entity sorting, and entmake

This lesson’s issue: Scrambled attribute order on a block insert.

(Note: previous postings: Lesson 001, Lesson 002, Lesson 003Lesson 004, and Lesson 005)

Here is an AutoCAD Electrical transformer symbol popped into a circuit. If I decide to use the generic attribute editor command built into AutoCAD to examine and possibly adjust values for this instance of the transformer block insert, the attribute order listed is pretty scrambled.

autolisp006a

It appears that the order that attributes are listed here are in the same order that the ATTDEF entities were inserted into the block’s source “.dwg” file. (Note: you can use AutoCAD’s BATTMAN command and painfully re-order the attribute listing for the block reference in the active drawing… but that is a one-time fix and not a permanent library “.dwg”-based solution. Read on.)

Call up the block’s original “.dwg” file, VXF1D.dwg. Type this at the command prompt:

Command: (entget (car (entsel))) [Enter]  

Select object:[Pick on the TAG1 attribute definition]

((-1 . <Entity name: 7ffffbbedf0>) (0 . "ATTDEF") (330 . <Entity name: 7ffffbbe820>) (5 . "5F")(100 . "AcDbEntity") (67 . 0) (410 . "Model")
(8 . "0") (100 . "AcDbText") (10 0.166667 0.749995 0.0) (40 . 0.125) (1 . "XF") (50 . 0.0) (41 . 1.0) (51 . 0.0) (7 . "WD") (71 . 0) (72 . 1)
(11 0.375 0.749995 0.0) (210 0.0 0.0 1.0) (100 . "AcDbAttributeDefinition") (280 . 0) (3 . "") (2 . "TAG1") (70 . 0) (73 . 0) (74 . 0) (280 . 0))

and then pick on the TAG1 attribute definition. This expression will read the attribute definition entity (the one you select) and return the entity’s data (above). This is pretty cryptic but the part shown in red is the picked entity’s “handle” number, “5F”.

If we repeat for the RATING1 and the DESC1 attributes, the handle numbers are “5E” and “60” respectively.

autolisp006b

These entity handle numbers (in hexidecimal format) indicate the order that these attribute definitions were created and added to the library symbol drawing. It looks like the RATING1 attribute definition (5E) was created just before the TAG1 (5F) attribute definition. Next, the DESC1 (60) was defined and added to the symbol.

ATTDEF creation order and attribute display order

Does the creation order drive the attribute’s display order when the source library symbol is inserted as a block insert into another drawing?

Let’s take another look at the attribute editor dialog display when the symbol above is inserted as a block reference into a drawing.

autolisp006c

Note that these three attributes are listed together with RATING1 (original handle “5E”) leading, TAG1 next (original handle “5F”) and DESC1 (original handle “60”) bring up the rear. It looks like that the creation order DOES influence the display order.

Let’s say that we really want the TAG1 attribute to be first in the list. Then we want the three DESC1, DESC2, and DESC3 attributes listed, then all of the “RATING*” attributes, catalog assignment attributes, and then all the rest in alphabetical order. So, if creation order is a controlling factor, we have a couple options…

OPTION 1: We could carefully re-construct our VXF1D.dwg base library symbol (and the many hundreds of others) so that the attribute defintions are each created in this order.

OPTION 2: Write a little AutoLISP utility that takes an existing scrambled library symbol dwg file, deletes all the attribute definitions, and then pushes them back into the symbol’s dwg file in the desired order.

Let’s go with the second option.

Using (entmake…)

Let’s try a little experiment. Let’s save an ATTDEF’s block of data into an AutoLISP variable. Then delete the ATTDEF using AutoCAD ERASE command. Then see if we can re-create a new, carbon-copy of the original ATTDEF with a call to “entmake”.

1. With our VXF1D.dwg drawing open on the screen, type this at the command prompt:

Command: (setq tag1_data (entget(car (entsel)))) [Enter, pick on TAG1 attdef]

Select object: ((-1 . <Entity name: 7ffffbbedf0>) (0 . “ATTDEF”) (330 . <Entity
name: 7ffffbbe820>) (5 . “5F”) (100 . “AcDbEntity”) (67 . 0) (410 . “Model”) (8
. “0”) (100 . “AcDbText”) (10 0.166667 0.749995 0.0) (40 . 0.125) (1 . “XF”)
(50 . 0.0) (41 . 1.0) (51 . 0.0) (7 . “WD”) (71 . 0) (72 . 1) (11 0.375
0.749995 0.0) (210 0.0 0.0 1.0) (100 . “AcDbAttributeDefinition”) (280 . 0) (3
. “”) (2 . “TAG1″) (70 . 0) (73 . 0) (74 . 0) (280 . 0))

Command: (setq desc1_data (entget (car (entsel)))) [Enter, pick on DESC1 attdef]

Select object: ((-1 . <Entity name: 7ffffbbee00>) (0 . “ATTDEF”) (330 . <Entity
name: 7ffffbbe820>) (5 . “60”) (100 . “AcDbEntity”) (67 . 0) (410 . “Model”) (8
. “0”) (100 . “AcDbText”) (10 0.234887 -0.89792 0.0) (40 . 0.125) (1 . “”) (50
. 0.0) (41 . 0.7) (51 . 0.0) (7 . “WD”) (71 . 0) (72 . 1) (11 0.424471 -0.89792
0.0) (210 0.0 0.0 1.0) (100 . “AcDbAttributeDefinition”) (280 . 0) (3 . “”) (2
. “DESC1″) (70 . 0) (73 . 0) (74 . 0) (280 . 0))

Command: (setq rating1_data (entget (car (entsel)))) [Enter, pick on RATING1 attdef]

Select object: ((-1 . <Entity name: 7ffffbbede0>) (0 . “ATTDEF”) (330 . <Entity
name: 7ffffbbe820>) (5 . “5E”) (100 . “AcDbEntity”) (67 . 0) (410 . “Model”) (8
. “0”) (100 . “AcDbText”) (10 0.136875 0.59375 0.0) (40 . 0.125) (1 . “”) (50 .
0.0) (41 . 0.7) (51 . 0.0) (7 . “WD”) (71 . 0) (72 . 1) (11 0.399375 0.59375
0.0) (210 0.0 0.0 1.0) (100 . “AcDbAttributeDefinition”) (280 . 0) (3 . “”) (2
. “RATING1″) (70 . 0) (73 . 0) (74 . 0) (280 . 0))

2. Now erase these three ATTDEFS ( ! )

3. Now, let’s recreate them, but in a specific order. Type these three commands at the command line:

(entmake tag1_data) [Enter]

(entmake desc1_data) [Enter]

(entmake rating1_data) [Enter]

4. The deleted ATTDEFs are re-created, but they are now in a different order. Confirm that the handles of the three new ATTDEFs now increment in the new order of TAG1, DESC1, and then RATING1.

 

A Re-order ATTDEF AutoLISP utility

With our little utility, we need to more or less duplicate what we just did above:  1. Capture all the ATTDEFs, 2. Erase them, 3. Re-create them with (entmake…) in the desired order.

Here’s what our little AutoLISP utility’s program flow might look like in order to do this:

1. We first define the order for specific attribute definition tagnames. We decide that we’ll support wild-cards in the tag names. This makes it easy to define all of the various AutoCAD Electrical component “tag-ID” attribute tagnames in the first entry of the priority list.

  ; Set up order list of ATTDEFS. Wild-cards are supported.     
  (setq orderlst (list "TAG1*,TAG2,TAGSTRIP,P_TAG1,P_TAGSTRIP"
                       "DESC1"
                       "DESC2"
                       "DESC3"
                       "RATING*"
                       "MFG"
                       "CAT"
                       "ASSYCODE"
                       "INST"
                       "LOC"
                       "FAMILY"
                       "TERM*"))

2. Now, with the source block “.dwg” open in AutoCAD, our utility constructs a selection set of all ATTDEF entities found in the drawing. The call is to “ssget” and we pass the “_X” parameter which processes ALL entities in the active drawing. Our expression has a ‘((0 . “ATTDEF”)) filter included so that only ATTDEFs will end up in our selection set.

  ; Gather up selection set of all ATTDEFs on the active drawing
  (setq ss (ssget "_X" '((0 . "ATTDEF"))))

3. Our utility then opens each of these ATTDEF entities in a “while” loop, indexing through the ATTDEFs in the selction set “ss”, one at a time. It pushes each ATTDEF block of data into an overall big list of data that we’re calling “attdef_lst”.

   (if (AND (/= ss nil)(> (sslength ss) 1))
    (progn ; at least one ATTDEF found. Okay to continue.
      ; Extract all ATTDEF tag names and overall ATTDEF entity data. Save in parallel lists.     
      (setq attdef_lst nil) ; start with blank data list
      (setq slen (sslength ss)) ; number of ATTDEFs in selection set
      (setq ix 0) ; used to index through the selection list
      (while (< ix slen)        
        (setq en (ssname ss ix)) ; get next ATTDEF entity name from selection set
        (setq ed (entget en)) ; open up this ATTDEF entity
        (setq attdef_lst (cons ed attdef_lst)) ; add its data to the big list
        (princ " ")(princ (cdr (assoc 2 ed))) ; display existing order in command window
        (setq ix (1+ ix)) ; increment index to get next ATTDEF entity from selection set
      )

4. Next we sort this long list of chunks of ATTDEF data alphabetically – by the tagname part of each chunk of ATTDEF data. Setting up this sort function is a bit cryptic. See the normal AutoCAD help for a few more examples. For our time here, just go with it as shown. Basically it doing a sort on the “2” part of each ATTDEF block of data. This is the part that carries the ATTDEF “tag name”. To get at it in each list, our function uses the “(assoc 2…)” to dig it out and then a “(cdr …” to strip off the leading “2” identifier. This leaves just the tag name to be used in the sort compare.

      ; Initially sort the list alphabetically by tagname. The tagname is in the "2"
      ; part of the entity's data. Access the value by extracting with the
      ; (assoc 2 ...) function.
      (setq attdef_lst (vl-sort attdef_lst
            '(lambda (X Y) (< (cdr (assoc 2 X)) (cdr (assoc 2 Y))))))

5. Now we’re ready to create a new version of this data list with certain ATTDEF tagnamed chunks pushed to the front part of the list. The utility cycles through the tagname priority list that we’ve set up in “orderlst” (step #1 above). For each tagname or wild-carded tagname, the utility cycles through the alphabetized list, looking for a match on the (cdr (assoc 2 …)) tagname extracted from each ATTDEF block of data. On a match, that block of data is pushed into the new list.

      ; Okay. So far, so good. We now have all ATTDEFs alphabetically sorted and held
      ; in a big data list called "attdef_lst".
           
      ; Now prepare to create a brand new ATTDEF list by pulling entries from the
      ; "attdef_lst" and pushing them into this new list in the desired order.
      (setq new_attdef_lst nil) ; Start with empty new list
      (setq processed_ix_lst nil) ; To be used for tracking what items have been
                                  ; pulled from alphabetized list into the new list.
     
      ; Process the ATTDEF tagnames against the desired order given in the "orderlst"
      (foreach sortorder_name orderlst
        (setq ix 0)
        (foreach rec attdef_lst
          (setq attdef_tag (cdr (assoc 2 rec))) ; extract ATTDEF tagname for this entry
          (if (AND (not (member ix processed_ix_lst)) ; check if already processed
                   (wcmatch attdef_tag sortorder_name)) ; check if tagname wild-card
                                      ; matches with the target sortordername string.
            (progn ; This ATTDEF name matches what we're looking for. Pull its data out
                   ; of the attdef_lst and push the entity's block of data into the
                   ; new "new_attdef_lst" ordered list.
              (setq new_attdef_lst (cons rec new_attdef_lst))
              ; Remember this entry's index number so won't try to add it in again
              (setq processed_ix_lst (cons ix processed_ix_lst))
          ) )
          (setq ix (1+ ix))
        )
      )   

6. When no more of the priority list to process, we need to push any un-referenced attribute definition chunks of data on to the new list (they are still alphabetized).

      ; The new_attdef_lst should contain the ordered attributes that were specifically
      ; defined. Now add in any left-overs. These will go in alphabetically.
      (setq ix 0)
      (foreach rec attdef_lst
        (if (not (member ix processed_ix_lst))
          ; This entry not processed above. Go ahead and push it into the new list.
          (setq new_attdef_lst (cons rec new_attdef_lst))
        )
        (setq ix (1+ ix))
      )

7.  That’s it. Our new ATTDEF blocks of data list is complete. But it’s in reverse order because we used the more efficient “cons…” function to build the new version of the list (instead of using the slower “append…” function). Reverse the list.

      ; Now put the new_attdef_lst into the correct order (it is reversed due to
      ; using "(cons ...) to add elements to the beginning of the list instead of
      ; the end of the list)
      (setq new_attdef_lst (reverse new_attdef_lst))

8. Now we cross our fingers. The utility erases ALL of the existing ATTDEF entities in the drawing…

      ; Erase all existing ATTDEFS (!)
      (command "_.ERASE" ss "")

9. … and we then cycle through our new, re-ordered list of ATTDEF data and re-create each ATTDEF using calls to (entmake …) .

      ; Now rewrite the ATTDEFs but using the new ordered list of ATTDEF blocks of
      ; data. Each ATTDEF should pop back in exactly as before, but the handle assignment
      ; given to each re-written ATTDEF will now be the next sequential for the
      ; drawing (!)
      (foreach rec new_attdef_lst
        (princ "\n")(princ (cdr (assoc 2 rec))) ; display attdef name to command window
        (entmake rec)
      )

 

Full Utility

Here’s the full utility. Cut and paste it into a file called reorder_attdef.lsp.

; ----------  R E O R D E R _ A T T D E F . L S P  ---------------------
(defun c:reorder_attdef ( / attdef_lst ix slen ss en ed attdef_tag new_attdef_lst
                            processed_ix_lst rec orderlst sortorder_name x y)
                           
  ; Set up order list of ATTDEFS. Wild-cards are supported.     
  (setq orderlst (list "TAG1*,TAG2,TAGSTRIP,P_TAG1,P_TAGSTRIP"
                       "DESC1"
                       "DESC2"
                       "DESC3"
                       "RATING*"
                       "MFG"
                       "CAT"
                       "ASSYCODE"
                       "INST"
                       "LOC"
                       "FAMILY"
                       "TERM*"))
  ; Gather up selection set of all ATTDEFs on the active drawing
  (setq ss (ssget "_X" '((0 . "ATTDEF"))))
 
  (if (AND (/= ss nil)(> (sslength ss) 1))
    (progn ; at least one ATTDEF found. Okay to continue.
      ; Extract all ATTDEF tag names and overall ATTDEF entity data. Save in parallel lists.     
      (setq attdef_lst nil) ; start with blank data list
      (setq slen (sslength ss)) ; number of ATTDEFs in selection set
      (setq ix 0) ; used to index through the selection list
      (while (< ix slen)        
        (setq en (ssname ss ix)) ; get next ATTDEF entity name from selection set
        (setq ed (entget en)) ; open up this ATTDEF entity
        (setq attdef_lst (cons ed attdef_lst)) ; add its data to the big list
        (princ " ")(princ (cdr (assoc 2 ed))) ; display existing order in command window
        (setq ix (1+ ix)) ; increment index to get next ATTDEF entity from selection set
      )
     
      ; Initially sort the list alphabetically by tagname. The tagname is in the "2"
      ; part of the entity's data. Access the value by extracting with the
      ; (assoc 2 ...) function.
      (setq attdef_lst (vl-sort attdef_lst
            '(lambda (X Y) (< (cdr (assoc 2 X)) (cdr (assoc 2 Y))))))
      ; Okay. So far, so good. We now have all ATTDEFs alphabetically sorted and held
      ; in a big data list called "attdef_lst".
           
      ; Now prepare to create a brand new ATTDEF list by pulling entries from the
      ; "attdef_lst" and pushing them into this new list in the desired order.
      (setq new_attdef_lst nil) ; Start with empty new list
      (setq processed_ix_lst nil) ; To be used for tracking what items have been
                                  ; pulled from alphabetized list into the new list.
     
      ; Process the ATTDEF tagnames against the desired order given in the "orderlst"
      (foreach sortorder_name orderlst
        (setq ix 0)
        (foreach rec attdef_lst
          (setq attdef_tag (cdr (assoc 2 rec))) ; extract ATTDEF tagname for this entry
          (if (AND (not (member ix processed_ix_lst)) ; check if already processed
                   (wcmatch attdef_tag sortorder_name)) ; check if tagname wild-card
                                      ; matches with the target sortordername string.
            (progn ; This ATTDEF name matches what we're looking for. Pull its data out
                   ; of the attdef_lst and push the entity's block of data into the
                   ; new "new_attdef_lst" ordered list.
              (setq new_attdef_lst (cons rec new_attdef_lst))
              ; Remember this entry's index number so won't try to add it in again
              (setq processed_ix_lst (cons ix processed_ix_lst))
          ) )
          (setq ix (1+ ix))
        )
      )   
     
      ; The new_attdef_lst should contain the ordered attributes that were specifically
      ; defined. Now add in any left-overs. These will go in alphabetically.
      (setq ix 0)
      (foreach rec attdef_lst
        (if (not (member ix processed_ix_lst))
          ; This entry not processed above. Go ahead and push it into the new list.
          (setq new_attdef_lst (cons rec new_attdef_lst))
        )
        (setq ix (1+ ix))
      )
      ; Now put the new_attdef_lst into the correct order (it is reversed due to
      ; using "(cons ...) to add elements to the beginning of the list instead of
      ; the end of the list)
      (setq new_attdef_lst (reverse new_attdef_lst))
     
      ; Erase all existing ATTDEFS (!)
      (command "_.ERASE" ss "")
     
      ; Now rewrite the ATTDEFs but using the new ordered list of ATTDEF blocks of
      ; data. Each ATTDEF should pop back in exactly as before, but the handle assignment
      ; given to each re-written ATTDEF will now be the next sequential for the
      ; drawing (!)
      (foreach rec new_attdef_lst
        (princ "\n")(princ (cdr (assoc 2 rec))) ; display attdef name to command window
        (entmake rec)
      )
  ) )
  (setq ss nil) ; release the selection set
  (princ)
)        
       

Let’s try it.

With drawing VXF1D.dwg open on screen, APPLOAD the above utility file reorder_attdef.lsp.

Then type REORDER_ATTDEF [Enter] at the command prompt. In the blink of an eye, the utility should delete all ATTDEFs, sort, and re-write them. Save the symbol “.dwg”.

Now, in a fresh or purged drawing, insert this revised symbol. Cross fingers. Launch the attribute edit command, pick on the newly inserted transformer symbol…

autolisp006d

Good work! Now batch this tool against a bunch of symbols (make a backup first, just in case!). In short order your library should be modified.

Advertisement

Could some custom applications make your company more productive? Find out. http://n8consultants.com

U P D A T E :  I just noticed that the original program with “RATING*” would put RATING10, 11, and 12 right after RATING1 and before RATING2. Looks like my original attribute definition order list example should explicitly list all 12 “RATING*” tag names like this to avoid this problem:

(setq orderlst (list "TAG1*,TAG2,TAGSTRIP,P_TAG1,P_TAGSTRIP" 
                       "DESC1" 
                       "DESC2" 
                       "DESC3" 
                       "RATING1" 
                       "RATING2" 
                       "RATING3" 
                       "RATING4"
                       "RATING5"
                       "RATING6"
                       "RATING7"
                       "RATING8"
                       "RATING9"
                       "RATING10"
                       "RATING11"
                       "RATING12"
                       "MFG" 
                       "CAT" 
                       "ASSYCODE" 
                       "INST"
                       "LOC"
                       "FAMILY"
                       "TERM*"))

September 1, 2009

Nate’s Simple Autolisp – Lesson 005

Filed under: Tutorials — nateholt @ 12:20 am

Using (nentsel…) and changing an existing block insert instance

Here’s the real-world example (happened  yesterday) that triggers quick Lisp lesson number “005”.  The issue revolves around the color yellow.

The challenge

lisp005_01I reviewed some drawings from a client. Everything looked fine with dark background. But a light background, some of the attributed blocks were difficult to read – yellow ones. Very uncomfortable at best, bordering on impossible at worst.

Normally, I would just figure out what “layer” the attributed text was on and force that layer’s color from yellow to a better color.

 

But there was a problem. This block’s single yellow text attribute definition was not set up to have its COLOR display “ByLayer”. It was “hard-coded” to be yellow ALL the time.

How can we tell?… Here is the result of the AutoCAD “LIST” command on the upper “52” bubble. Note that the single attribute, tag name BUBNUM, has been defined to always display as “YELLOW”.

autolisp005a

A Solution

Let’s fix this. What we’ll do now is build up a little AutoLISP tool to force a picked attribute to display in a different color. If you’d like to follow along “hands on”, download the above sample dwg file here. What we want our little AutoLISP tool to do is this: we pick on a yellow attribute. Our tool reads the attribute and, if the attribute’s color was NOT set up “BYLAYER”, changes its color to color “red”. This should show up nicely whether a dark or light background is selected.

Step-by-Step: Difference between (entsel) and (nentsel)

In each of the previous lessons here, here, here, and here we have looked at the AutoLisp (entsel) entity selection function.

With the example dwg file open/active on your screen, type this at the “Command:” prompt:

Command: (entsel) [Enter]
Select object: [pick on text in upper bubble] (<Entity name: 7ffffb04930> (-0.0405614 0.0279495 0.0))

The (entsel) function returns a two-element list: the first element is the entity name of the picked entity (i.e. the block insert instance) and the second element is the XY coordinate of the mouse pick point. We just want the entity name, the first element of this returned list. Use the AutoLISP “car” function to extract the first element. So, let’s do it again. Type this at the “Command:” prompt and pick on the upper bubble:

Command: (car (entsel)) [Enter]
Select object: [pick on text in upper bubble] <Entity name: 7ffffb04930>

Remember, just like in the previous lessons, we need to keep those “(” and “)” balanced out. For each “(” you add, you need to balance it with a “)”. Okay, good so far. Now let’s modify our “program” to open up the returned entity name with a call to “entget”:
 
Command: (entget (car (entsel))) [Enter]
Select object: [pick on text in upper bubble] ((-1 . <Entity name: 7ffffb04930>) (0 . “INSERT”) (330 . <Entity
name: 7ffffb039f0>) (5 . “18B”) (100 . “AcDbEntity”) (67 . 0) (410 . “Model”)
(8 . “0”) (100 . “AcDbBlockReference”) (66 . 1) (2 . “bubble”) (10 0.0 0.0 0.0)
(41 . 1.0) (42 . 1.0) (43 . 1.0) (50 . 0.0) (70 . 0) (71 . 0) (44 . 0.0) (45 .
0.0) (210 0.0 0.0 1.0))
 
Okay, so this is the available information returned when we picked on the block insert instance with (entsel). If you compare this cryptic list of data (the very top part below) with the upper part of the LIST command’s display when pick the same item (shown below beginning at the “list” command entry), you can see some of the potential data match-ups. Each piece of data is represented by a “dotted pair”. There is a lead number, referred to as the “DXF code”, identifies what the data on the right-hand side of the dot is associated with. We can guess at the ones shown here… they seem to match up:
 
autolisp005b
 
Here is a partial list of DXF codes (i.e. the left-hand number in each dotted pair). This is pulled from the Visual Lisp on-line HELP (topic = “DXF group codes in numerical order”).
 
autolisp005d
 
 
The problem
 
No problem getting information on the overall block insert instance itself. The problem is that we cannot get access to the attribute text embedded inside of the block insert instance… we cannot specifically “pick” on it.  No matter what we pick on the block insert, whether it is the circle part or the attribute part of the block, (entsel) always returns the entity name of the overall block insert. What we really need is a pick function that digs deeper into these complex entities and pulls out the “nested” sub-entity name of what we pick.
 
 
Selection of nested sub-entities with (nentsel)
 
This is where the AutoLISP function (nentsel) comes into play. It works like (entsel) but, if you pick on an entity that is part of an overall complex entity like a block insert instance or a multi-segment polyline, (nentsel) returns the picked sub-entity name, not the main overall entity name that (entsel) always returns.
 
So, let’s try it on the top bubble. Type this at the “Command:” prompt and pick on the text attribute inside of the top bubble block insert:
 
Command: (entget (car (nentsel))) [Enter]
Select object: [pick on text in upper bubble] ((-1 . <Entity name: 7ffffb04940>) (0 . “ATTRIB”) (330 . <Entity
name: 7ffffb04930>) (5 . “18C”) (100 . “AcDbEntity”) (67 . 0) (410 . “Model”)
(8 . “0”) (62 . 2) (100 . “AcDbText”) (10 -0.0606988 -0.0385562 0.0) (40 .
0.078125) (1 . “52”) (50 . 0.0) (41 . 1.0) (51 . 0.0) (7 . “Standard”) (71 . 0)
(72 . 4) (11 0.0 0.0 0.0) (210 0.0 0.0 1.0) (100 . “AcDbAttribute”) (280 . 0)
(2 . “BUBNUM”) (70 . 0) (73 . 0) (74 . 0) (280 . 0))
 
Okay, now compare the above data return from (entget (car (nentsel))) with the original “LIST” display on the whole bubble block insert. Note that the data returned due to the (nentsel) pick now points at the attribute itself, NOT the overall block insert instance as we saw with (entsel).
 
 
autolisp005c 
 
How do we know that the “62” type identifies the entity “Color” assignment? Look at the bottom of the “DXF codes” listing. DXF code “62” is associated with a fixed color assignment (i.e. not “ByBlock” or “ByLayer”). The value that is the right-hand part of the 62 dotted pair is the color number.
 
Our yellow attribute text is forced to remain yellow at all times because the BUBNUM attribute carries a (62 . 2) subrecord. Color yellow is color number 2!
 
Experiment – rewrite the attribute to color 1 – RED.
 
If we could only force this attribute’s (62 . 2) subrecord to be (62 . 1), then maybe it would turn “red”. Let’s try.
First step, we repeat the last command but we save the entity’s data in a variable we’ll call “old_data”. Type this at the Command: prompt.
 
Command:(setq old_data (entget (car (nentsel)))) [Enter]
Select object: [pick right on the attribute]((-1 . <Entity name: 7ffffb26940>) (0 . “ATTRIB”) (330 . <Entity
name: 7ffffb26930>) (5 . “18C”) (100 . “AcDbEntity”) (67 . 0) (410 . “Model”)
(8 . “0”) (62 . 2)(100 . “AcDbText”) (10 -0.0606988 -0.0385562 0.0) (40 .
0.078125) (1 . “52”) (50 . 0.0) (41 . 1.0) (51 . 0.0) (7 . “Standard”) (71 . 0)
(72 . 4) (11 0.0 0.0 0.0) (210 0.0 0.0 1.0) (100 . “AcDbAttribute”) (280 . 0)
(2 . “BUBNUM”) (70 . 0) (73 . 0) (74 . 0) (280 . 0))
 
Now we’re going to use a “substitution” AutoLISP function called “subst” along with a couple other cryptic fuctions “cons” and “assoc”. Type this at the Command: prompt.
 
Command: (setq new_data (subst (cons 62 1) (assoc 62 old_data) old_data)) [Enter]
((-1 . <Entity name: 7ffffb26940>) (0 . “ATTRIB”) (330 . <Entity name:
7ffffb26930>) (5 . “18C”) (100 . “AcDbEntity”) (67 . 0) (410 . “Model”) (8 .
“0”) (62 . 1)(100 . “AcDbText”) (10 -0.0606988 -0.0385562 0.0) (40 . 0.078125)
(1 . “52”) (50 . 0.0) (41 . 1.0) (51 . 0.0) (7 . “Standard”) (71 . 0) (72 . 4)
(11 0.0 0.0 0.0) (210 0.0 0.0 1.0) (100 . “AcDbAttribute”) (280 . 0) (2 .
“BUBNUM”) (70 . 0) (73 . 0) (74 . 0) (280 . 0))
 
Okay, now we have our new version of the entity’s data held in a variable called “new_data”.
 
One more step. We push this new version of the data back out to the entity. Type this at the Command: prompt.
 
Command: (entmod new_data) [Enter]

… and the attribute instantly changes from yellow (color 2) to red (color 1) !

autolisp005e

Wrapping all this into a little utility

That was instructive, but very painful. Let’s put all of these steps into a little ASCII text file, give it a “.lsp” extension, and APPLOAD it to use when needed.

Here it is using the Visual Lisp editor (any ASCII text editor can be used).

autolisp005g

APPLOAD the file and, when you need it, type ATTR2COLOR [Enter] at the Command: prompt. Change the color of as many “non-BYLAYER” colored attributes as you can pick!

July 16, 2009

Propagating AutoLISP variables drawing to drawing – the ( vl-propagate…) function

Filed under: Tutorials — nateholt @ 12:52 pm

The last posting gave an example utility to do a find/replace on substrings with MText entities. When going from one drawing to another in AutoCAD, this function could “remember” the previous find and replace substrings, even when AutoCAD was running in its normal “multiple drawing interface” mode.

This was made possible by using the VisualLisp “Blackboard”. The first time the utility was executed, the user’s “find” and “replace” substrings were pushed out to the blackboard. Then, on subsequent runs of the utility, possibly on other drawings later opened in the session, the utility would check the blackboard and retrieve any saved values. These then presented as defaults to the user.

There’s another way to do this, it works well and is a tiny bit simpler. It is to use the (vl-propagate…) function to preset a given AutoLISP variable to a preset value for all open drawings.  It eliminates the “check the blackboard” step. And, even cooler, it presets this variable for any future drawings opened during the AutoCAD session (see highlighted VisualLisp help shown below)!

mtext03

Both the blackboard and propagate methods are detailed here using the last posting’s AutoLISP utility as an example.

Drawing to drawing values – the “Blackboard” method

Here is a snippet from the original version of the utility. It deals with checking for a previous “find” substring value, previously saved on the blackboard as “save_find”. This check for “save_find” on the blackboard happens on the third line down – the call to the function “vl-bb-ref”.

; Look for previous find/replace substrings stored on the
  ; "black board". If found, present these as defaults.
  (setq default_find (vl-bb-ref 'save_find))
  (if (AND default_find (/= default_find ""))
    (progn ; default "find" string found from previous run
      (setq find default_find) ; default from previous run
      (setq find (getstring (strcat "\nFind [" default_find "]=") T))
      (if (= find "")(setq find default_find)) ; use previous
    )
  ; ELSE
    (setq find (getstring "\nFind=" T))
  )
  (if (AND find (/= find ""))
    ; Save the "find" string on the "black-board" for
    ; reference next time command is invoked.
    (vl-bb-set 'save_find find) 
  ) 

And, in the last line above, the current value of the “find” AutoLISP variable is pushed out to the blackboard with the call to “vl-bb-set”. This enables the “find” substring value to be retrieved on subsequents runs of the utility (using the vl-bb-ref function above!).

Drawing to drawing values – the “Propagate” method

 Here is the same snippet but using the “vl-propagate” function. It is a bit cleaner / simpler. The key thing to remember here is to NOT list the propagated AutoLISP variables as “local” variables in the utility’s definition.

; Look for previous find/replace substrings that have been
  ; "propagated" to Lisp variables "default_find" and "default_replace"
  ; in all dwgs of current session, whether open or yet to be opened.
  (if (AND default_find (/= default_find ""))
    (progn ; default "find" string propagated from previous run
      (setq find default_find) ; default from previous run
      (setq find (getstring (strcat "\nFind [" default_find "]=") T))
      (if (= find "")(setq find default_find)) ; use previous
    )
  ; ELSE
    (setq find (getstring "\nFind=" T))
  )
  (if (AND find (/= find ""))
    (progn ; Save the "find" string out to variable "default_find" for all
           ; other open drawings or those that will be opened during this
           ; AutoCAD session.
      (setq default_find find) ; set the value into "default_find"
      (vl-propagate 'default_find) ; preset variable "default_find" everywhere 
  ) )

What happens with this one call to (vl-propagate ‘default_find) is that this AutoLISP variable and its value at the moment the vl-propagate function is executed is suddenly visible within any other drawings open on the screen… AND this variable will magically appear preset to the initial value in any future drawings you open during your AutoCAD session!

Conclusion

There it is. I wish I had stumbled across vl-propagate a bit sooner. It would have simplified setting up an AutoLISP utility’s processing of multiple-drawing in a batching operation.

July 15, 2009

MText Substring Find/Replace Utility

Filed under: Tips — nateholt @ 9:02 am

Find and replace substrings within short or long MText entities. This little utility, put together quickly to meet a customer request, seems to be able to work around the way that an MText entity breaks up a long character string into 250-character blocks.

The user interface is bare-bones… basic command line prompts. But a cool thing is that it does “remember” the last entered find and replace substrings by saving them on the internal AutoLisp “black-board”. This means that these values survive moving from one drawing to another in MDI (multidocument) mode.

(Does such a utility already exist? Donno… but it’s fun writing one, even if it’s an exercise in reinventing a wheel.)

Before presenting this little tool, here is what drove the need for it.

The Scenario

Let’s say you’ve inserted a large MText entity on many dozens of drawings (example shown below). Buried within this text string is a specification number. You discover that the spec number is wrong!

 mtext01

At a minimum, you’d like to be able to just open each drawing, “hit a button”, and have the MText update to include the correct spec number (i.e. do a substring “Find/Replace”).

MText entity structure

The MText entity’s character string is stored in blocks of characters, about 250 characters each. If the MText string is short, there is one block. Longer strings… multiple blocks.

To illustrate using the above example… type this at the AutoCAD command prompt:

(entget (car (entsel))) [Enter] and then pick on the MText entity.

This display of the picked MText entity’s subrecords is displayed in the command window. The character parts are highlighted. Since the MText extends beyond 250 characters, there are multiple blocks defined (first one highlighted in yellow, the second in green).

mtext02

This complicates writing a little find/replace utility for MText substring replacement. We need to concatenate these internal blocks of characters, then do our find/replace, and then break them back up into 250-character chunks and push them back out to the entity.

An MText Find/Replace Utility

Here is the attempt at doing the above. The utility’s source code is reproduced below. The first half or so includes some internal funtions to parse a character string and insert replacement substrings. Starting about half-way down is the main program. It does an “ssget” to find all MText entities on the active drawing and begins to process each, one at a time. For each one it opens up the MText entity and creates one long string of text from the 250-char blocks held in the entity (there may only be one). Then it applies the find/replace string parsing function. Now, if any change was made by the find/replace function, it rebuilds the entity with the new text string… breaking it up into 250-character blocks and stuffing these back into the original MText entity.

One other thing of interest… the “find” and “replace” substrings that you type in to the command are “remembered” on the AutoLISP black-board. This means that these values survive going from one drawing to another in MDI mode. You still have to re-appload the function but the default substrings from the previous run will still be there.

; --- M T E X T _ F R . L S P  -------
(defun fr_txt_replace ( s what replace firsthitonly / cnt j chk hitit)
  ; replace "what" with "replace" in s. Case insensitive.
  ; firsthitonly = nil if replace all instances, /= nil if replace first instance only
  ; -- internal
  (defun do_strchr2 (str chx startfrom case_sensitive / 
        rpos pos xflag len ignore_case)
    ; Return char pos of first occurance of char "ch" in string "str"
    ; Supports chx strlen > 1
    ; and user gives starting place to look for chx, usually 1
    ; "startfrom" = 1 to start search from first character
    ; "case_sensitive" = 1=case sensitive, nil=case in-sensitive
    (if (not case_sensitive)(setq ignore_case 1)(setq ignore_case nil))
    (setq pos 0)
    (if (not startfrom)
      (setq startfrom 0)
    ; ELSE
      (if (> startfrom 0)(setq startfrom (1- startfrom)))  ; needs to be 0 index based
    )
    (if (AND str chx (/= chx "") (> (setq len (strlen str)) 0) (<= startfrom len))
      (progn
        (setq xflag nil)
        (while (AND (= xflag nil) (< pos len ))
          (setq xflag 1) ; default to exit loop
          (if (setq ixy (vl-string-search chx str startfrom))
            (progn
              (setq pos (1+ ixy)) ; return index is 0-based, increment by 1
          ) )                    
    ) ) )
    pos ; return char position or 0 if not found
  )   
  ; -- main routine --
  (setq cnt 1)
  (if (not what) (setq what ""))
  (if (not replace) (setq replace ""))
  (if (= what "")
    (if (OR (not s) (= s "")) (setq s replace))
  ; ELSE
    (progn
      (setq hitit nil)
      (while (AND (not hitit) (> (setq pos (do_strchr2 s what cnt nil)) 0))
        ; Hit a match, replace it
        (setq s (strcat (substr s 1 (- pos 1)) replace (substr s (+ pos (strlen what)))))
        (setq cnt (+ pos (strlen replace)))
        (if firsthitonly (setq hitit 1)) ; exit on first hit if option is "ON"
      )
    )
  )
  s
)
; -- MAIN PROGRAM STARTS BELOW --
(defun c:mtext_fr ( / ss find replace slen ix mtext_en oldstr ed newed
      code default_find default_replace cnt chars_to_output ixx newstr x
      chg_cnt)
                   
  ; Generic Find/Replace of MTEXT substring
 
  ; Look for previous find/replace substrings stored on the
  ; "black board". If found, present these as defaults.
  (setq default_find (vl-bb-ref 'save_find))
  (setq default_replace (vl-bb-ref 'save_replace))
  (if (AND default_find (/= default_find ""))
    (progn ; default "find" string found from previous run
      (setq find default_find) ; default from previous run
      (setq find (getstring (strcat "\nFind [" default_find "]=") T))
      (if (= find "")(setq find default_find)) ; use previous
    )
  ; ELSE
    (setq find (getstring "\nFind=" T))
  )
  (if (AND find (/= find ""))
    ; Save the "find" string on the "black-board" for
    ; reference next time command is invoked.
    (vl-bb-set 'save_find find) 
  ) 
  (if default_replace
    (progn
      (setq replace default_replace) ; default from previous
      (setq replace (getstring (strcat "\nReplace with [" default_replace "]=") T))
      (if (= replace "")(setq replace default_replace)) ; use previous
    )
  ; ELSE
    (setq replace (getstring "\nReplace with=" T))
  )
  (if (AND replace (/= replace ""))
    ; Save the "replace" string on the "black-board" for
    ; reference next time command is invoked.
    (vl-bb-set 'save_replace replace) 
  ) 
  (setq chg_cnt nil) ; track how many MTEXT ents changed 
  (setq ss (ssget "_X" '((0 . "MTEXT"))))
  (if (/= ss nil)
    (progn
      (setq slen (sslength ss))
      (setq ix 0)
      (while (< ix slen)
        (setq mtext_en (ssname ss ix))
        (setq ed (entget mtext_en)) ; open the MTEXT entity
        ; Mtext string is stored in "1" and "3" subrecords.
        ; Extract all of the pieces of the Mtext (there may only be one)
        ; and build one long character string.
        (setq oldstr "")
        (foreach x ed
          (if (OR (= (car x) 1)(= (car x) 3))
            (progn
              (setq oldstr (strcat oldstr (cdr x)))
        ) ) )
        (setq newstr (fr_txt_replace oldstr find replace nil))           
        (if (/= newstr oldstr)
          (progn ; update MTEXT entity. This may be tricky. Write
                 ; out blocks of 250 characters at a time. Start
                 ; with "3" subrecords if there are going to be
                 ; two or more required. Always end with a "1"
                 ; subrecord.
            (setq cnt (1+ (fix (/ (strlen newstr) 251.0))))
            ; cnt=1 if just one "1" subrecord takes care of the MTEXT
            ; character string. cnt=2 means a "3" and a "1". cnt=3
            ; means two "3's" and a "1"... and so on.
            (princ "\nOLD=")
            (princ oldstr)
            (princ "\nNEW=")
            (princ newstr)
            (if (not chg_cnt)(setq chg_cnt 0))
            (setq chg_cnt (1+ chg_cnt))
           
            ; Now rebuild the MTEXT character string records
            (setq newed nil)
            (foreach x ed
              (if (OR (= (car x) 1)(= (car x) 3))
                (progn ; hit first part of the MTEXT text string. Output
                       ; all of the revised character string.
                  (if (> cnt 0)
                    (progn ; output it now    
                      (setq ixx 0)
                      (repeat cnt
                        (setq chars_to_output (substr newstr 1 250))
                        (setq newstr (substr newstr 251)) ; strip off what is being output
                        (setq code 3)
                        (setq ixx (1+ ixx))
                        (if (= ixx cnt)
                          (progn ; this is the last block of characters.
                            (setq code 1)
                        ) )
                        (setq newed (cons (cons code chars_to_output) newed))
                      )
                      (setq cnt 0) ; flag to not repeat
                  ) ) 
                )
              ; ELSE
                (progn ; anything other than char subrecord
                  (setq newed (cons x newed))     
              ) )  
            )
            ; Now update the MTEXT entity with the new collection
            ; of subrecord data
            (setq newed (reverse newed)) ; put back in original order
            (entmod newed)
            (entupd mtext_en)
          )
        )     
        (setq ix (1+ ix))
      )       
      (setq ss nil)
    )
  )
  ; Give some indication of change count
  (if (not chg_cnt)
    (princ "\nNo changes")
  ; ELSE
    (progn
      (princ "\nMTEXT modified: ")
      (princ chg_cnt)
  ) )
)
(Princ "\nType MTEXT_FR [Enter] to run.")
(princ)

If you’d like to test this utility, cut and paste from above into an ascii text file called mtext_fr.lsp. Back up your drawing!!! Then, APPLOAD the “.lsp” file. Type MTEXT_FR [Enter] at the command prompt.

UPDATE: customer reports success. He modified the above utility. He hard-coded the find/replace text substrings instead of having the (getstring…) calls to prompt for the values [in other words, he used (setq find “somestring”) and (setq replace “newstring”)]. Then he created a two-line script file and referenced this in AutoCAD Electrical’s “Project-Wide Utilities” command. Ran it against his project… issue resolved.

July 6, 2009

Electrical Idol contest – 2006

Filed under: Musings — nateholt @ 8:46 pm

(note: this posting / contest from original blog. Appeared in May 2006)

I’ve been around since the 50’s so It’s rare for me to experience three totally brand-new things in one week. But this was one of those weeks. The first two will probably become known eventually but the third, well, it’s just too good to keep mum about…

Announcing… the Controlling the Machine blog’s “Electrical Idol” contest! This should be fun…

Last night, sitting in my hotel room (on the road this week), I scratched out lyrics for a theme song for AutoCAD Electrical. But I have no talent plus no one else handy who seems willing to do the required crooning part (see below). So, I’d like to run a fun contest where Blog readers record it, send it in, I post it here, and then we’ll figure out a way to determine who will become the “Electrical Idol”.

I’m thinking of even awarding prizes: 0.5W Resistors!
Grand prize: 33,000 ohms
1st Runner-up: 2,000 ohms
2nd Runner-up: 1,000 ohms

You don’t have to have talent or instruments for the recording. It is probably best done acappella. Lyrics below
UPDATE: added a 4th verse.

(suggest it be done to the tune of “Don’t Worry, Be Happy”).

(snapping fingers to establish beat throughout)

(whistle chorus to begin)

The engineer, he come to say
Switch re-lay with time de-lay
Don’t worry…. A-Cad-E
The re-lay con-tact count too low
Too many N.C., no more N.O.
Don’t worry…. A-Cad-E
Don’t worry A-Cad-E now.

[CHORUS]
(crooning)
Don’t worry
(crooning)
A-Cad-E
(crooning)
Don’t worry A-Cad-E
(crooning harmonizing)
Don’t worry
(crooning harmonizing)
A-Cad-E
Don’t worry A-Cad-E

Shop foreman come to call you nuts
Com-po-nent tag name dup-li-cates
Don’t worry…. A-Cad-E
A finger in your face he wags
About those missing wire tags.
Don’t worry…. A-Cad-E
Don’t worry A-Cad-E now.

[CHORUS]

The customer, he spec’d Square-D
Now on the phone says Allen-Brad-ley
Don’t worry… . A-Cad-E
The project name, you thought was locked
Now gotta edit thousand title-blocks
Don’t worry…. A-Cad-E
Don’t worry A-Cad-E now.

[CHORUS]

Design changes at your door
After it hit the shop room floor.
Don’t worry…. A-Cad-E
The en-gi-neer with face of woe
Twelve more P L C I/O
Don’t worry…. A-Cad-E
Don’t worry A-Cad-E now.

[CHORUS]
(fade)

UPDATE – only one submission. Listen to it here.

July 5, 2009

How to undo the “Do not show me this again” selection

Filed under: Tips — nateholt @ 11:15 pm

Have you thought this?… “Gee, if I select ‘Do not show me this again’… it seems so permanent. What if I someday I regret this, I want to change my mind. Am I doomed?…”

I felt that fear at one time in my life, but no more. Go ahead. Hit the toggle with an easy conscience knowing that, in this case, “never again” doesn’t have to be forever.

Example

Here’s an example (provided by Autodesk’s Pat Murnen)… the Terminal Strip Editor tool may sometimes throw this alert message:

undo_donot_show_01

 

If I don’t want to see this message for the next couple days while working on this old project… but don’t want to lock myself out for eternity, can I hit the “Do not show me…” toggle?

Answer: Yes

No problem. When you’re ready to restore this or any other accumulated “no shows”, just get into the normal AutoCAD “Options” dialog. Select the “System” tab and then the “Hidden Message Settings” button.

undo_donot_show_02

 

This sub-dialog pops up showing you which are your system’s hidden messages. Toggle the ones you want to start seeing again.

Redemption! It’s so good.

Nate’s Simple AutoLisp – Lesson 004

Filed under: Tutorials — nateholt @ 11:31 am

(note: first appeared October 2008 on old blog)

Geeze, it’s been over two years since lesson “003”. Busy times. Anyway, we’re back. If you have 10 minutes to kill and want to take a chance with an exciting, hands-on experience, then read on.

Warning/Disclaimer: I’m neither a professional teacher nor am I well versed in the theory behind AutoLisp programming. But even beginners can come up with something useful and… best of all, have fun at the same time!

Here’s what we’ll do in the next 10 minutes. We will build up, step-by-step, a useful program consisting of just one line of code. Then we’ll add it as a button on the AutoCAD menu. Note: We’re using AutoCAD Electrical as our example application but this applies to AutoCAD in general.

Step-by-Step… A Really Simple “Wire Layer” Lister

We just want to have a lead-pipe simple tool to quickly list the layer name of the picked object, let’s say a series of AutoCAD Electrical wire segments, one at a time, boom…boom…boom. We don’t want a screen-full of data about the object, we don’t want some kind of property dialog to display, we just want this one piece of info and we want it fast.
Just for fun, let’s start develop this little tool with nothing more than the “Command:” window. With your AutoCAD schematic drawing active on screen, let’s start here:
Command: (entsel) [Enter]
Select object: [pick on wire segment, return=] (<Entity name: 7ef05f60> (19.5858 16.9905 0.0))
 
The (entsel) function returns a two-element list: the first element is the entity name of the picked line segment and the second element is the XY coordinate of the mouse pick point. We just want the entity name, the first element of this returned list. Use the AutoLISP “car” function to extract the first element. So let’s next try this:
 
Command: (car (entsel)) [Enter]
Select object: [pick on wire segment, return=] <Entity name: 7ef05f60>
 
Remember, gotta keep those “(” and “)” balanced out. For each “(” you add, you need to balance it with a “)”. Okay, good so far. Now let’s modify our “program” to open up the selected line segment with a call to “entget”
 
Command: (entget (car (entsel))) [Enter]
Select object: [pick on wire segment, return=] ((-1 . <Entity name: 7ef05f60>) (0 . “LINE”) (330 . <Entity name: 7ef05c10>) (5 . “6C”) (100 . “AcDbEntity”) (67 . 0) (410 . “Model”) (8 . “RED_18AWG”) (100 . “AcDbLine”) (10 20.0 17.0 0.0) (11 18.5625 17.0 0.0) (210 0.0 0.0 1.0))
 
Wow, got a lot more information than we really need. The part we want to focus in on is the “8” subrecord which is used to carry the entity’s layer assignment. We’ll use the “assoc” call to filter out all but the first instance of the target subrecord… 
Command: (assoc 8 (entget (car (entsel)))) [Enter]
Select object: [pick on wire segment, return=] (8 . “RED_18AWG”)
 
Really close. The layer name is in the second part of this return. We can use the “cdr” function to strip off the first part of this list of data…
 
Command: (cdr (assoc 8 (entget (car (entsel))))) [Enter]
Select object: [pick on wire segment, return=] “RED_18AWG”
 
Okay, we did it. Pick entity, get layer name instantly. But it only does one at a time. Let’s add a “while” loop around our little program like this:
 
Command: (while (princ (cdr (assoc 8 (entget (car (entsel))))))) [Enter]
Select object: RED_18AWG
Select object: RED_18AWG
Select object: BLK_14AWG
Select object: BLK_14AWG
Select object:
; error: bad argument type: lentityp nil
Command: *Cancel*
 
Nice. We could stop now. We’re can pick,pick,pick and get instant gratification with each pick. It doesn’t look too pretty when we decide to kick out with [ESC] or pick in empty space. Let’s do just a tiny clean-up so that it exits nicely when we just pick in empty space to finish. We will store the return from (entsel) in variable “x”. If valid, then we go ahead and dig out the layer name of “x” and “princ” it out to the command window. The extra “(princ)” at the very end also helps give a clean return.
 
Command: (while (setq x (entsel))(princ (cdr (assoc 8 (entget (car x))))))(princ) [Enter]
Select object: BLK_14AWG
Select object: RED_18AWG
Select object: RED_18AWG
Select object: BLK_14AWG
Select object:

Nifty.

Now let’s add our cool tool to the pull-down menu so we don’t have to type it in each time. Type CUI to bring up the standard “Customize User Interface” dialog. Select “ELECTRICAL” and “Menus” for the pull-down menu customization. Let’s put our new tool under the “Wires” pull-down. So expand “Wires” as shown here.

lisp004_01

  Highlight an existing command, let’s say “Insert Wire” and right-click. Pick “Duplicate”. Now you’ll end up with two “Insert Wire” entries, one above the other. Click on the new, lower one and begin to edit it.
 lisp004_02

Change the Display name, description. Carefully enter in your new program in the “Macro” line as shown here. The “^C^C^P” prefix is not absolutely necessary but does serve a useful purpose. If you are in some existing command and then pick on this new menu item, the ^C^C part will cancel out of the previous command before starting your new command. Be careful about NOT adding in extra spaces. These can sometimes be interpreted as an [Enter] keystroke when the “Macro” string is executed.

lisp004_03

Save the CUI changes. Now, go to your “Wires” pull-down menu.

WOW! There it is! Give it a try!

lisp004_04

 

BUT… this cool utility is encoded right into my current version of the menu. Could be easy to lose and a hassle to remember it and to type it back in 100% correctly. Let’s make it a bit safer – put the one-line program into an ASCII text file, call the file “cool.lsp”, and just reference this file from our new menu entry instead of having to encode the program itself into the menu entry.

The key here is to create the cool.lsp ASCII text file and save to some folder that is one of the support paths defined for AutoCAD (or a new support path you add for your cool utilities). This way, a simple call to the AutoLISP function “findfile” will find it.
After you create the one-line ASCII text file called “cool.lsp”, do this: test at command line to make sure it works. Type this string:
Command: (if (setq x (findfile “cool.lsp”))(load x)) [Enter]
Select object: BLK_10AWG
Select object: BLK_10AWG
Select object:
 

Okay, the above test worked. It found, loaded, and executed the one line of code. It worked! Now edit the menu button and encode this “findfile” command string into it. To do this, type CUI and find your “Cool layer lister” entry. Change the macro string to match below. OK out of CUI command and test the menu button.

lisp004_05

 

Time’s up! Was that fun or what… ? ! ! !

Nate’s Simple AutoLisp – Lesson 003

Filed under: Tutorials — nateholt @ 11:24 am

(note: this first appeared in August 2006 on the old blog)

Warning/Disclaimer: I’m neither a teacher nor am I versed in the theory behind AutoLisp programming. But I invite you to come along for a ride. This stuff is not only fun, but it can be useful ! Each lesson should take 5-10 minutes.

So, here we go…

Lesson 003 – Reusable Programs – Saving as an Executable File

In the first two lessons we were just typing and executing AutoLisp expressions from the command prompt. Now we’ll go the next step and create our first program saved as an “executable” file.

Here’s where we ended the last lesson: a single line AutoLisp expression that would display the block name of a picked block insert.

nate_lesson002c.jpg

Today let’s take this one line expression, expand it a bit to make it more readable, and save it to an executable file. Then we can load and run this utility any time we want.

Let’s use the built-in Visual Lisp editor.

1. Type VLIDE [Enter] at your AutoCAD command line.

2. Select “File” > “New File” > “File” > “Save As” and give it a file name of BN.LSP

3. Now type in the code shown below starting with the line “(defun c:bn …. )”

lesson_003a.jpg
OK. This “program” is pretty much the same as our original one-line expression we typed in at the command line. It’s just a bit cleaner and prettier looking, PLUS, we’ve saved it to a file so we can call it up and run it at any time.

Let’s test it and then we’ll talk a little bit about it…

4. Select “Tools” > “Load Text in Editor”. This loads your program.

5. Now let’s give it a test. Mouse back to the AutoCAD command window. Type BN [Enter] at the command prompt. This should execute your program. If all is well, you’ll see a “Select block insert:” prompt. Pick on a block insert and, hopefully, your program will execute to the end and display the insert’s block name.

Here’s the minimum information you should be aware of at this point:

a) The first line, “(defun c:bn…)” is where we’re defining our own custom command (function) called “bn”. The c: part has nothing to do with a drive on your computer, it means that this is an external command being defined.

b) Whenever you see a “setq” key word, this means that some variable is getting assigned some value. The variable name follows the setq key word and the value follows the variable name.

c) The “princ” keyword instructs the program to output to the command line whatever follows this keyword, whether it be a fixed text string, numeric value, or contents of some variable.

d) The number of open parenthesis ” ( ” and closed ones ” ) ” need to be balanced. If not, your program will not load or will choke when it runs.

There’s more but our 10 minutes are about gone… we have to hurry.

Our program runs one time and then quits. Let’s make a simple modification to make it “loop” around so we can keep picking. When we’re done, we can just pick in “empty space” or hit [ESC] key to quit.

So, here’s our modified program. We’ve added a “while” key word and added another set of parenthesis to keep everything balanced:

lesson_003b.jpg
Now when we load and run it, it is a bit more useful.

Finally, let’s exit the Visual Lisp environment and try APPLOADing and running our new program. In other words, we do not need to invoke the Visual Lisp editor to run our program. We can just do a normal APPLOAD and off we go.

1. Exit Visual Lisp. Now you should be back at the AutoCAD command prompt.

2. Type APPLOAD and browse to your saved copy of bn.lsp. Load it.

3. Type BN at the command line. It should run, just like before…!

Okay, time’s up. Next lesson we’ll push a bit further into this fun stuff.

Nate’s Simple AutoLisp – Lesson 002

Filed under: Tutorials — nateholt @ 11:22 am

(note: this originally appeared in July 2006 on the old blog)

Warning/Disclaimer: I’m neither a teacher nor am I versed in the theory behind AutoLisp programming. But I invite you to come along for a ride. This stuff is not only fun, but it can be useful ! Each lesson should take 5-10 minutes.

So, here we go…

Lesson 002 – First peek at Block Inserts

AutoCAD Electrical makes heavy use of standard AutoCAD blocks with attributes. There is a lot of neat stuff here, so let’s get started.

Just so we can stick together, download this sample drawing here and open it up. Its file name is lesson_002.dwg.

Let’s go back to where we left off in the last lesson. We were picking a LINE entity with entsel and opening it up with entget to examine its internals. Let’s enter the same AutoLisp expression as before but this time let’s pick on a block insert entity instead of a line entity.

1. At the Command: prompt type this… (entget (car (entsel))) [Enter]

2. Pick on the limit switch symbol LS406 in the upper left-hand part of the drawing. Now you’ll see a bunch of data displayed in your command window. It will look something like this:

nate_lesson002a.jpg

This is a list of sublists of data defining the base part of the block insert. Each element of this list starts with a group code number followed by the value for that group code.

Group code 0 gives the entity type. Find this entry: (0 . “INSERT”) in the data returned. This tells us that we picked and opened a block Insert instance.

Group code 8 identifies the base layer that this block instance is inserted on. In the example here it is layer “SYMS”.

Group code 2 gives the block name, “HLS11″.

Group code 10 for an INSERT entity gives the block’s insertion X,Y,Z coordinate. For our picked instance of “HLS11″ the returned data says that it is inserted at XY coordinate 3.875,16.0.

Group codes 41-43 give the block insert’s X,Y,Z scale factor values and 50 gives the rotation angle.

Let’s figure out how to write a single-line AutoLisp expression to just give us the block name of the picked entity. Somehow we have to extract the group code 2 value from this big list of data.

There is a base AutoLisp function called assoc that can be of great help to us here. We can pass the group code number we want and the full list of data to search. The assoc function will return just the part of the list that matches the specific group code we pass to the assoc function. Let’s try it. We want to get the insert’s block name, this is carried in group code 2:

3. Type this at the AutoCAD Command: prompt…. (assoc 2 (entget (car (entsel)))) [Enter] and pick on the limit switch block insert. You will see this returned to the command line prompt:

nate_lesson002b.jpg

Hmm… we’re close. But we need to get rid of the first part of this list and just leave the block name. There is another AutoLisp function, (cdr …. ) that discards the first element of a list. Let’s try it.

4. Type this at the Command: prompt… (cdr (assoc 2 (entget (car (entsel))))) [Enter]. Make sure you end with five “)” to keep the “(” and “)” counts balanced. Pick on the limit switch again. Now we get a nice clean return.

nate_lesson002c.jpg

Okay, time’s up. Next lesson we’ll eliminate the need to type in this AutoLisp expression into the command line every time we want to run this “program”. We’ll create a text file version of this program and learn how to “APPLOAD” it. This will let us quickly run it whenever we want.

Nate’s Simple Autolisp – Lesson 001

Filed under: Tutorials — nateholt @ 10:42 am

(note: originally appeared July 2006 on the old blog)

Warning/Disclaimer: I’m neither a teacher nor am I versed in the theory behind AutoLisp programming. But I invite you to come along for a ride. This stuff is not only fun, but it can be useful ! Each lesson should take 5-10 minutes.

So, here we go…

Lesson 001 – Picking stuff

We’ll start out simple, nothing fancy. We’ll just type and run AutoLisp expressions right at your AutoCAD “Command:” line prompt.

1. Open up an AutoCAD drawing. Draw a LINE entity anywhere.

2. At the Command: prompt type this… (entsel) [Enter]

nate_lesson_001a.jpg

3. As soon as you hit the [Enter] key, AutoCAD evaluates what you’ve typed on the command line. The word “entsel” inside of the open and close parenthesis is a predefined AutoLisp function for picking stuff, one thing at a time. It defaults to a “Select object:” prompt. Now pick on the LINE entity in your drawing.

You will see something like this displayed on your command line:

nate_lesson001b.jpg

It is a “list” of two pieces of data. Only the first element is of interest to us now. It’s a numeric code that gives the entity name identifier of the LINE entity you selected.

Okay, let’s dig a bit deeper.

We need to isolate this entity name from the two-element list returned by (entsel).

4. Type this at the command line prompt: (car (entsel)) [Enter]

5. Pick on a line entity. Now you’ll see the entity name code all by itself. The “(car … )” AutoLisp function returns the first element of a multi-element list.

nate_lesson001c.jpg

Now, the cool part. We’re going to “open” up and display the guts of a picked entity.

6. Type this at the command line prompt: (entget (car (entsel))) [Enter]

7. Pick on a line entity. Now you’ll see a bunch of data displayed in your command window. It will look something like this:

nate_lesson001d.jpg

At first it just looks like gibberish. But there is some interesting stuff in here. Let’s poke around.

This is the basic data about our picked LINE entity. It is a “list” of stuff. Each element of this list starts with a group code number followed by the value for that group code.

Group code 0 give the entity type. Look at the data and pick it out. You’ll see (0 . “LINE”). So, we picked on a LINE entity.

For a LINE entity, group code 10 gives the line’s staring X,Y,Z coordinate. Look for the (10 ….) entry in the data dump. The data following the 10 is the starting X,Y,Z coordinate. Group code 11 gives the ending X,Y,Z coordinate.

Group code 8 gives the layer name that the entity is inserted on.

Okay, that’s all for now. Time’s up. Hope to see you for the next lesson!

Chinese New Year’s good luck falls a bit short for US-based Autodesker

Filed under: Musings — nateholt @ 10:17 am
(note: this appeared in Feb 2008 on the old blog)
(Singapore, 07-Feb-2008): New-year’s day good fortune appeared to take a momentary lunch break as Autodesk’s Nate Holt and three companions were strolling down the pleasant, nearly deserted Orchard Road boulevard in Singapore in search of an open restaurant. “I just heard a whoosh”, said Autodesk’s Novi Michigan-based Jared Bunch, “and caught a glimpse of a shadow falling from the sky”. Both Lake Oswego-based Jose Santos and Novi’s Mike Spitzer were able to jump out of the way. Not so Toledo-based Nate Holt, AutoCAD Electrical Architect. The business end of a large palm branch grazed his head and neck and struck him on the shoulder. Twisted/broken glasses went flying.  

Fortunately, due to the quick thinking of Jose and Mike, a photographic record of the incident was preserved (though somewhat dramatized). As there was no permanent injury, the quartet repaired to a nearby restaurant to celebrate “Mr. Lucky’s” Chinese New Year good fortune.

mr_lucky

Solar eclipse – but not from anywhere around here

Filed under: Musings — nateholt @ 9:57 am

(note: this originally appeared in March 2007 on the old blog)

Wow, this is pretty impressive.

eclipse_moon_sun.jpg

A solar eclipse when viewed from the earth is far different from this. Standing on earth and looking up, the apparent size of the moon and the apparent size of the Sun are almost exactly the same. But move the camera back, way back, so that the moon is five times farther way from what we’re used to seeing (i.e. a camera on a spacecraft orbiting the sun)… then this is what a solar eclipse looks like.

Short article is here.

Pretty cool.

UPDATE: and here’s another view of a solar eclipse… in 1999 looking down from the Mir space station. Again, pretty cool!

eclipse99_mir_big

Solar Storm – Dec 7, Dec 14 2006

Filed under: Musings — nateholt @ 9:55 am

(note: this originally appeared in December 2006 on the old blog)

My phone went momentarily dead this morning. I was thinking “sunspots” as a joke… but who knows…!

Here’s today’s lead headline on this site:

http://www.spaceweather.com/

RADIATION STORM: A radiation storm is underway. Based on the energy and number of solar protons streaming past Earth, NOAA ranks the storm as category S3: satellites may experience single-event upsets and astronauts should practice “radiation avoidance.”

The rush of protons may be a sign of an approaching CME (coronal mass ejection). Protons are accelerated in shock waves at the leading-edge of CMEs, so when the proton count rises, we can guess that a CME is en route. Northern sky watchers should remain alert for auroras, which could flare up if and when a CME arrives.

sunspot.jpg

This is interesting stuff.

UPDATE: http://www.space.com/scienceastronomy/061213_solar_storm.html

UPDATE: here is comparision between Wednesday evening and Thursday evening:

http://www.space.com/spacewatch/aurora_cam.html

Wednesday, Dec 13

wed_space_cam.jpg

Thursday, Dec 14

thur_space_cam.jpg

Man-made Sun Spots

Filed under: Musings — nateholt @ 9:52 am

(note: originally posted September 2006 on the old blog)

This is pretty cool. This telescopic image of the sun’s disk was reportedly snapped in Normandy by French astro-photographer Thierry Legault.

sun290906_468x460.jpg

It shows two man-made (!) “sunspots”, the space shuttle and the International Space Station orbiting the earth together.

sunspot_636x700.jpg

So, question is this… how long did Mr. Legault have to snap this picture? Hours, minutes, or seconds? Let’s figure it out.

From grade school, stuff in low-earth orbit takes about 90 minutes to orbit the earth. The sun is 93 million miles away and is about 850,000 miles in diameter. This works out to an apparent width of a half degree or so. That’s all we need. It takes the shuttle 90 minutes to orbit the earth, or go through 360 degrees. It covers the sun for a half degree of that travel (assuming lucky enough to pass right through the center). So, it seems that all we’d have to do is divide 720 into 90 minutes. This would yield about 1/8 minute (7 seconds maximum) that the shuttle is silhouetted against the sun’s disk. Sound about right? Have to be quick to not miss this awesome shot…

Five years ago

Filed under: Musings — nateholt @ 9:46 am

(originally posted on Sept 11, 2006)

Some weeks afterwards , a friend wanted me to go with him to the site. We jumped in his pick-up and drove all day. Spent the next day there and then drove back. Unforgettable.

Something that tore at my heart on that day five years ago is here

Ice-cube Tray Phenom

Filed under: Musings — nateholt @ 9:40 am

(note: originally appeared in July 2006 on the old blog)

I’ve noticed some strange things growing in my ice-cube tray. It doesn’t happen all the time, maybe one out of every 40-50 cubes. Here’s a two-fer:

ice_spikes_002.jpg

The valve controlling the refrigerator’s ice maker broke and I was too cheap to fix it… went back to the old-fashioned manufacturing method of low-tech ice cube trays. But I did install an under-sink reverse osmosis water filter and started creating extra-clean cubes. Taken together, this weird phenomenon began to appear. Ice spikes shooting straight out of the tops of random cubes at odd angles. Defying gravity.

ice_spikes_001.jpg

Had to figure this out. What’s the explanation?

Water expands as it freezes, about 10% (remember grade school and the iceberg talk?… they look big but another 90% is hidden below the surface…). This had to be a clue.

Let’s say I put the ice cube tray into the freezer. The top of the water surface is exposed to the cold air blowing over it in the freezer compartment and starts to freeze quickly. The liquid water below drops more slowly toward the freezing point and starts to expand as it closes in on the freezing point of water at 32F / 0C. But the top surface of the cube is already pretty much frozen, so pressure builds and something has to give. In most cases the whole top surface of the semi-frozen cube pushes up a bit. Look at the close-up above. The cubes on the left-hand side are especially pushed up.

But, in rare cases… maybe there is a tiny weak spot in the top of the cube that the slightly warmer water, under pressure due to expansion below, oozes up through. As this water oozes out, the outer surface of the “ooze” keeps freezing while the inner water oozes up even higher. Maybe this process builds these weird looking ice needles.

UPDATE: I am not (totally) nuts.

Older Posts »

The Shocking Blue Green Theme. Create a free website or blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

Join 25 other followers