Using AngelScript CSG

Download AngelScript CSG version V2.0-02, with IDE included
Windows 64bit here.
Linux (K)ubuntu 15.10 64bit here.

AngelScript CSG is a work in progress, see the previous articles on this blog. Today's article is providing practical information on installing and using the software.

Windows 64bit

The AngelScript CSG setup package will install the script compiler as_csg.exe and the editor/IDE as_ide.exe. ​​ However, before installing the setup package, please make sure to install the Visual C++ Redistributable Packages for Visual Studio 2013, issued by Microsoft. You will also need a recent version of OpenSCAD installed on your system.

Linux (K)ubuntu 15.10 64bit

The angelscript_csg.tar.gz contains the binaries for AngelScript CSG script compiler as_csg and the editor/IDE as_ide. Extract the contents to a suitable folder ~/angelscript_csg is recommended. ​​ You will also need a recent version of OpenSCAD installed on your system. If you extracted to folder ~/angelscript_csg, add this to bottom of your ~.bashrc file:

 export PATH=$PATH:~/angelscript_csg

export LD_LIBRARY_PATH==LD_LIBRARY_PATH=:~/angelscript_csg

A small taste of the AngelScript language

If you are familiar with C, C++ or similar languages the learning curve will be short and painless. AngelScript is similar in most respects. A relatively complete description of the general language is found here, but as a short primer here are a few key points to know

  • The language is strongly typed, variables must be declared with a type as in C or C++

  • Line comment lines are preceded by double slash //

  • Comment blocks begin with /* ​​ and end with */

  • There are two forms of objects, reference types and value types.

The value types are like the primitives

 int i  ​​​​ ​​ = 0;  ​​ ​​ ​​ ​​ ​​ ​​​​ // an integer value type

doble pi = 3.13159; ​​ // a floating point value type

The reference types uses object handles. Object handles are used to hold references to other objects. When calling methods or accessing properties on a variable that is an object handle you will be accessing the actual object that the handle references, just as if it was an alias.

 double radius = 3;

 sphere@ s ​​  = sphere(radius);  ​​ ​​ ​​ ​​ ​​​​ // handle to a sphere

cylinder@ s = cylinder(10,radius); ​​ // handle to a cylinder

cylinder@ s2 = @s;    // 2nd handle to same cylinder

  • Functions are defined as in C/C++, returning value types or reference types

double sum(double a, double b)

{

 ​​ ​​ ​​​​    ​​​​ return a+b;

}

solid@ sum(cylinder@ c, sphere@ s)
{
   ​​​​ return union3d(c,s);
}

  • Arrays may be defined for value types or reference types

int[] iarr = {0,1,2,3,4,5};

solid@[] sarr = { cylinder(10,3), sphere(3) };

 

// build a growing array of spheres with increasing radius

sphere@[] spheres;

for(int i=0; i<10; i++) spheres.push_back(sphere(i));

 

// report the size of the array to terminal (answer will be 10)

cout << spheres.size() << endl();

The above is just a small taste of the language. If it catches your interest, you may want to look at the the full language description for more details. Remember also that withing the AngelScript CSG IDE you can use Help → View Documentation to find more specifics on how to construct the various CSG objects that are not described in the general AngelScript language description.

Another topic is transformations, but we leave that for another day.

AngelScript CSG IDE

Download AngelScript CSG version V2.0-02, with IDE included
Windows 64bit here.
Linux (K)ubuntu 15.10 64bit here.

This is an update to previous articles part 1, ​​ part 2 and part3 on AngelScript CSG. It is a work in progress, the goal is to establish a powerful modelling system enabling Constructive Solid Geometry (CSG) modelling based on the AngelScript scripting language.
Today's update is to introduce phase 2 of this project – the ​​ AngelScript CSG IDE. The first phase focused on the implementation of the scripting compiler, that is the command line program that interprets AngelScript .as files and turn them into OpenSCAD .csg files. The addition in phase 2 is an editor/IDE that allows easy editing of multiple AngelScript files with syntax highlighting, brace matching etc. It also runs the script compiler and highlights any errors in an easy manner. For model visualisation, launch OpenSCAD from within the IDE. If the .as file is changed/recompiled then OpenSCAD can be made to refresh automatically.

Below is a quick introduction to how it works. The user (you) writes AngelScript CSG code in the IDE and compiles it using either the Build menu, the green arrow in the toolbar or simply pressing F5 on the keyboard. This will save the *.as file to the disk and run the command line compiler, producing an OpenSCAD *.csg file. If you have not already done so you can launch OpenSCAD from the Build menu (or press F6) to view the 3d model.

If you set OpenSCAD to automatically reload modified files, you can simply continue editing the *.as file and press F5 and it will be updated automatically in OpenSCAD when required.

Angelscript CSG and OpenSCAD working together.

Below is an illustration of what happens if you make a coding mistake, you get an error message in red in the console window at bottom, and a red marker indicates the line of the first error. In the exampe, the user wrote 'doublex' by mistake instead of 'double'.

Highlighting coding errors (Windows)

The program also runs on Linux Kubuntu 15.10 64 bit. Below is the Find & Replace feature used under linux to fix a similar programming error.

Fixing coding errors (Linux)

All for today :-) Please give it a try and leave your comments below.

 

AngelScript CSG – part 3

Download AngelScript CSG version V2.0-02, with IDE included
Windows 64bit here.
Linux (K)ubuntu 15.10 64bit here.


This is an update to previous articles part 1 and part 2 on AngelScript CSG, which is using AngelScript as a language for Constructive Solid geometry (CSG).

Today's update is to describe the latest couple of changes. One may now create polyhedrons from image files and from OpenSCAD style text files. To support images a new command “image2d” has been introduced, supporting image files of type PNG, JPG, GIF, XPM and more. It is also possible to resample an imported image before using it to create a 3d model.

