ARCHICAD: REVEALING GDL: Programming For Architect's Unique Challenges

ARCHICAD: REVEALING GDL: Programming For Architect's Unique Challenges
ARCHICAD: REVEALING GDL: Programming For Architect's Unique Challenges

Video: ARCHICAD: REVEALING GDL: Programming For Architect's Unique Challenges

Video: ARCHICAD: REVEALING GDL: Programming For Architect's Unique Challenges
Video: Язык GDL в ARCHICAD 2024, April
Anonim

This expert material continues the series of articles "ARCHICAD: Rediscovering", which began in December 2016 with an article by Vladimir Savitsky "Creation of structures and extraction of working drawings from a model", and then continued with the publications of Svetlana Kravchenko "ARCHICAD: Rediscovering. Visualization - new opportunities for an architect "and Alexander Anishchenko" TEAMWORK: effective teamwork step by step ". The cycle is designed to help users unleash the full potential of ARCHICAD®… We asked the architects to share their personal experience of using the program using non-standard approaches, little-studied functions and new features that many users may not even be aware of. As developers of the ARCHICAD application, we are confident that only a deep knowledge of the product can reveal its full value and decisively influence the results, speed and quality of the designer's work. Do you also prefer "untread paths"? Do you have experience in using non-standard approaches in working with ARCHICAD, regularly use not the most famous features of the application? We will be glad to invite new authors to cooperation: [email protected]. Svetlana Kravchenko, a practicing architect, reports:

zooming
zooming

Surely many of you have heard about GDL in ARCHICAD, but not everyone still knows how to use it in work. Considering the incredible usefulness of this feature, as well as the many questions after my first webinar on this topic, I decided to go into more detail about how even the smallest knowledge of it can help a lot in the day-to-day work of an architect.

Let's start with the basics GDL (Geometric Description Language) is a BASIC-like programming language designed to work in the ARCHICAD environment. It describes 3D solid bodies (such as doors, windows, furniture) and 2D symbols in the floor plan window. These objects are called Library Features.

For those who are even a little familiar with programming, mastering this language will not be difficult. However, with sufficient desire, the study of GDL will be quite within the power of a person far from this environment. Any architect has studied geometry and descriptive geometry in his time, has excellent volumetric thinking, and this is already half the success. You do not need to immediately try to write complex objects, you should start with the basic geometric shapes and forms; a lot of information can be gained by examining the scripts of other Library Items. Well, the main source of information is the GDL reference manual, which can be accessed through the Help menu in ARCHICAD itself. So, what is the use of knowledge of GDL for an architect? For example, unlike Grasshopper, with which you can create complex structures, GDL is simply indispensable for writing various markers and callouts, as well as for creating special components for other Library Features or tools. One of my first applications of GDL in my work was the creation of a special panel door leaf, which, when resized, did not scale in all directions, but only changed the panel dimensions. The thickness of the curly frame and the width of the harness remained unchanged. Also, architects very often want to add some simple functions to existing standard library objects - and this is the main reason why they begin to delve into GDL. Of course, knowledge of GDL is not vital, and many of these tasks can be accomplished with standard tools. For example, you can build infills with slabs and save them as a special door leaf. If you have only a few of these non-standard doors, then this will be even faster. But if in your project there are many similar doors of different sizes and their width changes in the process of work, then writing a special panel in GDL will greatly speed up and simplify the work. Geometric description implies that any of the possible shapes can be written in text according to dimensions or coordinates. For a 3D script, there is a block of commands for basic spatial shapes, such as: - BLOCK and BRICK - a parallelepiped constructed in three dimensions with the origin at point 0 of the coordinate system BLOCK a, b, c BRICK a, b, c

zooming
zooming

- CYLIND - cylinder along the Z axis, with height h and radius r CYLIND h, r

- SPHERE - sphere centered at the origin and radius r SPHERE r

zooming
zooming

An ellipse and a cone are described in a similar way. The next block of figures is already more complicated - these are various prisms. They are described by a set of point coordinates. The simplest prism is determined by the number of points (n), height (h) and listing the coordinates of all points in order. PRISM n, h, x1, y1,… xn, yn

