Jump to content

MullinRJ

Member
  • Posts

    1,990
  • Joined

  • Last visited

Everything posted by MullinRJ

  1. For some unknown reason, to me at least, when processing a text block with 3 or more point sizes, the above procedure gets very close to achieving the proper text length after scaling, but not the exact length. It's slightly long. I did achieve acceptable perfection by repeating the process of scaling a second time. If you need more input on how to do this, write back. Raymond
  2. OK, not as hard as I originally imagined. You just have to scale each character's size by the same amount, and the scale factor is the (existing text length) / (unwrapped text length). Then use a loop to get each characters's size and scale each one to a new size. The secret sauce is knowing how to set the TextWrap and TightFill attributes. The rest is easy peasy. PROCEDURE xxx; { Scale a wrapped text block to fit in the same box on one line. } { 2 Jun 2022 - Raymond Mullin } VAR H :Handle; I :Integer; TxtBoxW, TxtWid, SF, ChSz :Real; BEGIN H := FSActLayer; TxtBoxW := GetTextWidth(H); { target size } SetTextWrap(H, False); { clear Text Wrap } SetObjectVariableBoo(H, 684, False); { clear Tight Fill } TxtWid := GetTextWidth(H); { full length of text } SF := TxtBoxW / TxtWid; { scale factor } for I := 0 to GetTextLength(H) do begin ChSz := GetTextSize(H, I); { individual character size } SetTextSize(H, I, 1, ChSz * SF); { new character size } end; { for } SysBeep; END; Run(xxx); Like Pat, I leave it to you to Pythonize it. Raymond
  3. If the point size is uniform through the entire block, the scaling formula is quite easy to achieve a known length. If you have mixed character sizes throughout the block, and potentially mixed font faces, you will have to build a dynamic array of each character and store its size and fontID, calculate the scale factor needed to change the block length to the desired size, then scale each character individually to achieve the overall desired length. Doable, but tedious. And who said HS algebra class was a waste of time? Raymond
  4. Hello @WhoCanDo, I did not try to replicate your problem, but I suspect it may have something to do with using PickObject to make your selections. Instead, I built an array of HANDLES to store the crop objects and used the handle array of the crop objects to define the VPs. Here's the sample code: procedure Main; var Lyr : string; i, j : integer; Hands :Array [1..500] of Handle; procedure Log_Selected (h : Handle); begin i := i + 1; Hands[i] := h; end; procedure Create_Viewport(hv :Handle); begin if (hv <> Nil) then begin SetSelect (hv); DoMenuTextByName ('Create Viewport', 0); Layer (Lyr); end; end; BEGIN Lyr := GetLName (ActLayer); i := 0; ForEachObject( Log_Selected, Sel & (L=Lyr) ); DSelectAll; For j := 1 to i do Create_Viewport(Hands[j]); END; Run(Main); It works on a really simple drawing with 5 mock view ports. I get 5 VPs when done. HTH, Raymond
  5. @LarryO, I've used the VS Export feature for years and have had no problems, except for things the Export feature does not support, like holes in a Polyline, and pretty much any new feature in the last decade or two. But, by and large, for exporting simple geometry, it's pretty robust. If you care to post examples where you are having problems I'd be happy to look at them and comment. The example you posted above is pretty vanilla and shouldn't cause a problem. Granted, you may find that VS Export adds what appear to be extra commands that define an object's attributes (like Pen and Fill colors), but you can usually remove them if you find them to be unnecessary for your needs. All the best, Raymond
  6. @LarryO, When I run your code, I get a GROUP with a 3DPoly inside, and I tried it in VW 2019 and VW 2022. Same result. Try it again. If you still see the same thing, try it in a blank file, and if that still doesn't work, restart VW and try once again. You should see a GROUP with a 3DPoly inside. You are right. 3DPolys are not potential containers like Polylines which allow holes, they are more akin to Polygons, which don't allow holes. Raymond
  7. Hi SamIWas, You might want to check again after the Lunar Eclipse next Monday. If you see an improvement, you might be onto something ;-) Raymond
  8. Yes, Message() only updates mid-script if the script is interrupted by an interactive call like GetPt(), or a dialog call like AlrtDialog(). So, what's the point, unless you want to see Sam's point? PROCEDURE StupidMessageTricks; VAR I :Integer; BEGIN for I := 1 to 3 do begin message(I); AlrtDialog(concat(I)); end; { for } END; Run(StupidMessageTricks); Raymond
  9. Did you try HMoveForward() and HMoveBackward()? Raymond
  10. I would start with H := FIn3D(ObjHand); and then use variable H to step through the object's components. But if the container object is a PIO, any changes you make to objects inside will be discarded the next time the PIO regenerates. If the container object is a Symbol, then any change you make inside the symbol will show up in every instance of that symbol in the drawing. If you explain more of what you are trying to achieve, I or someone else, can better explain how to proceed. Raymond
  11. @The Hamma, Out of curiosity, how many scripts is too many? Ball park. I assume the limit depends on how many total items are in a workspace, but knowing how many scripts you added would be interesting to know. Also, were your scripts Menu Items, or Palette Scripts? Thanks, Raymond
  12. Sam, in my experience, whenever you pick an upper limit for a data structure, you can count on a user going right past it. Just sayin'. Raymond
  13. @DomC, That's one of the more creative suggestions I've ever heard. 🤣🤣🤣 Raymond
  14. I just modified the third script above so that it only acts on selected text blocks. I changed <T=Text> to <Sel & (T=Text)>. Now the three scripts should be functionally equivalent. A little sleep does wonders to clear the brain. Raymond
  15. I believe @anthonydcobb is looking for a way to loop through a list of selected text objects to increase or decrease their sizes. In Pascal you can use a for, while, or repeat/until loop, or you can use one of the built-in functions in the ForEachObject function family (there are 5). Here are three examples that essentially do the same thing: This one uses a while loop. PROCEDURE xxx; { Increase the text size of all selected text objects on the active layer by 1 point. } { Assumes all characters in a text block have uniform size. } { Uses a WHILE loop to examine the list of selected objects. } CONST TextSzInc = 1; VAR H :Handle; BEGIN H := FSActLayer; while (H <> nil) do begin if (GetTypeN(H) = 10) then { 10 is object type for TEXT objects. } SetTextSize(H, 0, GetTextLength(H), GetTextSize(H, 0)+TextSzInc); H := NextSObj(H); end; SysBeep; END; Run(xxx); This one uses ForEachObjectInList(). PROCEDURE yyy; { Decrement the text size of all selected text objects on the active layer by 1 point. } { Assumes all characters in a text block have uniform size. } { Uses ForEachObjectInList to control the loop. Only looks at Selected objects, and does not enter Groups. } CONST TextSzInc = -1; function IncPtSz(H :Handle) :Boolean; Begin if (GetTypeN(H) = 10) then SetTextSize(H, 0, GetTextLength(H), GetTextSize(H, 0)+TextSzInc); End; { IncPtSz } BEGIN ForEachObjectInList(IncPtSz, 2, 0, FSActLayer); { 2 = Selected objects, 0 = Shallow } SysBeep; END; Run(yyy); And lastly, this one uses ForEachObject(), which is a CRITERIA based call. PROCEDURE zzz; { Increment the text size of all selected text objects on the active layer by 2 points. } { Assumes all characters in a text block have uniform size. } { Uses ForEachObject() to control the loop, which filters objects by <criteria>. } CONST TextSzInc = 2; procedure IncPtSz(H :Handle); Begin SetTextSize(H, 0, GetTextLength(H), GetTextSize(H, 0)+TextSzInc); End; { IncPtSz } BEGIN ForEachObject(IncPtSz, Sel & (T=Text)); { where "Text" = 10 } SysBeep; END; Run(zzz); You can Pythonize these pascal scripts as is and they will work, or you can use more conventional ways of building a python list of selected text handles and iterate over that list. I will let you decide which way to go. Hopefully this will get you started. Raymond
  16. Try using: vs.Move3DObj(vs.LNewObj(), 0, 0, AOD) instead. This moves the symbol by referencing its handle, and vs.LNewObj() returns a handle to the last object created in the drawing – "usually". Raymond PS - After actually reading your code, move the "move symbol" line after you assign variable h2. Then it should look like this: h2 = vs.LNewObj() vs.Move3DObj(h2, 0, 0, AOD) vs.SetObjectVariableInt(h2, 101, 3) #Set symbol scaling to assymetric ...
  17. You can use the GetPt(X, Y) function, then count the vertices in a loop until the XY values match. It's kind of a brute force method but it will get you there. One way to test the points' proximity is to store both points in vector variables, one for the GetPt value in P1, and the other for the vertex values in P2. Then use this line in your loop to decide: IF (Norm(P2-P1) < Tol) THEN {points match} Set the value of Tol to a number significantly smaller than the distance between your vertices, for example: Tol := 1e-6; { or some relevantly small number } The NORM() function returns the length of a vector, in this case, NORM(P2-P1), or NORM(P1-P2), is the distance between the two points, and it's always non-negative. Also, make sure Snap to Points is turned ON when your script runs. HTH, Raymond
  18. Yes, all objects inside the outer rectangle have the record attached, using a Mac. I select the outer rectangle and run your script. When I select the wall object, I see this in the OIP. When I first click on the wall, the IFC record is selected, but when I click on the "Format-1" record above it I see the field "fld" at the bottom with dummy data "abc", so the record is definitely attached. Raymond
  19. It seems to be working on my VW 2022. When I first select the wall after the script is run, the OIP Data pane has the <Default IfcWallStandateCase> record format selected. If I change the record selection to "MyRecord> (with the wall selected) I see the proper fields show up below. Is this what you are seeing? Raymond
  20. @elepp, Unlike my fellow scriptographers, except for the simplest of cases, I spend too much time trying to get <criteria> to work cleanly, so I usually take a more blunt approach. Here is a Vectorscript that looks at the Name List, which includes worksheet names, and decides which names are Worksheets, then tests if the name matches a predefined pattern, namely, it starts with N-characters in common. I know you are working in Python, but it's late, I'm tired, so I'll let you pythonize it. PROCEDURE xxx; { Script to recalculate all worksheets whose names start with the string in WSName. } { 25 Feb 2022 - R. Mullin } CONST WSName = 'TAB_WE_H'; WSType = 18; VAR ObjH :Handle; I, J, NameCnt :Longint; aName :String; BEGIN J := 0; NameCnt := NameNum; for I:=1 to NameCnt do begin aName := Index2Name(I); ObjH := GetObject(aName); if (GetTypeN(ObjH) = WSType) & (pos(WSName, aName) = 1) then begin RecalculateWS(ObjH); J := J + 1; end; { if } end; { for } Message(J, ' worksheets recalculated.'); END; Run(xxx); Raymond PS - Feel free to beat on the <criteria> beast if you so choose. PPS - I think the problem with using ForEachObject to test for worksheet names is that worksheets are not ON the drawing, and I think ForEachObject only tests objects that exist on Design Layers, Sheet Layers, in Viewports, and inside Symbols. @Pat Stanford, @JBenghiat, please verify.
  21. In Vectorscript there is a 32K limit to the amount of text the Script Editor can hold for scripts stored in a Script Palette. One way around it is to use an $INCLUDE statement in the script's main text body to reference the script's external location for the Pascal compiler. The compiler can handle much more than 32K bytes. It may be that you've run into the same text size limit in your Python script. Using an external editor check the size of your 810 lines to see if this is true. If it is, you will have to break you script into smaller chunks and import them into the main script. Raymond
  22. VW is not set up to run scripts on OPEN or CLOSE file events, but it is very doable to replace the "Open..." and "Close..." menus with custom menu scripts that run your commands after or before the respective Open(filename) or Close(filename) commands. Of course, you would have to edit the workspace on each computer you support to swap out the existing menu commands and reassign keyboard shortcuts to the new ones. But since there are multiple ways to open a file in VW – Open... command, drag and drop file on the Dock app icon, double click on a file in the Finder – you cannot add a script to each of these methods, only to the menu command. I'm not sure if this helps. Raymond
  23. @Jiajing, My apologies. I didn't read your code example before I answered. What I said above is still true, you can set the "item" variable to 1 at any point in your dialog handler, BUT what I didn't say and should have, you must return that value at the end of the DialogHandler proc. The second part is always needed, since the handler uses the value of "item" on exit to determine whether to stop or continue. I modified your example slightly to demonstrate a little better. Note that now when you click on your "None" pushbutton, "item" is set to 1 and the dialog closes without any fanfare, but hey, it's just an example. Also note, there are two constants that run just before the dialog starts and just after the dialog stops – kSetUpDialogC & kSetDownDialogC. I only included the first one in the DialogHandler definition below. The second one is rarely used, but it's included if you need it. In Python you have to define them, but in VectorScript they are predefined. import vs # control IDs kOK = 1 kCancel = 2 kUnnamed1 = 5 kTextbox = 7 kCpop = 8 kC2 = 9 kSetUpDialogC = 12255 # predefined dialog event constant used for setup BEFORE dialog runs kSetDownDialogC = 12256 # predefined dialog event constant used for setup AFTER dialog runs def CreateDialog(): # Alignment constants kRight = 1 kBottom = 2 kLeft = 3 kColumn = 4 kResize = 0 kShift = 1 def GetPluginString(ndx): # Static Text if ndx == 1001: return 'OK' elif ndx == 1002: return 'Cancel' elif ndx == 1003: return 'Dialog' elif ndx == 1005: return 'None' elif ndx == 1007: return '' elif ndx == 1008: return '' elif ndx == 1009: return '' # Help Text elif ndx == 2001: return 'Accepts the dialog data.' elif ndx == 2002: return 'Cancels the dialog data.' elif ndx == 2005: return 'Help text.' elif ndx == 2007: return 'Help text.' elif ndx == 2008: return 'Help text.' elif ndx == 2009: return 'Help text.' return '' def GetStr(ndx): result = GetPluginString( ndx + 1000 ) return result def GetHelpStr(ndx): result = GetPluginString( ndx + 2000 ) return result dialog = vs.CreateLayout( GetStr(3), True, GetStr(kOK), GetStr(kCancel) ) # create controls vs.CreateEditText( dialog, kTextbox, GetStr(kTextbox), 16 ) vs.CreateColorPopup( dialog, kCpop, 16 ) vs.CreateEditReal( dialog, kC2, 1, 0.0, 16 ) vs.CreatePushButton( dialog, kUnnamed1, GetStr(kUnnamed1) ) # set relations vs.SetFirstLayoutItem( dialog, kTextbox ) vs.SetBelowItem( dialog, kTextbox, kCpop, 0, 0 ) vs.SetBelowItem( dialog, kCpop, kC2, 0, 0 ) vs.SetBelowItem( dialog, kC2, kUnnamed1, 0, 0 ) # set alignments # set help strings cnt = 1 while ( cnt <= 9 ): vs.SetHelpText( dialog, cnt, GetHelpStr(cnt) ) cnt += 1 return dialog # Sample dialog handler # Uncomment this code if you want to display the dialog def DialogHandler(item, data): if (item == kSetupDialogC): # 12255 pass elif (item == kOK): # 1 pass elif (item == kCancel): # 2 pass elif (item == kUnnamed1): # 5 item = 1 # change "item" from 5 to 1 after pushing button elif (item == kTextbox): # 7 pass elif (item == kCpop): # 8 pass elif (item == kC2): # 9 pass return item dlg = CreateDialog() result = vs.RunLayoutDialog( dlg, DialogHandler ) # use "result" if you want to process # things after the dialog closes Raymond
  24. Just assign the variable to be 1.
×
×
  • Create New...