Ektepar (Carsten J. Arnholm) - Copyright ©

From photo to 3d-model

The code:

shape@ main_shape()

{  ​​​​ 

 ​​ ​​​​ image2d@ image = image2d("ektepar_ca.png");

 ​​ ​​​​ uint w = image.width();

 ​​ ​​​​ uint h = image.height();

 ​​ ​​​​ double s =0.002;

 ​​ ​​​​ double dx=400;

 ​​ ​​​​ double dy=400;

 ​​ ​​​​ auto surface = loft3d( image,dx,dy,w);

 ​​ ​​​​ return ​​ mirror(0,1,0)*

 ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​​​ scale(s/5,s/5,2*s)*

    ​​​​ polyhedron(surface,vec3d(0,0,-300),h);

}

If the image is very large it is a good idea to downsize it before it is imported, or alternatively use the “resize” method supported on image2d objects. A third way is to keep the image size but reduce the final segmentation. To reduce the segmentation by half in the example above, you could enter w/2 in the call to loft3d and h/2 in the call to polyhedron.
Another example is using an
OpenSCAD surface text file to create a model of a wolf on a mug:

 

Wolf from text file

As in the case with the image, the data is used to create a lofted surface and from that a polyhedron is created. In this case the data file contains 300 data points in X and 275 rowx (y).

const double pi = 4.0*atan(1.0);

double to_rad(double deg) { return pi*deg/180.0; } ​​ // from degree to radian

 

shape@ main_shape()

{  ​​​​ 

 ​​ ​​​​ double h = 30;

 ​​ ​​​​ double r = 30;

 ​​ ​​​​ double e = 3;

 

 ​​ ​​​​ solid@ cyl = cylinder(h+10,r+e);

 

 ​​ ​​​​ // model space distance between the data points

 ​​ ​​​​ double dx=1;

 ​​ ​​​​ double dy=1;

 

 ​​ ​​​​ // specify that default segmentation shall be used, i.e.

 ​​ ​​​​ // all points in the file in s-dir (=x) and t-dir (=y)

 ​​ ​​​​ int ns_seg = -1;

 ​​ ​​​​ int nt_seg = -1;

 ​​ ​​​​ loft3d@ surface = loft3d("model.data.txt",dx,dy,ns_seg);

 ​​ ​​​​ solid@ ​​ wolf = translate(-23.1,-0.5,h+5) *

 ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​​​ rotate_x(to_rad(-90)) *

 ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​​​ scale(0.154, 0.109, 1)*

 ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​​​ polyhedron(surface,vec3d(0,0,-30),nt_seg);  ​​​​ 

 ​​ ​​​​ return union3d(cyl,wolf);

}

 

void main()

{

 ​​ ​​​​ shape@ obj = main_shape();

 ​​ ​​​​ obj.write_csg(GetOutputFullPath('.csg'));

}

AngelScript CSG and OpenSCAD

Below is an updated list of the revised CSG related commands. For even more details, and documentation of the functions each object type provides, run the following command, where <filename> is your own AngelScript CSG file, or one of the provided samples.

> as_csg -doc <filename>.as

This will generate a file 'angelscript_csg_doc.txt' listing all the constructors and functions for all the command types.

 

Angelscript CSG

OpenSCAD

2d utilities

 

pos2d  ​​ ​​​​ (position in 2d space)

array type [x,y]

spline2d (spline curve in 2d space)

N/A

image2d ​​ (bitmap image support)

'file' in surface command

2d primitives

 

shape2d ​​ (abstract type)

N/A

circle(double r);

circle(radius)

square(double size, bool center=false);

square(size,center)

rectangle(double dx, double dy, bool center=false);

square([width,height],center)

polygon(array<pos2d@> points);

polygon([points])

polygon(pos2d@ p1,...);

 

2d booleans

 

union2d(array<shape2d@> shapes);

union() { ... } ​​ 

union2d(shape2d@ shape, …);

 

difference2d(array<shape2d@> shapes);

difference() { ... }

difference2d(shape2d@ shape, …);

 

intersection2d(array<shape2d@> shapes);

intersection() { ... }

intersection2d(shape2d@ shape, …);

 

hull2d(array<shape2d@> shapes);

hull() {...}

hull2d(shape2d@ shape, …);

 

minkowski2d(array<shape2d@> shapes);

minkowski() {...}

minkowski2d(shape2d@ shape, …);

 

offset2d(shape2d@ shape, double r);

offset(r=...)

soffset2d(shape2d@ shapes, double delta, bool chamfer=false);

offset(delta=…, chamfer=false/true)

3d utilities

 

pos3d  ​​ ​​​​ (position in 3d space)

array type [x,y,z]

vec3d  ​​ ​​​​ (vector in 3d space)

array type [x,y,z]

spline3d (spline curve in 3d space)

N/A

loft3d  ​​​​ (lofted surface)

N/A

3d primitives

 

solid  ​​​​ (abstract type)

N/A

cone(double h, double r);

cylinder(h,r1,r2);

cube(double size);

cube(size);

cuboid(double dx, double dy, double dz);

cube([width,depth,height]);

cylinder(double h, double r);

cylinder(h,r);

sphere(double r);

sphere(r):

polyhedron(points,faces)

polyhedron(points,faces)

polyhedron(surface,offset)

polyhedron(points,faces)

3d booleans

 

union3d(array<solid@> shapes);

union() { ... } ​​ 

union3d(solid@ shape, …);

 

difference3d(array<solid@> shapes);

difference() { ... }

difference3d(solid@ shape, …);

 

intersection3d(array<solid@> shapes);

intersection() { ... }

intersection3d(solid@ shape, …);

 

hull3d(array<solid@> shapes);

hull() {...}

hull3d(solid@ shape, …);

 