There are many varieties of prism. The next view, PRISM_, allows you to indicate status codes to the coordinates of points, which determine the visibility of faces and edges, and also allow you to create curved prisms and prisms with holes (see the section Status Codes in the reference book). Another kind, BPRISM_, creates a prism twisted around the Y-axis. FPRISM_ builds a prism with a chamfer or fillet on the top side.

zooming
zooming

There are several commands that describe more complex polyline-based shapes: EXTRUDE, PYRAMID, REVOLVE, RULED, SWEEP, TUBE, COONS, MASS. Their description with examples can be found in the reference. For a 2D script, shapes are described by other commands: line, circle, rectangle, polyline, spline. But you can also register a command for building a projection from a 3D script.

Creating 2D or 3D shapes is only part of the functionality of GDL. If you just need a table, then it's easier to build it with the tools of ARCHICAD itself. An object is written in the case when some kind of parametricism is required: the ability to select different types of table legs, the number of legs, resize the table while maintaining the remaining dimensions, calculate the lumber for its manufacture, weight and cost. The object may not contain any geometry at all, but only perform calculations. For this, Control Clauses (Control Operators) are also used, such as loops, conditional statements, referring to a specific place in the code (subroutine). It is best to familiarize yourself with loops and conditions at the very beginning - they are used often. So, all of the examples below have conditional statements. EXAMPLE # 1 - object rotation Often, designers have a desire to make an object rotatable. Using this simple example, we will look at the structure of the Library Item, as well as the main windows of the GDL Object Editor. To open any object located in the project space (if the developer has not put a password on it), you need to select it and press the key combination Ctrl + Shift + O. Another way is to use the File> Libraries and Objects> Open Object menu. If at this moment no object has been selected, then a window for selecting an object will open. Let's add rotation parameters, for example, to a louver grille (Fig. 1).

zooming
zooming

So, we have opened the GDL Object Editor window (Fig. 2). At the top left, there is a window for viewing different views, as in the usual window of object parameters; even to the left are buttons for choosing a view - plan, elevation, 3D-window and preview. Below there are buttons for opening parameter tables, data lists and scripts. Scripts can be opened in two ways: click on the button with the name of the script - open in the same window, click on the button to the right with the window icon - the script will open in a separate window. This can be useful to see different scripts at the same time (Figure 3).

zooming
zooming

At the top in the window of any script there is a very necessary Check button: when you click it, the editor will prompt you if there are any errors in the script. The message will contain the reason for the error and the line number where the error was found. In the "Details" section, you can select an object subtype: custom door leaf, doorknob, curtain wall frame, and so on. So, special objects (pen, canvas, frame) will appear in the corresponding window for selecting these elements. When a 2D type is selected, the object will have no windows for 3D geometry. There you can also select types for different markers - nodes, sections, leader captions, zones; they will also appear in their respective tools. In this section, you can fill in a description of the object and select a password. Further - "Parameters", where all data that are used in this object and which can be changed while working on the project are presented in the form of a table. Here we need to add parameters for the turns, which we will use later.

zooming
zooming

Press the New button located above the table (Fig. 4). A new row will appear in which you need to fill in the columns. The first of these columns is Variable. Here we write the name of the variable, which will be used in scripts, in Latin letters and without spaces. You need to name it so that it is easy to remember and at the same time it is easy to understand what this variable is responsible for. In our case, we need to create two variables for the value of the angles of rotation along the X and Y axes (the object can be rotated around the Z axis anyway right in the plan). I decided to name them angle_x and angle_y. In the next column, you need to select the data type. The choices are presented in table 1.

zooming
zooming

The last two types are not used in the construction of the object, but are needed for greater clarity and orderliness of the list in the object parameters window. We need a corner - this is the second pictogram in the table. The third column is Name. Here you can write without rules in any language what exactly we want to see later in the object parameters window. And the last column is Value. Now you can leave 0 here: this value changes at any time both in the script and in the parameters of the object itself. Figure 5 shows how the two new options look in the GDL Object Editor window. 5. Use the arrows at the beginning of the line to move the line to a convenient location.

zooming
zooming

Then you need to save the object under a new name, since the standard library is hardcoded into the container, and objects cannot be overwritten in it. The Object Parameters window will now look like this (Fig. 6).

zooming
zooming

