Convert arc to a polygon of tiny segments that looks like the arc

As subject indicates: is there a way to do this from within the application?

Reason for asking: the via-fencing plugin works poorly / “buggily” when the trace has arcs. The defects I notice are precisely incorrect spacing at the points where the arcs join straight segments. I did a quick test, manually drawing a polygon of tiny segments following the same curve that I got from an arc, and the plugin worked flawlessly and produced a correct via fence, all vias equi-spaced.

I’m considering writing a standalone C++ program that does this (I mean, it’s just a program that does file I/O and math — in my book, that spells fun,fun,fun !!! :smile:) … but, let’s say that I don’t have lots of time to spend on having fun, so if there is a native way to do this, I’d like to know about it.

Thanks!

Ok, so let’s reformulate the question, after having had my fun.

Seeing after a couple of hours no answers, and having tried in the application, I figured that the answer to my original question is NO, so I went ahead and had my fun. The program seems to be working flawlessly, replacing the arc with a polygon of tiny segments. (and yes, the via-fence generator works perfectly well with the modified file that has the polygons instead of arcs)

The new questions are:

  • Is there any advantage in having an arc, instead of, say, 32 little segments? (for example, is the generated Gerber cleaner?)
  • If there is, then how can I save the arc in the file? (the idea being: generate the polygon, run the via-fence generator, then remove the polygon and restore the original arc)

KiCad isn’t (yet) very good at editing arcs, but at least there’s no way to edit small segments in any reasonable way if you want to modify the design. So, having arcs has an advantage.

Why don’t you just try? The gerber viewer can show items as outlines, so you can see how complex the arc is in both ways. But I think this isn’t important. KiCad exports many arc-like shapes as bunches of straight lines, anyway.

I suppose your C++ program handles the pcb file directly, reading and writing the file text. You could for example add an inner layer and save the arcs there (maybe offset so that they don’t clash with vias). Then you can use them by changing their layer again and moving them.

But honestly, creating a python script would be better. It doesn’t need IO at all, it can modify the board items as objects. I don’t think it would be terribly difficult to translate the math to python.

Or better yet, contribute by fixing the original plugin.

Thanks eelik for these comments/suggestions. (excellent point, ability to later edit the design … I feel embarrassed that I didn’t think of that to begin with! :smile: )

One problem with your suggestions is: I am pathologically Python-phobic and therefore Python-impaired! :upside_down_face:

So, although I’m sure that the math can very easily be translated to Python by someone fluent in Python, that someone at the present time cannot be me.

One thing that had already crossed my mind (similar to your very last suggestion) was to share the idea with the author of the plugin, so that if making it work with arcs is too tricky or involved, then the plugin could temporarily convert arcs to polygons just for the purpose of generate the fence, without committing that conversion to the actual board’s layout, so that the arcs remain in the layout.

Why don’t you share the source code here?

Sure thing (although the code is extremely unclean and nothing remotely close to “quality code”). If for some inexplicable reason anyone decides to use this code, you shall use it at your own risk; no warranties (express or implied) provided, etc. etc.