minkowski3d(array<solid@> shapes);

minkowski() {...}

minkowski3d(solid@ shape, …);

 

linear_extrude(shape2d@ shape, double height);

linear_extrude(height=..) { ... }

rotate_extrude(shape2d@ shape, double angle);

rotate_extrude(angle=..) { ... }

2d/3d transformations

tmatrix  ​​​​ (abstract type)

multimatrix([ …. ]);

rotate_x(double rx);

rotate([rx,0,0]);

rotate_y(double ry);

rotate([0,ry,0]);

rotate_z(double rz);

rotate([0,0,rz]);

scale(double sx, double sy, double sz=1.0);

scale([x,y,z]);

translate(double dx, double dy, double dz=0.0);

translate([dx,dy,dz]);

mirror(double dx, double dy, double dz);

mirror([dx,dy,dz]);

 

AngelScript CSG – part 2

Download AngelScript CSG version V2.0-02, with IDE included
Windows 64bit here.
Linux (K)ubuntu 15.10 64bit here.


This is an update to the first article on AngelScript CSG, where the idea of using AngelScript as a language for Constructive Solid geometry (CSG) was introduced. Since the first article, I found the idea interesting enough to warrant implementation of more functionality so that the language could be tested a bit further. To do that, almost all of the OpenSCAD modelling features have now been implemented in AngelScript CSG, there is an up-to-date summary at bottom. ​​ If you want to give it a try, use the download link at the top of this article, it comes with several small samples, plus a language definition file for Notepad++ so you can edit *.as files with AngelScript CSG syntax highlighting.

Unlike OpenSCAD, AngelScript CSG is strongly typed and procedural. Procedural means it executes the script just like in most other languages, it isn't just a static declaration. These features makes the language very expressive and general, with lots of possibilities for the user to write own functions etc.

Currently, AngelScript CSG is implemented as a command line “compiler”. It reads an *.as file and creates a *.csg file of the same name. The *.csg file is compatible with OpenSCAD, currently used for model display and generation of STL files. ​​ 

In addition to implementing most of the features in OpenSCAD, there are also some unique features in AngelScript CSG, such as the ability to define polygons from 2-dimensional splines and the ability to define lofted surfaces from 3-dimensional splines. The lofted surface can then for example be used as basis for creating a polyhedron. This is illustrated in the following example:

Polyhedron generated from a lofted surface

The code:

// Angelscript CSG - Lofted surface/polyhedron example

 

shape@ main_shape()

{  ​​​​ 

 ​​ ​​ ​​​​ // Create 3 arrays with guide points

 ​​ ​​ ​​​​ pos3d@[] c1 = { pos3d(0, ​​ 0, 0), ​​ pos3d(100, 50, ​​ 5), pos3d(200, ​​ 0, 0) };

 ​​ ​​ ​​​​ pos3d@[] c2 = { pos3d(0,100,40), ​​ pos3d( 80,100,-15), pos3d(200,100,10) };

 ​​ ​​ ​​​​ pos3d@[] c3 = { pos3d(0,200, 0), ​​ pos3d(100, 150, 5), pos3d(200,200, 0) };

 

 ​​ ​​ ​​​​ // Create 3d spline curves and from them a lofted surface

 ​​ ​​ ​​​​ spline3d@[] curves = { spline3d(c1),spline3d(c2),spline3d(c3) };

 ​​ ​​ ​​​​ auto surface = loft3d( curves );

 ​​ ​​ ​​​​ 

 ​​ ​​ ​​​​ // Create polyhedron from the surface with a surface copy offset -1mm

 ​​ ​​ ​​​​ return polyhedron(surface,vec3d(0,0,-1));

}

 

void main()

{

 ​​ ​​​​ shape@ obj = main_shape();

 ​​ ​​​​ obj.write_csg(GetOutputFullPath('.csg'));

}

Another example is using the language features and create user written scripting functions to do general things like a “filleted union” of 2d shapes:

AngelScript CSG generated shape displayed in OpenSCAD via generated .csg file

The idea is to fillet all concave corners of any 2d shape with a single generic function. I am not sure it is possible to express something as generic as this directly in an OpenSCAD module, but I could be wrong. Notice the use of a or loop to assemble 2d shapes to union:

 

// fillet_union: function to fillet any concave corners

shape2d@ fillet_union(double r, shape2d@[] parts)

{

 ​​ ​​​​ shape2d@[] offset_parts;

 ​​ ​​​​ for(uint i=0; i<parts.size(); i++) {

 ​​ ​​ ​​ ​​ ​​​​ // offset each part outwards by +r (straight)

 ​​ ​​ ​​ ​​ ​​​​ offset_parts.push_back(soffset2d(parts[i],+r));

 ​​ ​​​​ }

 ​​ ​​​​ 

 ​​ ​​​​ // compute union & offset inwards by -r to fillet concave corners

 ​​ ​​​​ return offset2d(union2d(offset_parts),-r);

}

 

shape@ main_shape()

{  ​​​​ 

 ​​ ​​​​ double r ​​ = 50;  ​​​​ // radius of circle

 ​​ ​​​​ double rf = 10;  ​​​​ // radius of fillet

 ​​ ​​​​ 

 ​​ ​​​​ // collect some 2d primitives in an array (it can be any number)

 ​​ ​​​​ shape2d@[] parts = { circle(r), square(2*r), translate(2*r,2*r)*circle(r) };

 ​​ ​​​​ 

 ​​ ​​​​ // compute the fillet union

 ​​ ​​​​ return fillet_union(rf,parts);

}

void main()

{

 ​​ ​​​​ shape@ obj = main_shape();

 ​​ ​​​​ obj.write_csg(GetOutputFullPath('.csg'));

}