There are two new parameters, the value of which can be changed at any time. But now nothing will happen, since no commands have yet been written using them. Now you need to open the 3D script window. Here is a complete description of how to build a 3D model based on the given parameters. In addition, various macros can be nested in the object. Before all constructions, you need to rotate the coordinate system in which the object will be built. Here it is important to understand the following logic: all rotations, movements and scaling occur differently than when working in ARCHICAD itself. We do not take an element and rotate it, but rotate the global coordinate system (after changing it, it becomes local) front building an object. Move (ADD Command), Rotate (ROT), Scale (MUL) are coordinate system transformation commands. Further transformations can be deleted in the script one by one, several at once, or delete all at once. The reference book describes all this in sufficient detail and with examples. An example of moving a coordinate system in 3D space along three axes at once is shown in Fig. 7. ADD a, b, c

zooming
zooming

So, before all the constructions, we rotate the coordinate system, first along one, then along the other axis. Rotation along the X axis is performed by the ROTX alphax command, where alphax is the counterclockwise rotation angle; instead of alphax, you need to enter a previously created variable. The rotation along the Y axis is performed in the same way (Fig. 8).

zooming
zooming

Now you can set different angles for rotation - and changes in the 3D model will take place in the viewport located at the top left (Fig. 9).

zooming
zooming

Now you can set different angles for rotation - and changes in the 3D model will take place in the viewport located at the top left (Fig. 9). But nothing happens in 2D yet. In a 2D script, an object is built with separate lines and polylines, so the drawing of an object in plan is many times faster. On one site, this is imperceptible, but if there are hundreds of such grids in the project, the braking will be significant. You can calculate the coordinates of the points of these lines and plot them as they would look in the projection of the rotated object, but this is not very simple and not very fast. In this lattice, I propose the following solution: if the angles in X or Y are not equal to zero, then the object in the 2D script, that is, for the plan, will be rendered as a projection of the 3D model, and otherwise in the old way. The projection of the model for a 2D script is built by the PROJECT2 projection_code, angle, method command. You can read what projection_code, angle, method mean in the reference book, but we will get acquainted with the more important command from the section of IF - THEN - ELSE - ENDIF control statements. These are conditional statements that will help you build the conditional clause from the previous paragraph. In fig. 10 I have highlighted the added commands in the 2D script and added "translation" in red to the right.

zooming
zooming

Now you just need to save the object and you can use it (Fig. 11). The advantage of this method over conversion to morph is that the object remains parametric, it can be read in the specifications, in it you can change the dimensions of the slats, the size of the frame, and everything else that was in the original object.

zooming
zooming

So in detail, using this example, we examined the main windows and scripts of the GDL Object Editor. If the object you have chosen for rotation has parameters not in the form of a list, as in this lattice, but in the form of pictures and diagrams, this means that the developer has also written a graphical interface. Most often, the standard list with parameters is hidden, as in Fig. 12: There is no All Parameters section in the drop-down list of parameter pages.

zooming
zooming

In this case, you need to go into the parameters script and find a command that hides all parameters (Fig. 13). This script describes all the actions that affect the parameters: - designation of options or ranges of possible values (VALUES); - any calculations, the result of which is assigned to the parameter (PARAMETERS); - hiding or locking parameters (HIDEPARAMETER, LOCK).

zooming
zooming

You can simply delete the HIDEPARAMETERS ALL line, or by putting a "!" At the beginning of the line, make it unreadable (according to GDL syntax, a line starting with an exclamation mark is considered a comment. Further, I will write descriptions and translations in the screenshots after the "!" Sign). After that, the line "All parameters" will appear in the list of parameter pages, and by selecting it, you will see a standard list with parameters, among which there will be new lines for rotation. EXAMPLE # 2 - text on a symbol I take the next example from the current project. When working with the plan of a multi-apartment residential building, it was required to put the letter "K" on the outdoor units of air conditioners - and so that it was always located vertically. Of course, the letter could have simply been added on top with text or an external inscription-text, but then, when the air conditioner was turned, the text might have had to be moved as well. To start, I added four new parameters (Figure 14):

zooming
zooming

1. Show text: the parameter type is a boolean value, which implies two possible values: 0 (no) and 1 (yes). Thus, the text can be turned on or off.

2. Special text: parameter type - text. Allows you to write any text into the symbol (I intend to use one letter so that it fits inside the rectangle of the air conditioner block).