The relevant portion is member-function as_polygon() of class Arc (well, I did them all as quick struct's). In the pcb file, I saw that arcs are defined by three points on the arc, with the middle point being equi-spaced. Thus, the three points form an isosceles triangle (this was just inference; I didn’t really look up the documentation).

If we denote the three points (from the KiCAD file) p1, pm, p2, with pm being the point in the middle, and we denote the center of the circle as C, then I work with the triangle C→pm→m1, where m1 is the middle point from p1 to pm; that is, m1 = (p1+pm)/2. The angle at pm is denoted angle, and the angle at m1 is 90 degrees. cos(angle) is obtained from the inner product of the vectors p1‒pm and m1‒pm, and that cosine directly gives me the radius. Points on the circle are constructed by taking the vector starting at the center, normalizing to unit length, then multiply by the radius.

Class (struct, really) Point has member functions for basic vector arithmetic, and struct Segment represents a KiCAD trace segment, and not a mathematical segment (i.e., it has data-members layer, width, and net):

struct Segment
{
    Point p1;
    Point p2;
    string width;
    string layer;
    int net;

// ···

struct Arc
{
    Point p1;
    Point pm;
    Point p2;
    string width;
    string layer;
    int net;

// ···

vector<Segment> Arc::as_polygon() const
{
        // Compute angle from inner product m->C dot m->s1
    const Point v1 = (p1+p2)/2 - pm;
    const Point v2 = p1 - pm;

    const double cos_ang = (v1*v2) / (v1.mag() * v2.mag());
    const double radius = (v2.mag() / 2) / cos_ang;

    const Point c = pm + (v1 / v1.mag())*radius;

        // Compute extra points (normalizing to the correct radius)
    vector<Point> points({p1, pm, p2});

    for (int k = 0; k < 3; ++k) // 3 passes; each pass splits all segments
    {
        for (vector<Point>::iterator p = points.begin(); p != points.end(); ++p)
        {
            if ((p+1) != points.end())
            {
                const Point & p1 = *p++;
                const Point & p2 = *p;
                const Point dir = ((p1+p2)/2) - c;
                p = points.insert (p, c + (dir / dir.mag())*radius);
            }
        }
    }

    vector<Segment> polygon;
    for (vector<Point>::const_iterator p = points.begin(); p != points.end(); ++p)
    {
        if ((p+1) != points.end())
        {
            polygon.push_back (Segment(*p,*(p+1), width, layer, net));
        }
    }

    return polygon;
}

I tried that, but it has mostly given me headaches. I called the added layers Aux1.User and Aux2.User, labelled as signal layers (my board is four layers, so these added layers were layers 3 and 4); managed to get it to work as far as my program goes. However, KiCAD insists on renaming those layers to In3.Cu and In4.Cu (when I save the file from KiCAD); I thought it was because they had to be named something dot Cu, so I changed to Aux1.Cu and Aux2.Cu; KiCAD still insists on renaming them.

This is what my program generates:

  (layers
    (0 "F.Cu" signal)
    (1 "In1.Cu" signal)
    (2 "In2.Cu" signal)
    (3 "Aux1.Cu" signal)
    (4 "Aux2.Cu" signal)
    (31 "B.Cu" signal)

When I open the file in KiCAD, I see the layers, properly named, and I see the arcs on the Aux1.Cu layer. As soon as I save the file in KiCAD, the file changes to:

  (layers
    (0 "F.Cu" signal)
    (1 "In1.Cu" signal)
    (2 "In2.Cu" signal)
    (3 "In3.Cu" signal "Aux1.Cu")
    (4 "In4.Cu" signal "Aux2.Cu")
    (31 "B.Cu" signal)

Which doesn’t even make sense to me. I have many other boards where I name the inner layers, things like GND.Cu, signal-1.Cu, VCC1.Cu, things like that; they show on the file exactly as (1 "signal-1.Cu" signal), etc. Not sure why this is happening here.

I noticed that the layer name (both in the definition and its usage) seems to have quotes in v6, and no quotes in v5; however, the boards that I had originally created in v5, even though I edit them in v6, the layers come up without quotes (perhaps if I change something, or add new traces, anything that needs to be re-written, maybe then it will come up with quotes?

Anyway, I thought I’d mention this, in case this could be related to a bug that in turn is related to the odd behaviour I’m seeing (if it is something that I am doing wrong, please do let me know)

Thanks!

Another way to do it:

In v5.x there was no Track Curve so, one way to do it was draw Arc’s, Array them, change to Cu.

Now, in v6, there’s a Track Curve tool. But, for your purpose (given your issue doing it), you can use the process of Circular Array. Getting to the understanding of what values to use takes a few minutes but, once understood, it’s simple.

Below Snippet of screenshot of my old post demo of it (self-explanitory. Also, screenshot of the v6.x Array Tool in Pop-up menu… (note: old screenshot did it for Cu but, works on any layer…

The layers always keep their internal implementation names. They are used in the file format to tell which layer each item belongs to. The custom names are for the UI and other things visible to the end user. Did you verify what you did at all with KiCad UI?

I’m not sure why you even want to change the layer names. The extra layers are there for storing temporary data and shouldn’t matter for the design. (Some users have added inner layers just to get rid of GND ratsnest in older KiCad versions, and deleted the layers afterwards.)

Quotes don’t matter, all strings are quoted in v6 for consistency, even though unquoted may be understood and work, too.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.