Modular multiplication on a circle
Here is yet another implementation of the drawing algorithm commonly known as "Modular multiplication on a circle", inspired by this Houdini tutorial and countless images, which were drawn using this very algorithm and are offered for purchasing in microstock agencies.

Usage: play with the "Number of steps" and "Multiplier" values, some combinations result in generation of visually interesting geometric patterns, export the result to SVG or PNG file.
Optional Line start offset and Line end offset values result in drawing a line which is shorter than the one calculated by the basic algorithm, on one and/or another end of the line.
 Note: colorization is not exported with SVG since the way it is applied in the JS drawing code has no direct SVG equivalent. SVG drawing is exported as a set of paths (stroke color: black, stroke width: 1). In this page the same set is combined with a radial gradient, which is visible only there, where paths exist.
A word about Houdini implementation: instead of using multiple circles as the basis for generating lines as it is done in the mentioned above tutorial, one can do it in VEX code, as explained on the bottom of the page.
Modular multiplication on a circle in Side FX Houdini using VEX
In order to generate the lines in Houdini VEX do the folloving:
 Create a Geometry node and drop a Point Wrangle node into it
 Copy and paste the VEX code presented below into the Point Wrangle node
 Set the "Run Over" option of the Point Wrangle node to "Detail (only once)"
 Click on the "Create parameters" button of the Point Wrangle node and set the values of the created input elements to something meaningful. The "Angle" channel defines angular position of the starting point on the circle in degrees, its value of 90 should result in the output matching the one of the tutorial mentioned above.
VEX code of the basic algorithm  lines only
int steps = chi("Steps");
int multiplier = chi("Multiplier");
float scale = ch("Scale");
float angle = ch("Angle")/180*PI;
float step = PI*2/steps;
for (int i = 0; i < steps; ++i)
{
addpoint(0, set(cos(angle)*scale, sin(angle)*scale, 0.0));
angle += step;
}
for (int i = 0; i < steps; ++i)
{
int primid = addprim(0, "polyline");
addvertex(0, primid, i);
addvertex(0, primid, i * multiplier % steps);
}
VEX code allowing to adjust length of the lines
int steps = chi("Steps");
int multiplier = chi("Multiplier");
float scale = ch("Scale");
float angle = ch("Angle")/180*PI;
float startoffset = ch("StartOffset");
float endoffset = ch("EndOffset");
int pointlerp(vector positionsarray[]; int p0, p1; float ratio)
{
vector pos0 = positionsarray[p0];
vector pos1 = positionsarray[p1];
return addpoint(0, lerp(pos0, pos1, ratio));
}
float step = PI*2/steps;
vector positions[] = {};
for (int i = 0; i < steps; ++i)
{
vector pos = set(cos(angle)*scale, sin(angle)*scale, 0.0);
push(positions, pos);
addpoint(0, pos);
angle += step;
}
for (int i = 0; i < steps; ++i)
{
int primid = addprim(0, "polyline");
if(startoffset == 0 && endoffset == 0)
{
addvertex(0, primid, i);
addvertex(0, primid, i * multiplier % steps);
}
else
{
int endpt = i * multiplier % steps;
int startpt = startoffset == 0 ? i : pointlerp(positions, i, endpt, startoffset);
if(endoffset != 0)
endpt = pointlerp(positions, endpt, i, endoffset);
addvertex(0, primid, startpt);
addvertex(0, primid, endpt);
}
}
The article was created on 22.01.2018