3. Font: type - text. Please note - some types of writing this variable allow you to select the font values in the column from the list installed on the computer. "Fonttype" calls this list automatically, but if I write "typefont" or just "font", then I have to write the name of the font manually. I noticed this moment by chance in one of the standard objects.

4. Text pen: type - pen. Well, everything is clear here.

Now let's look at the icons that I clicked at the beginning of the lines. The first line has an icon pressed

Image
Image

which means bold - bold. That is, this line in the object parameters window will be bold. The other three have a pictogram

Image
Image

… It means that these lines will be nested in the drop-down list under the first line. In fig. 15 is a screenshot illustrating how it looks in the Object Parameters. To start, I added four new parameters (Figure 15):

Рис. 15. Окно Параметров Объекта
Рис. 15. Окно Параметров Объекта
zooming
zooming

And in fig. 16 - what I added in a 2D script (traditionally with translation and comments).

zooming
zooming

Fig. 16. Added lines in a 2D script In the next screenshot (Fig. 17), for greater clarity, I have tinted different types of words / commands / variables.

zooming
zooming

The object is ready (fig. 18).

zooming
zooming

And if I didn’t write lines with rotation and scaling, then the object would look like in fig. 19.

zooming
zooming

EXAMPLE # 3 - detailing To simplify work on a project, when writing an object, you can add a text parameter with a choice of several options for detailing (simple, medium, detailed). And in the 3D script, when building various small parts, add a condition of the type: if the level of detail = "detailed", then (description of building parts) the end of the condition Global Variables deserve special attention. They are 40 pages long in the reference manual and are grouped by topic for easy search. In the previous example, I used some object orientation data in the project. The same section of the reference manual contains Global Variables for the coordinates of the object location - they are used to create objects such as a leader with coordinates or elevations on a section / elevation. Very often GLOB_SCALE is used - the scale of the drawing (depends on the view according to the current window), at a scale of 1: 100 it is equal to 100, at a scale of 1:20 it is equal to 20. It is most often used to convert the font size to model meters or vice versa. Also, this parameter can be used to "hang" display options on the plan. For example, for a bench, write the following in a 2D script:

IF GLOB_SCALE <100 THEN ! if the scale is larger than 1: 100, then
PROJECT2 3, 270, 2 ! build a projection from a 3D model
ELSE ! otherwise
ENDIF ! end of condition

So on the master plan at a scale of 1: 500, the benches will be displayed as rectangles, and on a fragment with a larger scale, a detailed projection will be drawn. A similar technique, but for a 3D model, is used in standard trees - if you enable the Automatic crown type checkbox. At a certain distance from the camera, the crown type changes from detailed to simple, and from simple to ellipse. True, in order for the object's scripts to be re-read, you need to do something with them - for example, after changing the perspective, highlighting all the trees, open the object's parameters window and, without changing anything, just click OK, or click and uncheck the cover replacement checkbox.

Let me show it using the example of approximating a sphere. This is what I wrote in the 3D script: discam_x = abs (GLOB_EYEPOS_X-SYMB_POS_X) discam_y = abs (GLOB_EYEPOS_Y-SYMB_POS_Y) discam_h = sqr (discam_x ^ 2 + discam_y ^ 2) discam_z = discam_ymbos (GLOB_EYEPOS_Z-sqr) discam_z = abs (GLOB_EYEPOS_Z-sqr) ^ 2 + discam_z ^ 2) if discam <= 20 then res = 50 if discam20 then res = 20 if discam30 then res = 10 if discam> 40 then res = 5 resol res sphere 1 In the script I used the Global Variables GLOB_EYEPOS_X, GLOB_EYEPOS_Y, GLOB_EYEPOS_Z are the coordinates of the location of the camera (eyes) in the 3D-window of the project and SYMB_POS_X, SYMB_POS_Y, SYMB_POS_Z are the coordinates of the location of the object in space; abs - number module (removes "-", if any); sqr - square root; ^ 2 - squaring a number.

In the 3D window, at different distances from the camera, the sphere will be drawn with different approximations. For clarity, I turned on the wireframe mode (Fig. 20).

zooming
zooming

Through the Global Variables, the object can receive: - data about the location of the project (north, latitude, longitude, elevation), set in the corresponding dialog box; - current floor and own floor; - the type of the current view (for example, in the GOST jumpers the following condition is used: if the view type is a list, then build a view of the jumper in a section with position leaders); in the example with a lattice, you can add the following condition: if the type of view is a list, then do not rotate the coordinate system, so that in any case there would be a frontal view in the list of lattices; - incomplete display of constructions (you can force the object not to show some parts if only the kernel is selected).