Obviously, anything that AngelScript CSG can generate can also be created in OpenSCAD in some way, but the above is a small taste of the expressiveness in a procedural and object oriented scripting language. There are things you can easily express in a language like this that is much harder (at least for me) to express in the static OpenSCAD language. Instead of expressing the CSG tree explicitly, one may write algorithms to create models and these can be saved as CSG data at any time in the process.

The fact that OpenSCAD is used as a back end is for convenience at this stage. Clearly, it limits what can be expressed to what exists in OpenSCAD. But this is more than enough to evaluate the usefulness of AngelScript as a general scripting language and in particular to its application in CSG modelling.

Details on the updated features

Essentially what happened was that the “csg_” prefixes mentioned in the first article were removed from the commands to make them less verbose. Also, a full set of 2d primitives and boolean operations was introduced. The booleans now have “2d” and “3d” suffixes as you cannot mix 2d and 3d primitives in boolean operations (the same is true in OpenSCAD if less visible).

Mirror transformations, 2d and 3d hull and minkowski were also included for completeness. For 2d, offset was included for both the “radius” and straight “delta” cases. For the booleans, alternate syntax is now allowed for up to 5 single objects without requiring an array, otherwise one can use arrays for any number of input objects.

The AngelScript CSG commands are in fact C++ classes exposed in the AngelScript language. Therefore, it is possible to realise type safety using a class hierarchy. Below is the class hierarchy for the commands that create 2d or 3d geometric objects.

Angelscript CSG command type hierarchy for geometric objects

AngelScript CSG command type hierarchy for transformation objects

Other command types include pos2d, spline2d, pos3d, vec3d, spline3d and loft3d.

Being objects, every command provides functions for retrieving data., this can be quite useful, below are a couple of examples for “cylinder” and the “rotate_x” command objects.

cylinder functions

rotate_x functions

With such functions, you can create explicit dependencies in the modelling of objects by using e.g. the radius of a cylinder as a parameter to another command, creating something that depends on the cylinder radius.

AngelScript CSG vs. OpenSCAD

Below is an updated list of the revised CSG related commands. For even more details, and documentation of the functions each object type provides, run the following command, where <filename> is your own AngelScript CSG file, or one of the provided samples.

> as_csg -doc <filename>.as

This will generate a file 'angelscript_csg_doc.txt' listing all the constructors and functions for all the command types.

 

Angelscript CSG

OpenSCAD

3d utilities

 

pos2d  ​​ ​​​​ (position in 2d space)

array type [x,y]

spline2d (spline curve in 2d space)

N/A

2d primitives

 

shape2d ​​ (abstract type)

N/A

circle(double r);

circle(radius)

square(double size, bool center=false);

square(size,center)

rectangle(double dx, double dy, bool center=false);

square([width,height],center)

polygon(array<pos2d@> points);

polygon([points])

polygon(pos2d@ p1,...);

 

2d booleans

 

union2d(array<shape2d@> shapes);

union() { ... } ​​ 

union2d(shape2d@ shape, …);

 

difference2d(array<shape2d@> shapes);

difference() { ... }

difference2d(shape2d@ shape, …);

 

intersection2d(array<shape2d@> shapes);

intersection() { ... }

intersection2d(shape2d@ shape, …);

 

hull2d(array<shape2d@> shapes);

hull() {...}

hull2d(shape2d@ shape, …);

 

minkowski2d(array<shape2d@> shapes);

minkowski() {...}

minkowski2d(shape2d@ shape, …);

 

offset2d(shape2d@ shape, double r);

offset(r=...)

soffset2d(shape2d@ shapes, double delta, bool chamfer=false);

offset(delta=…, chamfer=false/true)

3d utilities

 

pos3d  ​​ ​​​​ (position in 3d space)

array type [x,y,z]

vec3d  ​​ ​​​​ (vector in 3d space)

array type [x,y,z]

spline3d (spline curve in 3d space)

N/A

loft3d  ​​​​ (lofted surface)

N/A

3d primitives

 

solid  ​​​​ (abstract type)

N/A

cone(double h, double r);

cylinder(h,r1,r2);

cube(double size);

cube(size);

cuboid(double dx, double dy, double dz);

cube([width,depth,height]);

cylinder(double h, double r);

cylinder(h,r);

sphere(double r);

sphere(r):

polyhedron(points,faces)

polyhedron(points,faces)

polyhedron(surface,offset)

polyhedron(points,faces)

3d booleans

 

union3d(array<solid@> shapes);

union() { ... } ​​ 

union3d(solid@ shape, …);

 

difference3d(array<solid@> shapes);

difference() { ... }

difference3d(solid@ shape, …);

 

intersection3d(array<solid@> shapes);

intersection() { ... }

intersection3d(solid@ shape, …);

 

hull3d(array<solid@> shapes);

hull() {...}

hull3d(solid@ shape, …);

 

minkowski3d(array<solid@> shapes);

minkowski() {...}

minkowski3d(solid@ shape, …);

 

linear_extrude(shape2d@ shape, double height);

linear_extrude(height=..) { ... }

rotate_extrude(shape2d@ shape, double angle);

rotate_extrude(angle=..) { ... }

2d/3d transformations

tmatrix  ​​​​ (abstract type)

multimatrix([ …. ]);

rotate_x(double rx);

rotate([rx,0,0]);

rotate_y(double ry);

rotate([0,ry,0]);

rotate_z(double rz);

rotate([0,0,rz]);

scale(double sx, double sy, double sz=1.0);

scale([x,y,z]);

translate(double dx, double dy, double dz=0.0);

translate([dx,dy,dz]);

mirror(double dx, double dy, double dz);

mirror([dx,dy,dz]);

 

Math functions

 

Other language features

 

AngelScript CSG

Download AngelScript CSG version V2.0-02, with IDE included
Windows 64bit here.
Linux (K)ubuntu 15.10 64bit here.

