Jump to content

MullinRJ

Member
  • Posts

    2,007
  • Joined

  • Last visited

Everything posted by MullinRJ

  1. @Sam Jones, Just remember that the (0,0) point inside a symbol definition is the Symbol's Insertion Point. In Pat's example, and you place the Locus outside the Symbol and want it in the same relative position inside the symbol, move the Locus by MINUS the symbol's insertion point. Try this. PROCEDURE xxx; { Move Locus into a Symbol at the same position as it was outside the symbol. } { Place a symbol, place a locus, select both, run script. } VAR Hsym, Hsdef, Hloc :Handle; Psym :Point; BEGIN Hsym := FSActLayer; Hsdef := GetObject(GetSymName(Hsym)); Hloc := LSActLayer; GetSymLoc(Hsym, Psym.x, Psym.y); if SetParent(Hloc, Hsdef) then begin HMove(Hloc, -Psym.x, -Psym.y); ResetBBox(Hsdef); SysBeep; { make noise when done } end; { if } END; Run(xxx); Are you writing code that needs to work if the User Origin is shifted? If so, I could write a lot more on how to manage coordinates between Drawing Layers and Symbol Definition spaces. If you also want your code to work in Rotated Plan View (ugh), I can help you there, too, but you'll need a good night's sleep and a large bottle of Aspirin. Merry Festivus 😉 Raymond
  2. @Jesse Cogswell, Wow, you posted THE question I had yesterday, before it left my head! SPOOKY!!! I tried everything you mentioned above with the same result – ¡NADA! If the standard palette names do exist somewhere I have not found them. This seems like a necessary function set to have. If you post a VE, I'll definitely upvote it. I'd really like Get/SetPaletteVisibility() to work on all of the standard palette names. I could have used it as soon as yesterday. Thanks, Raymond
  3. Hi @Pat Stanford, Thank you for the quick answer. It seems all the the *WSSubRow* VS commands are Read Only. Sad – another group of GET commands without the corollary SET commands. I understand *WSSubRow* value commands being Read Only, but *WSSubRow* attribute commands should be Read/Write. I'll file a VE in the morning. Thanks once again, Raymond
  4. Is there a way to set the height of a single database row via Vectorscript? SetWSRowHeight() only takes integer values for Rows. As an example, I want to set database row 5.1 to a height of 0 and leave all the other 5.x database rows alone. I can do it manually, but I want to do it in a script. I assume this is not possible but I hope I am wrong. TIA, Raymond
  5. Hello @stayathomedad, One command you might want to use in your script is: GetObjectVariableInt(LyrHnd, 154)); { return the layer type of the layer referenced by LyrHnd } where LyrHnd is a handle to a layer. This function will return a value of 1 for Design Layers, or 2 for Sheet Layers. Use this to test the layer handle before you delete it. For example: Message(GetObjectVariableInt(FLayer, 154)); { return the layer type of the First Layer in the drawing } will always return 1, because there is always a design layer in a file and design layers are always stored below sheet layers. Conversely: eg. - Message(GetObjectVariableInt(LLayer, 154)); { return the layer type of the Last Layer in the drawing } can return 1 or 2. If there are any sheet layers in a drawing, the Last Layer will always be a sheet layer and the function will return 2. If there are no sheet layers in a drawing, then the Last Layer will be a design layer and the function will return 1. HTH, Raymond
  6. You were very close. The only thing missing is the last line. You need to tell the function to return its answer with: GetTextBlock := ht; I assume ht is a global variable? For transportability, you should make ht a local variable like this: FUNCTION GetTextBlock(h:HANDLE):HANDLE; VAR ht :Handle; BEGIN Locus(0,0); h:=LNewObj; ht:=PrevObj(h); SelectObj(ht); DelObject(h); GetTextBlock := ht; END; {gettextblock} As you've written your code, do not delete ht from your global variables. ht can be both a local and global variable, as long as it doesn't confuse you. So with the above modified function, you will still write: ht:=GetTextBlock(h); in your main code. Raymond
  7. @SamIWas, This first code snippet is an annotated, verbose version of Waldo, as a function. It returns a handle to the FIRST SELECTED OBJECT in your current editing window so that your scripts will work even when you are not editing on a Design Layer. To use, place a copy of the Waldo function at the top of your code and replace H := FSActLayer; with H := FSWaldo; in your code. Modify this function as needed to achieve your desired functionality. Function FSWaldo :Handle; { Return a handle to the first selected object in the present context of the drawing window. } Var H, H1 :Handle; Begin Locus(0, 0); { drop a breadcrumb, a Locus } H := LNewObj; { H now contains a handle to the Locus } hMoveBackward(H, True); { move it to the bottom of the stacking order, but stay within the current container } H1 := NextSObj(H); { get the handle to the next selected object above your locus, or NIL if there are none } DelObject(H); { discard the locus } FSWaldo := H1; { return the handle in H1 } End; { FSWaldo } Here are two streamlined versions of Waldo to replace the FSActLayer and LSActLayer functions. These WALDOs will enable your scripts to work in any window you can open in Vectorworks. Function FSWaldo :Handle; Begin Locus(0, 0); hMoveBackward(LNewObj, True); FSWaldo := NextSObj(LNewObj); DelObject(LNewObj); End; { FSWaldo } Function LSWaldo :Handle; Begin Locus(0, 0); LSWaldo := PrevSObj(LNewObj); DelObject(LNewObj); End; { LSWaldo } @SamIWas, if you would, please describe where you got confused in your previous implementation. It may help me describe this more clearly in the future. HTH, Raymond
  8. Hi @klinzey, Last week I tested how the order of objects are returned by ForEachObject() to clarify how it responds in Single-Layer vs. Multi-Layer modes. I found in Single-Layer mode it returns objects in Stacking Order, bottom of stack first, top of stack last; but in a Multi-Layer mode (Show-Snap-Modify Others) it still searches objects on a layer the same (bottom to top) but it searches the layers top to bottom. This was not obvious or expected, but I'm glad I finally looked. I hope this helps others who are curious about how ForEachObject() works. Raymond
  9. Have you ever heard of WALDO? No, you are not missing anything in the VS API. There is no command to get a handle to any object when you are inside a container object (SymDef, Group, Sweep, etc.) But there is WALDO. He's very useful when you want to run a script and you are editing inside a container object. WALDO works like this: 1) Drop a 2D Locus anywhere – (0,0) is as good a place as any. 2) Get a handle to the Locus with H := LNewObj; 3a) Get a handle to the previous object with H1 := PrevObj(H), or H1 := PrevSObj(H). Now you are at the top of the stacking order. or 3b) Move the Locus all the way to the back and get a handle to the next object with H1 := NextObj(H) or H1 := NextSObj(H). Now you are at the bottom of the stacking order. 4) Delete the Locus with DelObject(H). WALDO (if you make it a function) returns H1. Your scripts can now run INSIDE container objects if you use WALDO instead of FActLayer, or FSActLayer, or LActLayer, or LSActLayer. Waldo works on Design Layers, too, so you don't have to worry about where you are when you run a script. Welcome to Waldo World, Raymond
  10. Hey, Pat. Pity, there are too may ways to recalculate a WS. You are right, it doesn't work if a WS isn't open for editing and its image gets recalculated. As someone who doesn't use WSs very much, that is not the way I typically work, but I am in the minority in this regard. If there is a way, I hope you find it. Raymond
  11. Hello, @Thomas K., and @Pat Stanford. Does this work? =RUNSCRIPT('GetWSName') where 'GetWSName' is a one line VS: WSScript_SetResStr(GetName(GetTopVisibleWS)); I hope I understand your question correctly. Pat, if you don't mind me heeding your recent advice, "This is exactly the type of problem that WSScripts were intended to solve." I think you nailed it. Raymond
  12. Hello @Gibson431, Welcome to the club. On your first At Bat, it's pretty much a given that nothing will make sense, and you are missing something, maybe everything. However, this is normal and no reason to turn around. There's only one way to go, and it's INTO the Rabbit Hole. There are many people down here and you're sure to get good advice on your journey. You are correct, when event 5 (initProperties) is issued, there is no geometry yet, so there is nothing for GetCustomObjectInfo() to return, except FALSE as the result. One thing I'd recommend you do is put an AlrtDialog() at the very front of your code, before the CASE statement and have it report the theEVENT and the theData (or theButton in the following example) for each execution of your code, the two pieces of info that are passed to your code every time it executes. This will show you what events are being sent and in what order, for every thing you do to your object (like place it, move it, rotate it, duplicate it, edit an OIP field, press a Widget Button, etc.) You'll be amazed how many time your script runs in response to every single user action, and which events are issued for each user action. I know, you want to have it draw something first so you can poke it, but trust me, knowing what events are coming, and in what order, will help you get there. For starters, just draw a Rectangle at the origin. Place the Rect() code in Event 3 (kResetEventID), then watch the show as you see the events file in. Here is a stripped out shell of an Event Enabled Object I've built in the past. There are things in it that need to be there and I can't explain all of them or what happens if they are not there. Much of the following was handed to me in dribs and drabs and I use it as a stepping stone to create new objects. Defining which procedures go into which events may require you to write back (a lot), but that is encouraged. Some are obvious, some are not. Trial and error are your friends. PROCEDURE YourCodeName; CONST kResetEventID = 3; kObjXPropPreference = 4; kObjOnInitXProperties = 5; kObjXPropHasUIOverride = 8; kObjXHasCustomWidgetVisibilities = 12; kObjXPropAcceptStates = 18; kObjOnObjectUIButtonHit = 35; kObjOnWidgetPrep = 41; kObjOnAddState = 44; kWidgetButton = 12; { 12 = Button WIdget } buttonID_1 = 1001; { user-definable index } buttonID_2 = 1002; { user-definable index } buttonID_3 = 1003; { user-definable index } VAR H, PIOHand, recHand, wallHand :Handle; result, IsNew, WidgetChanged :Boolean; WidgetIndex :Integer; theEvent, theButton, DoesNothing, WidgetID, WidgetMenuPos :Longint; PIOName, OldSel :String; P0 :Vector; BEGIN { MAIN LOOP } result := GetCustomObjectInfo(PIOName, PIOHand, recHand, wallHand); IsNew := IsNewCustomObject(PIOName); PIOrot := GetSymRot(PIOHand); GetSymLoc(PIOHand, P0.x, P0.y); { PIO insertion point } vsoGetEventInfo(theEvent, theButton); { VERY IMPORTANT } { comment out the following line when not needed. } AlrtDialog(concat('Top of Loop: event= ', theEvent, ' data= ', theButton, chr(13), 'hPIO≠nil ', PIOHand<>nil, ' Is new ', IsNew, chr(13), 'Result= ', result)); case theEvent of kObjOnInitXProperties: begin { 5 } { User has single-clicked the object's icon. } result := SetObjPropVS(kObjXPropPreference, TRUE); { 4, has custom Pref Dialog, OR don't open Object Properties dialog. } result := SetObjPropVS(kObjXPropHasUIOverride, TRUE); { 8, This tells VW to let the object decide what goes onto the OIP. } result := SetObjPropVS(kObjXHasCustomWidgetVisibilities, TRUE); { 12, enables Event 41 (kObjOnWidgetPrep). } result := SetObjPropVS(kObjXPropAcceptStates, TRUE); { 18, enable eventing for this plug-in. } result := vsoInsertAllParams; { Manually add the "normal" parameters... } result := vsoInsertWidget(9, kWidgetButton, 1003, 'Button 3 text', DoesNothing); { add button 3 } result := vsoInsertWidget(4, kWidgetButton, 1002, 'Button 2 text', DoesNothing); { add button 2 } result := vsoInsertWidget(0, kWidgetButton, 1001, 'Button 1 text', DoesNothing); { add button 1 } SetPrefInt(590, 1); { varParametricEnableStateEventing, kParametricStateEvent_ResetStateEvent } end; { 5 } kResetEventID: begin { 3 } { Object reset – redraw everything } WidgetChanged := vsoStateGetParamChng(PIOHand, WidgetID, WidgetIndex, OldSel); case WidgetIndex of { respond to changes in the OIP } end; { case } { draw PIO geometry } DrawSomething; vsoStateClear(PIOHand); { necessary } end; { 3 } kObjOnObjectUIButtonHit: begin { 35 } { User has clicked a button in the Object Info palette. } case theButton of buttonID_1: begin end; buttonID_2: begin end; buttonID_3: begin end; end; { case } ResetObject(PIOHand); end; { 35 } kObjOnWidgetPrep: begin { 41 } vsoSetEventResult(-8); { kObjectEventHandled } end; { 41 } kObjOnAddState: begin { 44 } theButton := vsoStateAddCurrent(PIOHand, theButton); end; { 44 } end; { case } END; Run(YourCodeName); One hint, PIOs pretty much start up not accepting any events. You enable the events you want to see with the SetObjPropVS() calls. I've commented what some of them do in the code above. When you get further in, you can play around with turning them ON and OFF to see what happens. There are a lot more events that are being ignored than the ones I've enabled, but many are specialized and you'll never need them. These will get you started. Welcome to the Warren, 🐰 Raymond
  13. Hi Pat, I never use Dialog Builder as I find it much easier to build them by hand. Usually dialogs are pretty simple in structure so hand coding is pretty straight forward. If ever you are interested, I can elaborate, but that seems to be beyond the scope of your questions here. If my script is really short and the dialog small (<100 lines total) I will usually make the dialog code a procedure and leave it at the top of the program. For larger scripts, I will make the dialog code a procedure/function and place it in its own $INCLUDE file. For the Dialog Procedure/Function, I structure it as follows: { In the MyDialog1 file... } Function MyDialog1 :Longint; Const Var Dialog :Longint; Function CreateDialogItems(DialogName :String) :Longint; { Create all the elements that are, or may be, displayed in the dialog box. } { Return the DialogID when done. } Procedure PositionDialogItems(DialogID :Longint); { Define the Control Tree (i.e., positions) of the dialog box elements relative to each other. } Procedure AlignDialogItems(DialogID :Longint); { Define the alignment of the dialog box items. } { This section is OPTIONAL, and often not needed. } Procedure SetDialogHelpItems(dlogID :Longint; DialogName :String); { Lastly, define the Help Strings that describe each dialog box item. } Procedure DialogEventLoop(var item :Longint; data :Longint); { Contains a CASE statement to process events for each editable or control ITEM in the dialog. } BEGIN { MyDialog1 } DialogID := CreateDialogItems(DialogName); PositionDialogItems(DialogID); AlignDialogItems(DialogID); SetDialogHelpItems(dlogID, DialogName); if VerifyLayout(DialogID) then MyDialog1 := RunLayoutDialog(DialogID, DialogEventLoop); END; { MyDialog1 } **************************************** { In the MAIN program file... } ... {$INCLUDE \File Path\MyDialog1.px } BEGIN { main program } ... if (MyDialog1 = 1) then { User pressed OK button } begin { continue with your program } end; { if MyDialog1 } ... END; { main program } Raymond
  14. I would imagine the four SavedSettings files work the same way. SavedSettings.xml SavedSettingsDialog.xml SavedSettingsRsrcMgr.xml SavedSettingsUser.xml And I just noticed "IFC_DM3_DefaultMapping.xml" is in the same User Settings folder, so I imagine it may be rewritten at closing, too; as well as the XML files in the DWG_DXF and DWF folders. Raymond
  15. That's what I like about you @Pat Stanford, you always show me something I wasn't expecting. After looking at it your way, I'd agree — it's a BUG. It should be returning a very small integer. Raymond
  16. Hey @Pat Stanford, Did you use GetObjectVariableInt(H, 101), not GetObjectVariableReal(H, 101)? I use it in Reshaper and it's worked for years. I didn't test today, but Reshaper is still working for symbols so I assume these commands are still working as advertised. All the best, Raymond
  17. To add to @Pat Stanford's reply, ObjVar 101 has an integer value of 1=None, 2=Symmetric, or 3=Asymmetric. ObjVars 102-104 are the XY&Z scaling factors (Real). If ObjVar 101 = 1 (None) then the XY&Z scaling factors = 1. If ObjVar 101 = 2 (Symmetric) then the Y&Z scaling factors = the X Scaling factor. Only when ObjVar 101 = 3 (Asymmetric) do you need to read all two, or three, scaling factors. (X&Y for 2D Symbols, and XY&Z for 3D symbols). Raymond
  18. David (@The Hamma), I was not able to get your script to work, and even tried it on a lone extrude without the first "vs.FIn3D()" and the "if vs.Random()" lines. If it's working for you then ignore the following. Cut & Paste works on the entire selection and probably won't work the way you've structured your script. By making some assumptions, I was able to get this version to work, but it assumes you are only working on one group at a time. A selection of multiple groups will get merged into one (not ideal). Again, if you got your script to work, please ignore the following. def FlipIt(h): if vs.GetTypeN(h) == 24: h2 = vs.FIn3D(h) if vs.GetTypeN(h2) == 5: if vs.Random() > 0.5: vs.Mirror(h2, False, (0,0), (5,0)) vs.Ungroup() vs.ForEachObjectInLayer(FlipIt, 2, 2, 0) vs.DoMenuTextByName('Cut', 0) vs.DoMenuTextByName('Paste In Place', 0) vs.Group() vs.SysBeep() Raymond
  19. Hello David, Okay, this works, but it is like throwing a hand grenade over the wall. Using Pat's script as a starting point I tried the not so obvious as it's something that's worked for me in the past. CUT & PASTE. Since you're working in 3D, I assume the final stacking order is not critical. The result in the script below will end up on the top of the stack. If it is critical, you can save the extrude's original position using NextObj() or PrevObj(), and use hMoveBackward() on your flipped extrude to reposition it to its original neighbors. More lines of code, but doable. After you get your script running, you should still file a bug. PROCEDURE Test; VAR H1,H2 :Handle; P1,P2 :Point; BEGIN P1.x := 1; P1.y := 0; P2.x := 2; P2.y := 0; H1 := FinGroup( FSActLayer ); { the Poly profile } H2 := Mirror( H1, False, P1, P2 ); DoMenuTextByName( 'Cut', 0 ); DoMenuTextByName( 'Paste In Place', 0 ); H2 := LActLayer; { for future reference } SysBeep; { make noise when done } END; Run(Test); HTH, Raymond
  20. @Pat Stanford, While I was admiring your Python Date function I noticed you are using an IF/ELSE to assign your boolean, which can be simplified by making the assignment directly. It's not much different, but it saves one line of code. S0:=Concat('import vs',CR, 'from datetime import date',CR, 'mybool = date(', Year,', ', Month,', ', Day,') > date.today()',CR, 'vs.Rpstr_SetValueBool(',SQ,'WSSplit_DateLimit',SQ,', mybool)',CR); Thanks for posting. Raymond
  21. @Pat Stanford, Are you still looking for a VS only solution? Raymond
  22. @Sam Jones, Use LActLayer, or a Waldo that places a locus and retrieves the PrevObj(LNewObj), were LNewObj is now the Locus. Delete when done. Raymond
  23. @MarcelP102, Long ago I felt that this article would survive better on my hard drive than in the ether. You've proved my supposition correct. Yes, it's one you only need rarely, but it's invaluable when it is needed. I tried to attach the file directly to this post, but the forum does not accept the .webarchive file extension. Let me know if the attached ZIP file opens and you can read it. If not, I'll send it to you offline. Are you using a Mac, or a PC? [ Upload Deleted ] Raymond
  24. @Sam Jones, What about __LeaderEndX and __LeaderEndY? They are relative to the insertion point of the Data Tag, but you can figure out their values from real drawing coordinates using Vectors, or at least I've been told that is possible. 😉 Raymond
×
×
  • Create New...