You can drag wall data into a window or door object. Callouts can get a lot of different information about the element with which they are associated, for example, a checkbox with layers of a multi-layer structure or a leader with a volume of an element. And so on, 40 pages of different and very useful Global Variables. EXAMPLE 4 - zone marker Let's take a look at how a custom zone marker is created. If you create a new object and select the Zone Passport subtype for it in the Details section, then in the Parameters section all the specific parameters that the Zone tool passes to the marker are displayed in blue (Fig. 21).

Рис. 21. Параметры объекта подтипа Паспорт Зоны
Рис. 21. Параметры объекта подтипа Паспорт Зоны
zooming
zooming

Using the TEXT2 command, you can write any of these variables in a 2D script - this is how you get a marker consisting only of text (Fig. 22).

zooming
zooming

Using the general parameters of the zone marker, you can define the text style and line height depending on the font height: DEFINE STYLE “ROOM” AC_TextFont_1, ROOM_LSIZE, 5.0 STYLE “ROOM” row = ROOM_LSIZE / 1000 * GLOB_SCALE * 1.5 text2 0, row, ROOM_NUMBER text2 0, 0, ROOM_NAME text2 0, -row, ROOM_AREA You can create a new parameter to select the type of marker (Fig. 23), set options for it in the Parameters script (Fig. 24) and in the 2D script write different types of marker rendering for different types.

zooming
zooming
zooming
zooming

2D script: if mt = "marker with number" then text2 0, 0, ROOM_NUMBER CIRCLE2 0,0, row endif if mt = "number and area" then text2 0, row / 2, ROOM_NUMBER text2 0, -row / 2, AREA_TEXT endif if mt = "title and area" then text2 0, row / 2, ROOM_NAME text2 0, -row / 2, AREA_TEXT endif if mt = "number, title and area" then text2 0, row, ROOM_NUMBER text2 0, 0, ROOM_NAME text2 0, -row, AREA_TEXT endif if mt = "area only" then text2 0, 0, AREA_TEXT endif In this script, I did not use the predefined area variable as an area, but converted the area to text and added to it units: area = str (ROOM_AREA, 4, 2)! converting a number into text with 2 decimal places AREA_TEXT = area + "sq.m." ! adding to the string value the letters "sq.m." You can supplement the lines in the marker with lines separating some lines. To find the length of a string, use the STW command. Let's add at the beginning of the script: tl1 = stw (ROOM_NUMBER) / 1000 * GLOB_SCALE tl2 = stw (ROOM_NAME) / 1000 * GLOB_SCALE tl3 = stw (AREA_TEXT) / 1000 * GLOB_SCALE if mt = "number and area" then tl = MAX (tl1, tl3) if mt = “number, title and area” then tl = MAX (tl1, tl2) if mt = “title and area” then tl = MAX (tl2, tl3) if mt = “area only” then tl = tl3 AND in the variants of markers, add the lines with the LINE2 command (Fig. 25).

Рис. 25. 2D-скрипт
Рис. 25. 2D-скрипт
zooming
zooming

If the zone number consists of several digits, for the marker, you can create a parameter for the radius of a circle, independent of the font height, or instead of a circle, describe an ellipse-like shape with a length equal to the length of the zone number line that we found earlier: POLY2_ 5, 1 + 2 + 4, -tl1 / 2, row, 1, tl1 / 2, row, 1, tl1 / 2, -row, 1001, -tl1 / 2, -row, 1, -tl1 / 2, row, 1001 You can add a new parameter for floor type (FLOOR_TYPE) and a parameter that allows you to hide or show it (ShowFloorType), and in a 2D script add a triangle with a polyline and text with a floor type: if ShowFloorType then ADD2 0, row * 3 POLY2_ 4, 1, -row * 1.4, -row * 0.8, 1, row * 2.8,60,201, row * 1.4, -row * 0.8, 1, 0,0,700 text2 0,0, FLOOR_TYPE endif For the floor type, it is desirable to add a separate parameter for the pen, as well as points for graphic editing the location of the floor marker. I described in detail how to add graphic editing points in my webinar, and using the link at the end of the article, you can download objects and see how this is implemented in this particular case.

