Jump to content

MullinRJ

Member
  • Posts

    2,017
  • Joined

  • Last visited

Everything posted by MullinRJ

  1. OOPS! This is the Python Scripting Corner. Try this instead: # CenterOnOrigin # Moves everything on a design layer to the Origin, # then moves the User Origin to maintain the same XY readings. # 11 Jul 2016 - Raymond J Mullin # USE AT YOUR OWN RISK, and USE on a COPY of your data FIRST! import vs (OrigX, OrigY) = vs.GetOrigin() vs.SelectAll() vs.Group() (PcenX, PcenY) = vs.HCenter(vs.FSActLayer()) Vx = -PcenX - OrigX Vy = -PcenY - OrigY vs.HMove(vs.FSActLayer(), Vx, Vy) vs.Ungroup() # optional, to retain the same x&y after the move vs.SetOrigin(Vx, Vy); # remove to keep drawing center unchanged Raymond
  2. Patrick, When in doubt, move it yourself. PROCEDURE CenterOnOrigin; { Moves everything on a design layer to the Origin, } { then moves the User Origin to maintain the same XY readings. } { 11 Jul 2016 - Raymond J Mullin } { USE AT YOUR OWN RISK, and USE on a COPY of your data FIRST! } VAR x, y : Real; Orig, Pcen, V :Vector; BEGIN GetOrigin(Orig.x, Orig.y); SelectAll; Group; hCenter(FSActLayer, Pcen.x, Pcen.y); V := -Pcen - Orig; { offset vector } hMove(FSActLayer, V.x, V.y); UnGroup; { optional, to retain the same x&y after the move } SetOrigin(V.x, V.y); { remove to keep drawing center unchanged } END; Run(CenterOnOrigin); Raymond
  3. Hippocode is correct, but his answer may be more complicated than what you need. It is more generic as it applies both to finding points between your endpoints, and also to finding points anywhere along the line passing through your two points. Elaborating on what I posted earlier: The vector V between points P1 and P2 is described as: V := P2 - P1; Its length Norm(V) is the distance between the two points. Its angle Vec2Ang(V) points in the direction from P1 to P2. V2 := V/2; is half as long as the distance from P1 to P2. The direction is the same but the length is half of V. To get the midpoint between P1 and P2 you add V2 to P1.* Similarly, let V3 := V/3; So P1 + V3 is 1/3 the distance between the two points. Similarly V4 := V/4, and P1 + V4 is one quarter the way between the points. Etc. You can add repetitively to get other multiples of points along the line: P1 + V3 is 1/3 from P1 to P2 P1 + V3 + V3 is 2/3 from P1 to P2 P1 + V3 + V3 + V3 is 3/3 from P1 to P2, or at the same position as P2. Elaborating on what Hippocode suggested: What Hippocode is proposing is to make V1 := UnitVec(V). That is, a vector of length 1 and the same direction as V. If you know the length of V (hint: Norm(V)) then you can scale the "unit vector" by any fraction you want, larger or smaller. NewVector := UnitV * OriginalLength * ScaleFactor; Then any new point is P1 + NewVector. Your original vector can be written this way with a scale factor of 1: V := UnitV * OriginalLength * 1; This is the more generic way of thinking about finding points along a line, but it may be more algebra than you need for a simple problem. What you will find is there are usually multiple ways to solve a problem. HTH, Raymond * Another concise way to find the midpoint between two points is: Pmid := (P1 + P2) / 2; It evaluates to the same value as P1 + (P2 - P1) / 2; This expression does not work for finding the 1/3 point, etc.
  4. YES! Vectors scale with multiplication and division by a constant. Vmid := 0.5 * V; or Vmid := V / 2; Same result. It is the same as scaling each component individually: Vmid.x := V.x / 2; Vmid.y := V.y / 2; Vmid.z := V.z / 2; Big time saver when writing code, and it and makes reading it later a lot easier to follow. There are tons of sites on the web about Vector operations. Google and learn. It is very visual so learning is easy. Anyone who can read a map and follow sequential directions already has the basics of Vector arithmetic mastered. Vector math is only a formal way of stating what you already can see spatially. It's an algebraic language, like Pascal, C, and Python are procedural languages, nothing more. Learn the grammar and never look back. Raymond
  5. Michael, Glad I could help. Vectors are easy once you get used to using them. As a data type, think of them as one variable name with three sub-parts: the X, Y, and Z components, or V.x, V.y, and V.z. Usually vectors contain data that is linked in XY or XYZ, but in a program they could contain 3 numbers that do not refer to spatial concepts, like Temperature, Hour, and Minute. You decide what goes in and what comes out. In this case the vector variable is just a convenient wrapper to keep three related values together under one name. For spatial data here are some quick rules to help you think about vectors. • You can store 2D or 3D points in a vector variable. If you store 2D data, just ignore the ".z" component. You can set it to 0, but it probably already is zero. • You can store XY or XYZ displacements in a vector (deltaX, deltaY [, deltaZ]). • If you add or subtract 2 vectors, you'll get a vector as the answer: V3 := V1 + V2; V2 := V3 - V1; { same expression, just rearranged } • if you add a vector and a point, the answer will be another point: P2 := P1 + Voffset; { Voffset is a displacement vector showing where P2 is relative to P1 } • Subtracting 1 point from another point returns a vector showing the displacement between the points: Voffset := P2 - P1; • Adding 2 points is somewhat meaningless, unless you think of their positions as vector displacements from the origin. Another thing to do that helps keep your programs clear is to save data directly to vector fields. Also start vector variable names with a "V" and point variables with a "P". It will help readability. Try this as an example: PROCEDURE xxx; { On a design layer with one Line selected, run script to show the angle and length of the Line. } VAR Ang, Lngth :Real; P1, P2, V :Vector; BEGIN GetSegPt1(FSActLayer, P1.x, P1.y); { starting point of Line } GetSegPt2(FSActLayer, P2.x, P2.y); { ending point of Line } V := P2 - P1; { displacement between points P2 & P1 } Ang := Vec2Ang(V); { angle of vector V } Lngth := Norm(V); { length of vector V } Message('Angle = ', Ang, '° Length = ', Lngth); SysBeep; END; Run(xxx); Welcome to the best part of Vector-scripting. It's all in the name ;-) Raymond
  6. Michael, Are you looking for the slope of the line that connects those points? If so, I think you want to use Vec2Ang(). It's a built-in VS call. Var V :Vector; ... V.x := p2X - p1X; { your DeltaX } V.y := p2Y - p1Y; { your DeltaY } V.z := 0; Ang := Vec2Ang(V); You'll get angles in degrees in the range of +-180. If you want values from 0-360, then test for a negative angle and add 360 to it. if (Ang<0) then Ang := Ang + 360; If you are looking for the angle between two vectors, the answer is a little more complicated, but easily done. Write back for more details. HTH, Raymond
  7. Carl, Try this instead of your function: FUNCTION H_FSActContainer: HANDLE; VAR h : HANDLE; BEGIN h := FIn3D(H_ActContainer); if not Selected(h) THEN h := NextSObj(h); H_FSActContainer := h; END; It may not solve all your problems, but it will return the First Selected Object inside your container. I did not execute your code. I just noticed your WHILE loop would not stop at the first Selected object. This function will return NIL if no objects are selected. HTH, Raymond
  8. Hello Andy, In your calls to SetObjectVariableBoolean() you placed "122" in the slot where a HANDLE variable to the ViewPorts should go. 122 is the type of a VIEWPORT and doesn't point to anything. Since you appear to be new to VectorScript I've attached a short script that demonstrates how to access each VP in your drawing. Use it as an example and modify it to do other tasks. The basic format is the same for many global tasks. Also, read the VS documentation on the set of ForEachObject calls. They are very powerful for walking through a file and selecting objects of interest. PROCEDURE VPProjScreenON; { Set "Project 2D" ON for each ViewPort in the drawing. } CONST Proj2D = True; { True = ON, False = OFF } procedure SetProj2D(H :Handle); Begin SetObjectVariableBoolean(H, 1005, Proj2D); { VP Project 2D } SetObjectVariableBoolean(H, 1035, True); { VP Display Planar } SetDSelect(H); End; { SetProj2D } BEGIN DSelectAll; ForEachObject(SetProj2D, T=VIEWPORT); { do this to each VP in the drawing } END; Run(VPProjScreenON); In this script, the call ForEachObject() gets a handle to each VP, one at a time, and passes it to the procedure SetProj2D(). All the detail work is done in SetProj2D(). Duplicate this program and set the CONSTANT Proj2D to FALSE to make a script that turns the setting OFF. One caveat, this script is only mildly tested. Use at your own risk and start out on a copy of you drawing first. Have Fun, Raymond
  9. Alan, I know very little about Python, but I do know that "print" requires parentheses in Python 3.x. Since you assert it works both ways in your environment, I'd surmise you're running Python 2.x. Based on that, if you use print with parentheses, your "print()" routines should work in both Python 2.x and 3.x environments. If you're only going to work in Python 2.x then you can use whichever form you like. HTH, Raymond
  10. Giovanni, It's been 5 months, and you may already have the answer, but for those that don't... This is not a Python problem, but rather a quirk of the AlrtDialog() call, and possibly its Modern Dialog underpinnings. Use two ampersands '&&' to show a single ampersand in an Alert Dialog string. Use a single ampersand '&' in Message() command strings and in normal string use. This holds true for both Python and VectorScript when using AlrtDialog(). Examples: 1) vs.AlrtDialog('Watch out for Alligators && Snakes!!!') vs.Message('Good luck avoiding Death & Taxes!!!') 2) forAlrt = True S = "Your Appetizer: {} {} {}".format('Cheese', '&&' if forAlrt else '&' , 'Crackers') vs.AlrtDialog(S) forAlrt = False S = "Your Appetizer: {} {} {}".format('Cheese', '&&' if forAlrt else '&' , 'Crackers') vs.Message(S) HTH, Raymond
  11. I am getting frequent errors that do not clear after I edit the module. Python continues to present the error at the end of the Error Output file after the error has been corrected. The errors are in modules I've written that I am importing into a larger program. How can I reset the Python engine to move past this? Example: aVar = Tuple1 - Tuple2 Error received: TypeError: unsupported operand type(s) for -: 'tuple' and 'tuple' So I change the operator to "+": aVar = Tuple1 + Tuple2 but on the next execution I get the same error: TypeError: unsupported operand type(s) for -: 'tuple' and 'tuple' This seems to happen with any "TypeError" I get in an imported module. They do not clear unless I restart VW. I have tried: import Polys from imp import reload reload (Polys) to no avail. Also, turning ON and OFF the "Run scripts in developer mode" pref has no effect. The only thing I have found to work is restarting VW. There has got to be a simple way I am unaware of. TIA, Raymond
  12. VG, PIO code executes multiple times when placing an object. If you put an AlrtDialog() call in your event loop at the very top, you can count how many times it actually goes through. The count may differ between object creation and object regeneration, but I'm not sure anymore. I've forgotten more than I currently remember on this topic. I know on one loop through your code, VW builds the gray outline that shows on the screen before you click to place it. Another loop actually builds your object. If you want to see how many times VW thinks your object is new, change your Message() to an AlrtDialog() and count. This is all part of the fun of figuring out how things really get done. Please be careful and avoid having too much fun at one time. It could drive you to drink (or worse). Good luck, Raymond
  13. Bill, You can test CW / CCW 'ness with the CrossProduct() function. If the CrossProduct's Z component is positive, the angle turns one way. If it is negative it turns the other way. If it is 0, the lines are collinear. The sign of the Z component is determined by the Right-Hand-Rule, which is well documented online. The magnitude of the CrossProduct's Z component is not important in this test, just its sign. If your lines always have a left/right angle between them the following code will test for CW/CCW: This should be easy to follow: function isCCW(P1, P2, P3 :Vector) :Boolean; { Return TRUE if the angle at P2 is CCW, FALSE if it is CW (or straight). } Var V1, V2, CP :Vector; Begin V1 := P2 - P1; { first line } V2 := P3 - P2; { second line } CP := CrossProduct(V1, V2); isCCW := CP.z > 0; End; { isCCW } If you are interested in more compact code (like me), then here's the same routine squished: function isCCW(P1, P2, P3 :Vector) :Boolean; { Return TRUE if the angle at P2 is CCW, FALSE if it is CW (or straight). } Var CP :Vector; Begin CP := CrossProduct(P2-P1, P3-P2); isCCW := CP.z > 0 ; End; { isCCW } HTH, Raymond
  14. As long as you have not converted the Solid Subtraction to a Generic Solid, you can reverse the addition/subtraction process by ungrouping the Solid. One UNGROUP per step. Then subtract again by selecting the other object for the subtraction. HTH, Raymond
  15. Yes. I'm sure it could be done with supplied nodes, but there is little support for vector math in the current set of nodes I searched, so the easiest way I found was to roll a custom node. Here's my stab at one that answers your question. If it's not exactly what you need, try tweaking the code inside, but if you do, please post back your changes. HTH, Raymond
  16. Hello Mark, Looking at your file's resources I was able to find the 41 colors you cannot delete. I selectively deleted groups of resources and purged the colors after each deletion. When the total color count dropped I knew there were color assignments in that batch of resources. I closed the file without saving and reopened it, looking more closely at individual resources. One of the colors (Center Line Red) is in your hatch "Unused Lights ". I assume you want to keep this. Three colors are in your Line Types, "RGB" and "RGBW". Check the geometry. I assume you want to keep these as well. The rest are in the objects of your symbols. I got the count down to 23, minus the four colors mentioned above, leaving 19 in your symbol library. I am not sure where these reside, defined inside PIO's or set in a PIO's instance, perhaps. You may have better insight than I. When you set an object to use class colors, the object still retains its original colors. I modified the script above to remove individual object colors in addition to setting all symbol objects to use class colors. It should be possible to search for objects using non-B&W colors. If you'd like to pursue that avenue, please contact me offline. I hope this gets you back on track. PROCEDURE MakeAllBW; { Set ALL Class Colours to B&W. } { Set ALL objects in symbols to accept colour attributes by class. } { 21 Mar 2016 - Raymond J Mullin } { Updated to also set symbol objects to B&W for their default colours. } { ***** ALWAYS use this script on a COPY of your data! ***** } VAR I :Integer; ClassName :String; function SetColourByClass(H :Handle) :Boolean; { Set Fill and Pen Colours to be "ByClass", and reset each object's default colours to B&W. } Begin { Set each object's default colours to B&W. } SetFillFore(H, 0, 0, 0); { black } SetFillBack(H, 65535, 65535, 65535); { white } SetPenFore(H, 0, 0, 0); { black } SetPenBack(H, 65535, 65535, 65535); { white } { Set each object to use class colours. } SetFillColorByClass(H); SetPenColorByClass(H); End; { SetColourByClass } BEGIN { Change colour attributes of all classes to use Black & White. } for I := 1 to ClassNum do begin ClassName := ClassList(I); SetClFillFore(ClassName, 0, 0, 0); { black } SetClFillBack(ClassName, 65535, 65535, 65535); { white } SetClPenFore(ClassName, 0, 0, 0); { black } SetClPenBack(ClassName, 65535, 65535, 65535); { white } end; { for } { Change all objects in Symbol Defs to accept colour attributes "By Class" } ForEachObjectInList(SetColourByClass, 0, 2, FSymDef); { 0=All objects, 2=Deep } SysBeep; { sound off when done } END; Run(MakeAllBW); All the best, Raymond
  17. Sure. I love a good puzzle. Glad I could help. I was trying to send you my email address offline, but my outgoing mail server is bottled-up just now. Try using the AOL address in my profile. Raymond
  18. Mark, This should do what you described. Make sure you try it on a test file first. PROCEDURE MakeAllBW; { Set ALL Class Colours to B&W. } { Set ALL objects in symbols to accept colour attributes by class. } { 21 Mar 2016 - Raymond J Mullin } { ***** ALWAYS use this script on a COPY of your data! ***** } VAR I :Integer; ClassName :String; function SetColourByClass(H :Handle) :Boolean; { Set Fill and Pen Colours to be "ByClass". } Begin SetFillColorByClass(H); SetPenColorByClass(H); End; { SetColourByClass } BEGIN { Change colour attributes of all classes to use Black & White. } for I := 1 to ClassNum do begin ClassName := ClassList(I); SetClFillFore(ClassName, 0, 0, 0); { black } SetClFillBack(ClassName, 65535, 65535, 65535); { white } SetClPenFore(ClassName, 0, 0, 0); { black } SetClPenBack(ClassName, 65535, 65535, 65535); { white } end; { for } { Change all objects in Symbol Defs to accept colour attributes "By Class" } ForEachObjectInList(SetColourByClass, 0, 2, FSymDef); { 0=All objects, 2=Deep } SysBeep; { sound off when done } END; Run(MakeAllBW); All the best, Raymond
  19. Yes, I appreciate your problem, and this is exactly what scripting can address. Back shortly, Raymond
  20. So, it seems not all objects are set to have their attributes to "Set by Class". If you have your classes set the way you like them, then all you need is a script to set all objects to "attributes by class". Do you want ALL attributes controlled by class, or just the color attributes? Raymond
  21. Are you setting your colors by class, or individual object attributes? A script would be pretty easy, but one needs to know which set of colors to change. Raymond
  22. I'd like to see a field that tags a post with the current or relevant version of VW that applies at the time of posting. It should default to the current version, but the poster could select an older version or versions relevant to the version he/she is currently using. When reading through older posts it's not always obvious which version is being discussed. It might also be useful to have a way to flag a post as still relevant in future versions, since issues don't always get resolved by the next release. Thanks, Raymond
  23. Hello LarryO, 175 is the preference number you need. However, to set Pref 175 to TRUE, Preference 168 must already be set to TRUE. So, try this two line script to return VW to the way you like: SetPref(168, True); { Rounding Style: Fractional } SetPref(175, True); { Exact as Fractions / Non-Exact as Decimals } Raymond
  24. Halloo, Mr. Do, Late to the party am I, but I want to proffer some alternate algebra to your question. You almost had your answer in your first post. See the following code to see what was missing. And though your trigonometry is spot on, there are some handy vector functions built into VectorScript that make life a little easier once you get familiar with how they work. Again, see following code. To use this script, draw an arc on a design layer, leave it selected and run the script. The script will deselect the arc and place a locus on the arc's mid point. All the best, Raymond (vector junkie) PROCEDURE Main; { St. Patrick's Day 2016 - Raymond Mullin } VAR h : HANDLE; StartAng, SweepAng, ArcRadius, MidAng : REAL; P1, P2 : VECTOR; { ARC Start and Stop points respectively } Pcen, Pmid : VECTOR; { ARC Center and Mid points } Vchord : VECTOR; { line between arc endpoints } BEGIN h := FSActLayer; if (h <> nil) do begin { Get arc data } GetSegPt1 (h, P1.x, P1.y); { start point } GetSegPt2 (h, P2.x, P2.y); { stop point } hCenter (h, Pcen.x, Pcen.y); { center point } GetArc (h, StartAng, SweepAng); { These all yield the same radius value, the } { distance between the arc center and a point on the arc } { Norm() always returns a positive value. } ArcRadius := Distance (Pcen.x, Pcen.y, P1.x, P1.y); ArcRadius := Distance (Pcen.x, Pcen.y, P2.x, P2.y); ArcRadius := Norm(P1-Pcen); ArcRadius := Norm(Pcen-P1); ArcRadius := Norm(P2-Pcen); ArcRadius := Norm(Pcen-P2); { Your 1st expression was almost correct } { you needed the mid angle, not just 1/2 the sweep and } { the new vector needed to be added to the arc's center point } Pmid := Pcen + Ang2Vec (StartAng+SweepAng/2, Distance (Pcen.x, Pcen.y, P1.x, P1.y)); { Another way to express it using vector functions } MidAng := StartAng + SweepAng/2; Pmid := Pcen + Ang2Vec (MidAng, ArcRadius); { Another way using only vector functions. } Vchord := P2 - P1; ArcRadius := Norm(P1-Pcen); Pmid := Pcen + UnitVec(Perp(Vchord)) * ArcRadius; { mark the center point of the arc } DSelectAll; Locus(Pmid.x, Pmid.y); end; { if } END; RUN (Main);
  25. I'm not sure where you are trying to set keyboard shortcuts for Classes and Layers dialogs. You might try describing your problem with more details. However, you can access the Navigation Palette with CMD-SHIFT-N, and the Organization Palette with Cmd-SHIFT-O. Both palettes let you access and change Classes and Layers, among other things. If the "Organization..." menu is not in your workspace (It wasn't in any of mine) you can add it manually using the Workspace Editor. I used the "All menus" category to find it. When I added it I didn't assign a hotkey, but after I closed the Workspace Editor it automatically adopted CMD-SHIFT-O for the hotkey. That's new to me. HTH, Raymond
×
×
  • Create New...