Constructive Solid geometry (CSG) is a form of 3d modelling where you assemble simple 3d primitives into a hierarchy to make interesting 3d objects. ​​ This technique is much used e.g. in 3d printing.

 

The key to CSG modelling is using 3d primitives like cylinder, sphere, cube … and for each of them apply 3d transformations like translate, rotate and scale. Finally, the transformed pieces are assembled into a CSG tree using 3d operations like union, difference and intersection. The result is a (hopefully) interesting 3d object. Wikipedia has a nice example:

A CSG assembly (from Wikipedia)

OpenSCAD

A popular open source program for CSG modelling with focus on 3d Printing is OpenSCAD. It is different from other CAD software in that it does not provide a complicated graphical user interface for modelling, but instead provides its own language for describing models. Below is a very simple example of OpenSCAD code for defining a simple “lollipop”, i.e. an object consisting of a stick to hold it and the head with the sweet:

OpenSCAD code below

// CSG modelling with OpenSCAD. ​​ 

// Carsten Arnholm, 26. December 2015

 

module lollipop(height, radius, head_radius )

{

 ​​ ​​​​ // use cylinder and sphere primitives and

 ​​ ​​​​ // assemble as a union.

 ​​ ​​​​ union() {

 ​​ ​​ ​​ ​​ ​​​​ cylinder(height,r=radius);

 ​​ ​​ ​​ ​​ ​​​​ translate([0,0,height])

 ​​ ​​ ​​ ​​ ​​​​ sphere(head_radius);

 ​​ ​​​​ }

 ​​ ​​​​ // The union is implicitly returned

}

// model parameters

height  ​​ ​​ ​​ ​​ ​​​​ = 200;

radius  ​​ ​​ ​​ ​​ ​​​​ = 5;

head_radius ​​ = 20;

 

// assemble and rotate

rotate([0,30,0])

lollipop(height, radius, head_radius );

 

// export needs to be done from the OpenSCAD GUI
//
File → Export ...

 

The resulting model becomes:

This is all very nice. There are a couple of things to observe, however. One is that The OpenSCAD language is static, i.e. you cannot change the values of the “variables” in the program, they are just declarations of named values. Another is that the models created cannot be assigned to variables and reused via them. The only means of abstraction available are functions (called “modules”) that implicitly return the model created within them. Also, the language is unique to OpenSCAD, and there seems to be significant resources put into developing this language in the OpenSCAD project.

Possible changes

This author is of the opinion that the excellent idea of OpenSCAD as a language oriented CSG modeller would benefit from a couple of (admittedly major) changes:

  • Use an existing scripting language instead of a self made language. This would enable focusing on the modelling features rather than the language syntax. For example, the somewhat ​​ incomprehensible “list comprehensions” ​​ is an example of obscure language syntax.

  • Use a more complete geometry/topology kernel such as for example OpenCascade instead of the facet-based and very resource-demanding CGAL. This could also simplify the language syntax by doing away with the “convexity” parameters that are currently seen in OpenSCAD, they are really irrelevant from a modelling point of view.

These are clearly radical suggestions that will probably never be implemented in OpenSCAD. This does not, however, stop one from experiementing with such ideas. The focus of this post is the language (#2 in the list is left for some other time). Could we use an existing scripting language to implement similar functionality as is currently in OpenSCAD?

AngelScript

I have been playing with the idea of using AngelScript as a language interpreter for CSG modelling. AngelScript has some very nice features for end users and is designed for integration into a C++ application. The language provides pretty much everything in the current OpenSCAD language, plus much more

  • It is not just a declarative language, variables can be modified

  • The script writer can, if needed write functions and even classes.

  • The language is strongly typed

  • and much more.


As an experiment a CSG interpreter prototype using AngelScript has been implemented. As with any real prototype, the idea is not to make a final application, just explore the possibilities. This is throw-away work, with the sole intention of learning. Also, since this prototype does not address #2 in the list, the prototype simply exports to the OpenSCAD .csg file format for visualisation (The OpenSCAD .csg file format is not the same as the OpenSCAD language, but it is still dependent on it).

So let us try the “lollipop” example in the prototype AngelScript CSG interpreter:

AngelScript CSG code below

// CSG modelling with AngelScript
//
Carsten Arnholm, 25. December 2015

// a global constant + a function to convert from degrees to radians

const double pi = 4.0*atan(1.0);  ​​ ​​ ​​ ​​ ​​ ​​​​ 

double rad(double deg) { return pi*deg/180.0; }

 

csg_solid@ lollipop(double height, double radius, double head_radius )

{

 ​​ ​​​​ // use cylinder and sphere primitives and

 ​​ ​​​​ // assemble as a union. We choose to keep variables here.

 ​​ ​​​​ csg_cylinder@ stick = csg_cylinder(height,radius);

 ​​ ​​​​ csg_solid@ head  ​​ ​​ ​​​​ = csg_sphere(head_radius);

 ​​ ​​​​ return csg_union( array<csg_solid@> =

 ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​​​ { stick, translate(0,0,stick.height())*head }

 ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​​​ );

}

 

void main()

{

 ​​ ​​ ​​​​ // model parameters

 ​​ ​​ ​​​​ double height  ​​ ​​ ​​ ​​ ​​​​ = 200;

 ​​ ​​ ​​​​ double radius  ​​ ​​ ​​ ​​ ​​​​ = 5;

 ​​ ​​ ​​​​ double head_radius ​​ = 20;  ​​ ​​ ​​ ​​ ​​​​ 

 ​​ ​​ ​​ ​​ ​​ ​​ ​​​​ 

 ​​ ​​ ​​​​ // assemble and rotate

 ​​ ​​ ​​​​ csg_solid@ model = rotate_y(rad(30)) *

 ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​​​ lollipop(height,radius,head_radius);

 

 ​​ ​​ ​​​​ // export to OpenSCAD-style .csg for display

 ​​ ​​ ​​​​ model.write_csg("lollipop.csg");

}

 

The generated CSG file “lollipop.csg” :

Generated OpenSCAD-style CSG file (generated from AngelScript CSG)

group() {

 ​​ ​​​​ multmatrix([[0.866025396499,0,0.500000012618,0],[0,1,0,0],[-0.500000012618,0,0.866025396499,0],[0,0,0,1]]) {

 ​​ ​​ ​​ ​​ ​​​​ union() {

 ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​​​ multmatrix([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]) {

 ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​​​ cylinder( r1=5, r2=5, h=200 );

 ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​​​ }

 ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​​​ multmatrix([[1,0,0,0],[0,1,0,0],[0,0,1,200],[0,0,0,1]]) {

 ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​​​ sphere( r=20 );

 ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​​​ }

 ​​ ​​ ​​ ​​ ​​​​ }

 ​​ ​​​​ }

}

 

The generated CSG file ​​ displayed in OpenSCAD:

As can be seen, the result is the same in both cases, and it makes it possible to evaluate the syntax similarities and differences. For an example as small as this, the AngelScript code does not appear to offer much benefit, but the code illustrates some important differences.

Basic construction and type safety of 3d primitives

First, observe that parts can be referred to in variables, like this

csg_cylinder@ stick = csg_cylinder(height,radius);

 double h = stick.height();  ​​​​ // Ok

The “stick” is a variable of type csg_cylinder, which allows you to do things like extracting data from it in code later, such as reading the cylinder height. The following code is also allowed:

csg_solid@ stick_solid = csg_cylinder(height,radius);

Because every csg_cylinder is also a csg_solid, it can be received as such. You are however, no longer allowed to call the height() function in this case:

 ​​ ​​ ​​ ​​ ​​​​  double h = stick_solid.height();  ​​​​  // error, slick_solid is not a csg_cylinder


… unless you typecast the stick_solid variable back to a csg_cylinder type:

csg_cylinder@ stick = cast<csg_cylinder@>(stick_solid);
 ​​ ​​ ​​​​  
double h = stick.height();  ​​​​ // Ok


The use of the character @ behind the types means that the variable is a handle. All of the CSG objects in the prototype are handle types and is used with the @ character.

3d transformations
Transformations are handled with a syntax familiar to OpenSCAD-users, except an explicit ​​ multiplication operator is used ​​ to indicate the coordinate system transformation from the object being transformed to the coordinate system of the result:

 ​​ ​​ ​​ ​​ ​​​​ csg_solid@ head = translate(0,0,stick.height()) * csg_sphere(head_radius);

 

Transformations can also be stored in variables:

tmatrix@ transform = rotate_y(pi/2) * translate(1,0,0);

 

Angelscript CSG vs OpenSCAD

Thefollowing is a summary of the CSG related commands

Angelscript CSG

OpenSCAD

3d primitives

csg_solid  ​​​​ (abstract type)

N/A

csg_cone(double h, double r);

cylinder(h,r1,r2);

csg_cube(double size);

cube(size);

csg_cuboid(double dx, double dy, double dz);

cube([width,depth,height]);

csg_cylinder(double h, double r);

cylinder(h,r);

csg_sphere(double r);

sphere(r):

3d transformations

tmatrix  ​​​​ (abstract type)

multimatrix([ …. ]);

rotate_x(double rx);

rotate([rx,0,0]);

rotate_y(double ry);

rotate([0,ry,0]);

rotate_z(double rz);

rotate([0,0,rz]);

scale(double sx, double sy, double sz);

scale([x,y,z]);

translate(double dx, double dy, double dz);

translate([dx,dy,dz]);

Boolean operations

csg_difference(csg_solid@ incl, csg_solid@ ​​ excl);
(array<csg_solid@>@ also accepted)

difference() { ... }

csg_intersection(csg_solid@ incl, csg_solid@ ​​ excl);
(array<csg_solid@>@ also accepted)

intersection() { ... }

csg_union(array<csg_solid@>@+ arr);

union() { ... }

 

Math functions

 

Other language features

 

Conclusion

For non-trivial models, it looks like using a language like AngelScript is indeed interesting. The Angelscript CSG prototype shows it is possible to express CSG models that have similar characteristics as OpenSCAD, but using an existing object oriented scripting language. It also shows that using objects will allow more flexible and powerful expressions, since the object properties may be retrieved from the objects directly (like in the example with the cylinder height).

 

To implement such features in AngelScript is reasonably straight forward. All you have to do is write C++ classes for the different concepts and expose constructors and member functions to those classes in AngelScript by means of the AngelScript API. The current prototype implementation is not 100% stable, for some cases there are issues with using the built-in array type (see for example its use in csg_union). However, such issues can always be overcome with a bit of effort.

 

All in all, the prototype appears successful in that it demontrated how a language like AngelScript can be used for CSG modelling.

 

Octave plug-in calling MSVC

This post is about creating plugins to the Windows version of GNU Octave 4.0.0 (Octave) using existing components created with Microsoft Visual Studio 2013 (MSVC). It takes too long to explain why this is sometimes useful, just assume that it is. A typical scenario could be that you have some kind of database accessible from MSVC code and you want to expose the data in Octave.

The figure below illustrates a possible setup. To implement a plugin in Octave, you write a piece of C++ code and compile/link it into a special kind of shared library referred to as an 'oct-file'. Such code can call other components, for example a DLL (Dynamic Link Library) created with MSVC. This way, the oct-file functions as the glue between the Octave application and some other software component.


T
his sounds simple enough, but in practice there are a couple of things to handle to make it work:

First, Octave and its oct-files are compiled using the MinGW GNU g++ C++ compiler (It is not practical to recompile Octave using MSVC) and GNU g++ code is not binary compatible with MSVC code. Therefore, we cannot statically link the MSVC dll with the oct-file and we cannot pass C++ objects in the calls between them, because name mangling schemes and calling conventions are incompatible between the compilers.

Second, Octave and its oct-files are compiled as 32bit. Even if we find a way around the first problem, it will not work if the MSVC component is compiled as 64bit, it has to be 32bit as Octave.

Now the whole thing sounds a lot more complicated, but the problem description also provides the clues to the solution

  • Use a C interface in the calls from the oct-file (GNU g++) to the MSVC code

  • Load the MSVC dll dynamically instead of linking statically

  • Compile the MSVC code as 32 bit

MSVC C++ code

The first bullet above means we must provide global functions declared as extern ”C” in the MSVC C++ code (linked as DLL, and exported), here is a sample header declaration:

#ifndef MSVC_COMP_H
#define MSVC_COMP_H

#ifdef MSVC_COMP_IMPLEMENTATION

 ​​​​ #define MSVC_COMP_PUBLIC __declspec(dllexport)

#else

 ​​​​ #define MSVC_COMP_PUBLIC __declspec(dllimport)

#endif

 

extern "C" {

 ​​ ​​​​ // msvc_get_data returns a pointer to internal data, must not be deleted outside

 ​​ ​​​​ MSVC_COMP_PUBLIC double* msvc_get_data(const char* file_path, const char* data_id, long* nsamp);

}

 

#endif // MSVC_COMP_H

Note that the function takes and returns parameters as if it was an old style C-function, no C++ objects or pointers are allowed, du to the compiler differences. The extern ”C” statement removes any name mangling in the compiled name, it makes it possible to look up from other code (see below).

Notice also that in this case, the function returns a pointer to some numerical data. Such data may be dynamically allocated by the MSVC compiler, and cannot be deleted in the GNU code (in this case the pointer ponts to a global variable internally in the MSVC code and it will be cleaned up in the next call).

The above is just the header file declaration, the implementation of the msvc_get_data function can use all C++ constructs, objects, pointers etc. ​​ The msvc_get_data function is thus just an adapter.

OCT-file code

The second bullet in our list, and how the OCT-file interfaces the MSVC code is best illustrated using an example (note that most error checking has been omitted for clarity).

File oct_get_data.cpp:

#include <octave/oct.h>

#include <fstream>

#include <windows.h>

#include <dMatrix.h>

 

// function pointer to the MSVC fuunction

typedef double* (*msvc_gdfunc)(const char* file_path, const char* data_id, long* nsamp);

 

// Octave function declaration

DEFUN_DLD (oct_get_data, args, nargout, "oct_get_data String")

{

 ​​ ​​​​ int nargin = args.length ();

 ​​ ​​​​ if(nargin < 2) {

 ​​ ​​ ​​ ​​ ​​​​ octave_stdout << "oct_get_data called with " ​​ 

 ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​​​ << nargin << " input and "

 ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​​​ << nargout << " output arguments.\n";

 ​​ ​​ ​​ ​​ ​​​​ octave_stdout << "Usage: ​​ oct_get_data(file_path,data_id); \n";

 ​​ ​​​​ }

 

 ​​ ​​​​ // get the arguments from the Octave call

 ​​ ​​​​ int iarg=0;

 ​​ ​​​​ std::string file_path  ​​​​ = args(iarg++).char_matrix_value().row_as_string(0);

 ​​ ​​​​ std::string data_id  ​​ ​​ ​​​​ = args(iarg++).char_matrix_value().row_as_string(0);

 

 ​​ ​​​​ // load the MSVC DLL dynamically using Windows API

 ​​ ​​​​ HMODULE hdll = LoadLibrary("msvc_comp.dll");

 ​​ ​​​​ if(hdll != NULL) {

 ​​ ​​ ​​ ​​ ​​​​ // get the function pointer

 ​​ ​​ ​​ ​​ ​​​​ msvc_gdfunc msvc_get_data = (msvc_gdfunc)GetProcAddress(hdll,"msvc_get_data");

 ​​ ​​ ​​ ​​ ​​​​ if(msvc_get_data) {  ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​​​ 

 ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​​​ // call the function in the MSVC dll

 ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​​​ long nsamp = 0;

 ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​​​ if(double* values = msvc_get_data(file_path.c_str(),data_id.c_str(),&nsamp)) {

 ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​​​ // assign output data, it returns data column major order, 2 columns

 ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​​​ Matrix result(nsamp,2);

 ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​​​ for(int j=0;j<2;j++) {

 ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​​​ for(int i=0; i<nsamp; i++) {

 ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​​​ result(i,j) = *values++;

 ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​​​ }

 ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​​​ }

 ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​​​ return octave_value(result);

 ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​​​ }

 ​​ ​​ ​​ ​​ ​​​​ }

 ​​ ​​​​ }

 ​​ ​​​​ // empty return value

 ​​ ​​​​ return octave_value_list();

}

The code above declares a function pointer type for our MSVC function. It then uses the Windows API to load the DLL dynamically + look up the pointer to the function. If found, it calls the MSVC function and constructs a suitable matrix object to return to the Octave application.

Obviously much more rigorous error checking is in order.

Compiling the OCT-file

As previously mentioned, the oct-file must be compiled using the GNU C++ compiler to be compatible with Octave. For this purpose we use the mkoctfile utility. It can be done within Octave, or via a Wiindows batch script as shown below.

File cppoct.bat:

@echo off

REM Script to compile C++ into Octave oct-files

REM

REM configure Octave oct compiler

set OCT_VER=4.0.0

set OCT_HOME=C:\Octave\Octave-%OCT_VER%

set OCT_BIN=%OCT_HOME%\bin

set OCT_INC=%OCT_HOME%\include\octave-%OCT_VER%

set OCT_LIB=%OCT_HOME%\lib\octave\%OCT_VER%

REM

REM set Octave bin dir first in path so g++ can be found

set PATH=%OCT_BIN%;%PATH%

REM

REM turn on echo so we can see what is going on as we compile

@echo on

%OCT_BIN%\mkoctfile.exe -I%OCT_INC% -I%OCT_INC%\octave ​​ -L%OCT_LIB% ​​ %1

@echo off

REM tidy up intermediate files

del *.o

Running this script for the oct_get_data.cpp file generates the oct_get_data.oct file.

Using the plugin in Octave

Once the oct-file and the msvc dll exist, it is recommended to store them in a common folder in the file system. In Octave, you then need to specify that folder using 'addpath'.

addpath("C:\\somefolder");
data = oct_get_data("myfile.dat","whatever");
plot( data(:,1), data(:,2));

Assuming the data returned was a matrix of X,Y data, the result could look something like below

 

 

If you find this useful, please add a comment below :-)

Blogging with LibreOffice

This post is just a WordPress workflow test.

The goal (heading 3)

The goal is to find a reasonable way to edit a blog post in WordPress without using the online editor (previous 5 words underlined). The reason is that my internet connection is sometimes unreliable and after having lost the whole text of blog posts during editing, I prefer to edit, format save the text offline using LibreOffice.

This text is therefore edited and formatted entirely in LibreOffice 5, complete with hyperlinks. Below, I also include a one column/2 row borderless table with an image in the first row and a centered caption in the second row:


Above is a random image.
This is the centered image caption, complete with some bolded text.

Having formatted the text, one should be able to transfer the whole thing into WordPress by copy/paste or some other simple process. It goues without saying the transfer should be complete with formatting, tables, links and images. But when I try direct copy/paste, everything just disappears (previous 3 words in italics).

The solution? (heading 3)

Searching the web, I found that lots of of people have similar needs. One tip that seemed interesting was to work around the problem by first pasting the formatted text into an Online HTML editor and then from that into WordPress.

If you read this post, it means the procedure works.

Code::Blocks with MSVC2013

This is a quick placeholder post on how to set up the C++ IDE Code::Blocks with the Microsoft Visual Studio 2013 tool chain. The main information is found in this PDF.

Quote from the PDF:

“This document aims to explain a way to configure Code::Blocks on Windows using different Visual
Studio C++ compilers and Windows SDKs. Where relevant it is discussed how to compile for x86
(32bit) and x64 (64bit) executables.

The general approach taken is to employ the Code::Blocks global variables for configuration of the
compiler and related tools, i.e. set the global variable values according to the needs of the compiler,
SDK and target processor architecture. Examples are provided for MSVC2010 and MSVC2013. By
extension, it should be feasible to reconfigure for other MSVC versions.”

If you have questions or comments to the method described, please comment below.

A pipe dream come true

The old sewage pipes have been a worry since we found out their condition recently. Fixing it seemed like a pipe dream. Today that dream came true, the plumber arrived and did a great job! The old cast iron pipes are out and modern plastic ones are in.  A last goodbye to the old rusted pipes:

image

The new plastic pipes have been installed, quite some difference compared to before:

image

A second pipe goes under the wall next to the fireplace. It continues under the chimney behind the wall and into a washing room.  A pipe from the kitchen also connects there. The big problem with the pipe under the chimney was that it was rusted and totally filled with old muck.  With a lot of effort and high water pressure, enough of it was cleaned to allow a slightly smaller plastic pipe to run inside it. We were saved!

image

Here is a detail of that connection.   The top one had to be cut a bit more after the image was taken as it was cracked. Luckily, the material further in was fine.

image

In the other end the pipes meet and go though the outside wall.

image

Tomorrow the new pipes will be covered by concrete, to prepare for pouring the new concrete floor on top.  A person from the company who will pour the floor showed up today so it seems to come together quite nicely now.

Flooding

It has been a while since the last post, and it is for a reason. On September 2nd we experienced very heavy rainfall, I measured 143mm rain on my weather station that day. The result was that our basement was flooded, the water level was 7-8 cm above the floor. As the basement contained old furniture, library and a home office with desktop computers we found ourselves in crisis mode.

The first action was to limit the damage by moving everything away from the basement and start pumping water. That’s a long story in itself, but to cut it short we got assistance from the insurance company. The second realisation was that  the drainage system needed improvement, here is a small part of that effort:

IMG_0210

My desktop computer found itself under water as well.  The water level was at least up to the bottom PCI slot in the image below. After cutting power and pouring water out of the cabinet, plus drying it for a couple of days, the machine actually rebooted. The only problem was that the motherboard sound card was fried and totally silent.  A cheap sound card was ordered and installed a few days later. It turned out the sound card (ASUS Xonar DG) had limited Linux support, but with a bit of effort it worked just fine, and the computer was back in business:

image

However, the basement was still a complete mess. The wooden floor with heating had been removed and under it we found rough concrete with sand used for levelling (the house is from 1969).  The sand was now all wet and very difficult to remove:

image

The solution was provided by these guys, who sucked out all the sand to allow the the floor to dry:

image

In theory, we could then start planning of the basement floor rebuilding. However, a “problem rarely arrives alone” as we say here.  As the house is from 1969 and the sewage pipes from that period are made from cast iron, it made sense to inspect them for damage.  That turned out to be a good idea…

image

The pipes are completely rusted and in urgent need of replacement. The needed repairs triggered by the flooding are just growing in magnitude and cost. They just have to be done. One might even argue that the good news is that due to the flooding we got a chance to inspect and replace the pipes.  The plumbers will arrive tomorrow…

This post could have been ten times longer, but the main idea is to explain the lack of recent blog posts.  Hopefully, we are now at the low point of this experience. Let the reconstruction begin!