And finally, let's consider another very important subtype of an object that opens up great opportunities - the Global Parameters of the Library (Fig. 26).

zooming
zooming

An object with this subtype does not build or draw anything, it defines parameters in model views. Thus, there you can take out those parameters that you would like to see common for the object, but at the same time be able to set different values for different types.

I'll show this with an example of a zone marker. I came across projects in which there were several sets of zones in different layers for different views. If there is a need for different markers, then the Library Global Parameters is the best solution.

I have a marker in which it is possible to put the type of floor in a triangle and change the type of marking (fig. 27). And these two parameters are moved to a separate file of the Global Library Parameters subtype (Fig. 28).

zooming
zooming
zooming
zooming

For these parameters to be displayed in the Model View Parameters dialog box, you need to register them in the object interface script (Fig. 29). I will not dwell on the special commands for this script in detail, they are described in sufficient detail and with examples in the reference book. I will only say that here we describe where this or that label or button will be located (a field with a choice of options, a check mark, etc.), images can also be inserted into the User Interface. In the standard library, almost every object has a graphical interface; you can see all the possibilities and see how these scripts are written. In addition to the Check button, the script also has a View button. By clicking on it, you can quickly see what happens.

zooming
zooming

You can save the object and view it in the Model View Options dialog box (Figure 30). Here we can change the type of marking at once for all zones in the project (with this marker), but separately for different types.

zooming
zooming

Now, in the zone marker object, you need to query the object for the values of these two parameters. In the Main Script (which is read by the object first, so all calculations and definitions of values that should be used in several scripts, it is better to write here) I write two lines like this: success1 = LIBRARYGLOBAL ("LibraryGlobals20", "ShowFloorType", ShowFloorType) success2 = LIBRARYGLOBAL ("LibraryGlobals20", "mt", mt) "success" will be 1 if the request is successful; otherwise it will be 0.

This can be used to write a warning message instead of a zone marker that the LibraryGlobals20 object has not been loaded into the library.

Then the object works as usual, using two new values: if the type of marking is such and such, then write such and such, and so on. In this article, I have covered only a small part of the capabilities of GDL. With its help, you can create both very simple design elements and very complex objects.

For example, you are dealing with small and simple SIP-panel houses. You have a specific list of options for changing the project: - the length and width of the house can be from 2.4 to 24 meters with a step of 1.2 m; - if the width exceeds 6 m, then there should be another wall in the middle; - two options for floor heights depending on the panel size; - number of storeys - one or two floors; - windows can be in certain places of panels of a certain size; - finishing of facades in three versions; - roofing in three versions; - wall thickness of several standard sizes, and so on.

You can set all these parameters for the object by adding the cost per square meter of the panel, roof, decoration, etc. And in 2D and 3D scripts of the object, completely build and draw this house with variables instead of static dimensions. So that the user does not get confused in a long list of parameters, you can write a graphical interface for several pages with pictures and diagrams. In the Main Script, calculate all volumes and display the cost. It is also possible to display a table with the layout of the panels in a 2D script next to the plan. Writing such an object will take a lot of time, you will need to draw up a detailed technical specification, provide for all the nuances, but then you will receive not just an object, but almost a program in which, by choosing parameters, you can get a set of a draft design with a calculation of materials and cost for the customer. Hopefully this overview has piqued someone's interest in the capabilities of GDL. My story began with a keen desire to change some small detail in some standard zone marker, and the more I read the guide, the more the potential of this tool, in my opinion, very useful for an architect, is revealed. From the link below you can download all the objects that were considered as examples in this article: Download examples Note. ARCHICAD 20 was used to write these objects, so they will not open in earlier versions. About GRAPHISOFT GRAPHISOFT Company® revolutionized the BIM in 1984 with ARCHICAD® Is the industry's first BIM solution for architects in the CAD industry. GRAPHISOFT continues to lead the architectural software market with innovative products such as BIMcloud ™, the world's first real-time collaborative BIM design solution, EcoDesigner ™, the world's first fully integrated energy modeling and energy efficiency assessments of buildings and BIMx® Is the leading mobile app for showcasing and presenting BIM models. Since 2007, GRAPHISOFT has been part of the Nemetschek Group.

Recommended: