Jump to content

Babysteps into Vectorscripting: Random Tools


Recommended Posts

Posted (edited)

I was missing tools to randomly scale, rotate, replace and place objects. I first tried to make them with Marionette, but @Pat Stanford challenged me to start with Vectorscripting. In the end I beleive this will give me more possibilitys. I started with a script to randomly scale a symbol within a user given max/min value. Surprisingly it wasn't that hard to make! The script make the symbols have a weird scale like 1.23442237. I have tried to round this to 1.23 with the 'Round' function. But that was not working, it gave me '1' as a result. Any tips for that? Do you see any improvements?

Other thing what would be nice if the script auto detects the type of selected object. So the script can also scale text and circles for example, instead of only symbols. I have some idea's how this could work, will try them at another time. 

 

Random Scale Symbols Script

PROCEDURE RandomScaleSymbols;
{Scales each selected symbol in the active layer randomly within a user given range}

{© 31-05-2022 v2 Arnhem - The Netherlands, Marcel Plomp mplomp@opzoom.nl}
{With the help of Pat Stanford and others}

{Licensed under the GNU Lesser General Public License}

VAR
scale   :REAL;
sMax   :REAL;
sMin	:REAL;

Procedure WithIt(Hd:Handle);
Begin
	Scale := Round(100 *(Random*(sMax - sMin) + sMin)) / 100;
	SetObjectVariableInt(Hd, 101, 2);
	SetObjectVariableReal(Hd, 102, Scale);
End;



Begin
	sMax:=RealDialog('Enter a maximum scale value:','1.00');
	sMin:=RealDialog('Enter a minimum Scale value:','0.00');
	ForEachObject(WithIt,((InSymbol & VSEL=TRUE)));
	ReDrawAll;
End;

Run(RandomScaleSymbols);

 

Planned scripts:
- Random replace selected symbol

- Random rotate objects (based on script of @Pat Stanford, I have some improvement idea's)

- Random place selected object on a face

 

Best regards,

Marcel

Edited by MarcelP102
Link to comment

Great start Marcel.  I hope you see why I think modifying existing objects is easier via script than with Marionette. I am sure some of our Marionette Gurus or Mavens may disagree with me, but I am happy to have that discussion. Even though it will always be a matter of personal preference.

 

In Vectorscript, Round (and its evil twin Trunc. 😉 ) convert a Real to a LongInt, effectively losing anything after the decimal point. So you have to do the math to get the right number of decimals.

 

Scale := Round((Scale := Random*((sMax - sMin) + sMin))*100)) / 100;

 

Multiple the scaling value by 100 to shift the decimal point 2 places, Round to a LongInt 100 times larger than what you want, then Divide by 100 to get back to what you need.

 

You could also use Num2Str which will let you set the number of decimal places and then Str2Num to get back to a Real.

 

It is a matter of personal preferences.

 

To handle different object types, If and GetTypeN are your friends.

 

Inside your WithIt procedure try something like:

 

If GetTypeN=15 Then

  Begin

    {Code for Symbols goes here}

  End

Else

  If GetTypeN=4 Then

    Begin

      {Code for Circle goes here}

    End

  Else

    Begin

      {code for items not specified above goes here}

    End;

 

Note that there should NOT be a semicolon at the end of the End or any procedure or function that comes directly before an ELSE statement. The semicolon ends the statement and the Else will not operate if there is a semicolon before.

 

Instead of nested IF statements (which are OK for 2 or three levels) you could also use a CASE statement will a block for each of the object types.

 

Information on Case statements can be found in the Vectorscript Language Guide:  https://developer.vectorworks.net/images/7/72/VectorScriptGuide.pdf  

 

Keep asking as you get stuck. 👍

  • Like 2
Link to comment
  • Marionette Maven

As much as I advocate for Marionette, I will always stand by the opinion that a traditional script is the way to go if it's doable for you! Marionette, to me, is just a way to hold your hand while you learn.

 

I'd like to suggest you also try recreating this script using Python in Vectorworks, just to introduce you to a different set of syntax that generally is more user-friendly than traditional Vectorscript. It shouldn't be hard to translate 🙂

Link to comment

If you take @Marissa Farrell's challenge and do try Python, a couple of points to remember.

 

1.  It is white space delimited. That means that things that would go inside a Begin/End block in VS have to be indented exactly the same amount. Also, you can not mix Tabs and spaces, Python thinks they are different things. If you get your indentation wrong, the script will not run properly.

 

2. It is Case sensitive.  So Variable, variable and vArIaBlE are all different things.  

 

If you need to do stuff with data from outside of VW, Python can import libraries and do amazing things. 

 

For ME, unless I must use python for some reason, I stick to VS.  I know it better. It is what I learned first (35 years ago). It is case insensitive. It is white space insensitive.  I also happen to like the Begin/End syntax. You have to type a little more, but it makes it very clear what statements are associated.

 

Your Mileage May Vary. 😉

  • Like 2
Link to comment
  • 3 weeks later...
Posted (edited)

Hi @Pat Stanford

 

I've continued the journey with Vectorscript using CASE statement and GetTypeN  I came up with this script below. I have used HScale2D, to scale 2D items. And 

HScale3D to scale 3D items. Unfortunately 3D Polygon, Sweeps and Multi extrude can't be scaled using that command. I've found that 'scale' can be used for those. But then all the selected objects will be scaled with the same factor. Properly because of the missing Handle? Do I need another approach for those object-types? Like what is done here? Why there is HScale3D and Scale command? HScale3D seems to be better because you can set the Z. The scale command always scales form the objects center it seems. 

 

Updated Script
 

PROCEDURE RandomScaleObjects;
{Scales each selected object in the active layer randomly within a user given range}

{© 17-06-2022 Arnhem - The Netherlands, Marcel Plomp}
{With the help of Pat Stanford and others}

{Licensed under the GNU Lesser General Public License}

VAR
	rScaleFactor 						:REAL;
	rScaleMax, rScaleMin				:REAL;
	rCenterX, rCenterY, rCenterZ 		:REAL;
	iObjectType							:INTEGER;

Procedure WithIt(Hd:Handle);
BEGIN
iObjectType := GetTypeN(Hd);
rScaleFactor := Round(100 *(Random*(rScaleMax - rScaleMin) + rScaleMin)) / 100;
	CASE iObjectType OF
		{Scales the Line, Rectangle, Oval, Polygon, Arc, Freehand, Text, Rounded rectangle and Polyline}
		2..6,8,10,13,21:		
			BEGIN
				HCenter(Hd, rCenterX, rCenterY);
				HScale2D(Hd, rCenterX, rCenterY, rScaleFactor, rScaleFactor, True);
			END;
		{Scales the Extrude, Sweep,  Mesh, CSG Solid, Nurbs Curve and Nurbs surface}
		{3D Polygon = 25, Sweep=34 and Multi extrude=38 does not work??}
		24,40,84,111,113:		
			BEGIN
				HCenter(Hd, rCenterX, rCenterY);
				HScale3D(Hd, rCenterX, rCenterY, 0, rScaleFactor, rScaleFactor, rScaleFactor);
			END;
		15:
			BEGIN
				SetObjectVariableInt(Hd, 101, 2);
				SetObjectVariableReal(Hd, 102, rScaleFactor);
			END;			
		{Look into if I want to add architectural objects like: 68,71,81,83,86,89
		Bitmap (=14) can be scaled with Hscale2D but moves in place..}
	END;
END;

BEGIN
	rScaleMax:=RealDialog('Enter a maximum scale value:','1.00');
	rScaleMin:=RealDialog('Enter a minimum Scale value:','0.00');
	ForEachObject(WithIt,((InSymbol & VSEL=TRUE)));
	ReDrawAll;
END;

Run(RandomScaleObjects);

 

 

Test script using SCALE function
 

PROCEDURE RandomScaleObjects;
{Scales each selected object in the active layer randomly within a user given range}

{© 17-06-2022 Arnhem - The Netherlands, Marcel Plomp}
{With the help of Pat Stanford and others}

{Licensed under the GNU Lesser General Public License}

VAR
	rScaleFactor 			:REAL;
	rScaleMax, rScaleMin	:REAL;

Procedure WithIt(Hd:Handle);
BEGIN
	rScaleFactor := Round(100 *(Random*(rScaleMax - rScaleMin) + rScaleMin)) / 100;
	SCALE(rScaleFactor, rScaleFactor);
END;

BEGIN
	rScaleMax:=RealDialog('Enter a maximum scale value:','1.00');
	rScaleMin:=RealDialog('Enter a minimum Scale value:','0.00');
	ForEachObject(WithIt,((InSymbol & VSEL=TRUE)));
	ReDrawAll;
END;

Run(RandomScaleObjects);

 

Edited by MarcelP102
Link to comment

Hello @MarcelP102,

   The big difference between Scale and HScale2D, is that Scale operates on all selected objects with no finesse, while HScale2D operates on a single item identified by its handle. There is a lot more control afforded by the latter command, but more diligence must be taken to pass meaningful parameters, thus making for longer scripts. 

 

   I only modified your first script, and I added a clause in your CASE statement to show you how to scale Multiple Extrudes. Sweeps should be similar, but you have to scale the Ht (height) and Wid (width) dimensions the same, because Sweeps are always circular. The Thk (thickness) dimension can be scaled a different value, if you like. I also swapped out your two dialog calls for one call that allows you to enter 2 numbers at once. If you want "fancy", then you'll have to code a small custom dialog, but for quick and dirty, this gets the job done with a one-line call.

 

   All of my changes are commented. If you have any questions, please don't hesitate to write back. You're off to a good start.

 

Enjoy,

Raymond

 

PROCEDURE RandomScaleObjects;
{Scales each selected object in the active layer randomly within a user given range}

{© 17-06-2022 Arnhem - The Netherlands, Marcel Plomp}
{With the help of Pat Stanford and others}
{ 18 Jun 2022 - Show how to scale MExtrudes, and some small mods made by "Others". }

{Licensed under the GNU Lesser General Public License}

VAR
	rScaleFactor 				:REAL;
	rScaleMax, rScaleMin			:REAL;
	rCenterX, rCenterY, rCenterZ 		:REAL;
	rCenterX1, rCenterY1, rCenterZ1		:REAL;	{ need more variables }
	Ht, Wid, Thk				:REAL;	{ need more variables }
	iObjectType				:INTEGER;

Procedure WithIt(Hd:Handle);
BEGIN
	iObjectType := GetTypeN(Hd);
	rScaleFactor := Round(100 * (Random*(rScaleMax - rScaleMin) + rScaleMin)) / 100;
	CASE iObjectType OF
		{Scales the Line, Rectangle, Oval, Polygon, Arc, Freehand, Text, Rounded rectangle and Polyline}
		2..6,8,10,13,21:		
			BEGIN
				HCenter(Hd, rCenterX, rCenterY);
				HScale2D(Hd, rCenterX, rCenterY, rScaleFactor, rScaleFactor, True);
			END;

		{Scales the Extrude, Sweep,  Mesh, CSG Solid, Nurbs Curve and Nurbs surface}
		{3D Polygon = 25, Sweep=34 and Multi extrude=38 does not work??}
		24, 40, 84, 111, 113:		
			BEGIN
				HCenter(Hd, rCenterX, rCenterY);
				HScale3D(Hd, rCenterX, rCenterY, 0, rScaleFactor, rScaleFactor, rScaleFactor);
			END;

		38:	BEGIN	{ MExtrude }
				Get3DCntr(Hd, rCenterX, rCenterY, rCenterZ); 		{ original 3D center }
				Get3DInfo(Hd, Ht, Wid, Thk);				{ 3D Size }

				Set3DInfo(Hd, Ht*rScaleFactor, Wid*rScaleFactor, Thk*rScaleFactor);

				Get3DCntr(Hd, rCenterX1, rCenterY1, rCenterZ1);		{ center after scaling }
				Move3DObj(Hd, rCenterX-rCenterX1, rCenterY-rCenterY1, rCenterZ-rCenterZ1);	{ move it back }
			END;

		15:	BEGIN	{ Symbol }
				SetObjectVariableInt(Hd, 101, 2);
				SetObjectVariableReal(Hd, 102, rScaleFactor);
			END;

		{Look into if I want to add architectural objects like: 68,71,81,83,86,89
		Bitmap (=14) can be scaled with Hscale2D but moves in place..}
	END;
END;		{ WithIt }

BEGIN
	{ Use standard Point Dialog to collect 2 numbers. }
	PtDialog('Enter Max and Min scale factors in X&Y fields below.', '1.0', '0.01', rScaleMax, rScaleMin);
	ForEachObject(WithIt, InSymbol & VSEL);		{ don't need excess parens }
	ReDrawAll;
END;
Run(RandomScaleObjects);

 

  • Like 2
Link to comment
Posted (edited)

So I had some time today to continue with the script. I'm able to get the dimensions of an sweep with 'Get3DInfo'. But Set3DInfo doesn't seem to work with a sweep. 

 

In the meantime I've tried to great a custom dialog so I can extend it later with more options. The code for the layout is working. The only piece of information missing is how to control value from the the input field (CreateEditReal) to the code. Any tips? Or can anyone point me to an example?

 

image.png.13ec38c9b0d8cb8134f22544354cbb65.png

 

Custom menu script

PROCEDURE Example;
VAR
dialog1 :INTEGER;
result  :INTEGER;
PROCEDURE Dialog_Handler(VAR item :LONGINT; data :LONGINT);
BEGIN
END;
BEGIN
dialog1 := CreateLayout('Input scale value', FALSE, 'OK', 'Cancel');

    {create controls}
	CreateStaticText(dialog1,3,'Enter a maximum and minimum scale value',-1);
	CreateStaticText(dialog1,4,'Max Scale:',10);
	CreateEditReal(dialog1,5,3,1,10);
	CreateStaticText(dialog1,6,'Min Scale:',10);
	CreateEditReal(dialog1,7,3,0,10);

    {set relations}
	SetFirstLayoutItem(dialog1, 3);
	SetBelowItem(dialog1,3,4,0,0);
	SetRightItem(dialog1,4,5,0,0);
	SetBelowItem(dialog1,4,6,0,0);
	SetRightItem(dialog1,6,7,0,0);

    IF RunLayoutDialog( dialog1, NIL ) = 1 THEN BEGIN
    Message('Hello, world');
    END;

END;
RUN(Example);

 

Edited by MarcelP102
Link to comment

Hi  @MarcelP102,

   Sweeps are tricky. You can individually scale the radius/diameter, but only if your view is looking down the center, i.e., along the axis of rotation. This would be a FRONT or a BACK view for an unrotated Sweep. You can then scale the height, but only if you are looking at a TOP, LEFT, RIGHT, or BOTTOM view of an unrotated Sweep. If the Sweep object is rotated, you have to set up custom views to access a local FRONT view, and then the local TOP view. I know this because it's how I scale sweeps in my Reshaper plug-in, and I can tell you it's a LOT of code to resize a Sweep. 

 

   Another approach would be to completely un-rotate the Sweep, then jump to the FRONT view to scale the Sweep's diameter, and then the TOP view to scale the Sweep's height. When done, re-rotate the Sweep to its original angles. If necessary, move it back into position. Again, a lot of code.

 

*****

 

In modern dialog code there is an Event Loop. In your example you call it "Dialog_Handler", but there is nothing in it. When you press the OK button, dialog item = 1, you need to read the EditReal fields into variables with GetEditReal() when this event is detected. Use a CASE statement on the "item" variable to detect different events. With those variables loaded you can execute your scaling code. You can either do this inside the dialog code, or outside after the dialog closes, but you only do so if the user pressed the OK button; item=1 inside the dialog, or Result=1 outside the dialog.

 

Raymond

Edited by MullinRJ
correct and clarify scaling instructions
  • Like 1
Link to comment

Join the conversation

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

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

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

×   Your previous content has been restored.   Clear editor

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

×
×
  • Create New...