cadquery icon indicating copy to clipboard operation
cadquery copied to clipboard

SVG export with better approximation using arcs

Open adam-urbanczyk opened this issue 1 year ago • 8 comments

OCCT supports approximation with arc segments https://dev.opencascade.org/doc/dev/refman/html/class_geom2d_convert___approx_arcs_segments.html we should use it in the svg exporter

adam-urbanczyk avatar Feb 16 '24 17:02 adam-urbanczyk

Technically speaking the correct way to do this is by using Bezier curves, and not circular arcs. There is a difference between the two.

    tooth = cq.Workplane("XY")
    tooth = tooth.moveTo(sunk[0], sunk[1])
    if f:
        tooth = tooth.lineTo(pts[0][0], pts[0][1])
    tooth = tooth.spline(pts, includeCurrent=(not f))

    # This point ensures that tip of tooth stays outside of outer circle.
    tip = _pol2cart(c+chop, 0)
    tooth = tooth.lineTo(tip[0], tip[1])

    # Define other side of the tooth profile, involute curve.
    pts.reverse()
    for i in range(0, len(pts)): pts[i] = _mir(pts[i])  # Mirror all points.
    sunk = _mir(sunk)
    if (not f):
        pts.append(sunk)
    tooth = tooth.lineTo(pts[0][0], pts[0][1])
    tooth = tooth.spline(pts, includeCurrent=False)
    if f:
        tooth = tooth.lineTo(sunk[0], sunk[1])

    # Close the path to origin and extrude.
    tooth = tooth.close()
    tooth = tooth.extrude(until=width)

That is code copied directly from https://sourceforge.net/p/nl10/code/HEAD/tree/cq-code/gears/common/invol_spur_gear.py . But in a nutshell we have spline() used in the same way that lineTo() would be used. I think this is a really easy fix by the way.

outline

neri-engineering avatar Feb 16 '24 22:02 neri-engineering

Technically speaking the correct way to do this is by using Bezier curves, and not circular arcs. There is a difference between the two.

Arcs should be quite simple, restricting to b-splines and splitting to bezier segments will be a little bit more complicated. OCCT supports that too it seems (GeomConvert_BSplineCurveToBezierCurve). PRs are always welcome.

adam-urbanczyk avatar Feb 17 '24 08:02 adam-urbanczyk

What is a "PR"?

I have a proposal. I would like to look into the SVG export code for the next few days. I've already glanced at it; it's under 400 lines of code if I recall correctly. I see the SVG export being roughly comprised of two steps: (1) get the edges from object (including silhouette outline) and then (2) convert that to SVG somehow. Of course there must be steps in between for example the 3D --> 2D projection, and/or rotations and translations of points in 3D and/or in 2D.

I have been having serious struggles with some of the OpenCascade APIs; the one I'm on horrible terms with at the moment is Prs3d_Drawer which seems to half-work, and the stuff that DOES work is incredibly difficult to figure out, how to use it. What I was trying to do in particular was to have a firmer grasp of how to render additional lines (above and beyond what CQ-editor uses by default, in particular silhouette outline and/or hidden lines). My struggles were mostly fruitless, and resources online were scant, even though this question has been asked of the OpenCascade community many times, and replies are very few and far between. Another example of the poor implementation in some of the OpenCascade modules is in relation to their "CubeMap" skybox implementation. Or rather, non-implementation which segfaults. Again, these questions have been asked as early as ten years ago and the response has been "not yet implemented". So why continue exposing a mostly undocumented but complete API, which has even been ported to Python, if it never worked in the first place? I call this "false advertising". It's wasting my time is what it is.

Because the CadQuery SVG exporter is [obviously] rendering the silhouette outline, it must have tackled this very problem to some degree, the problem I've been struggling with that is. Furthermore, I recently gained a firm understanding of the camera transformations in OpenCascade, because I'm still in process of working on the animator module (or static image capture to disc, programmatic, if you will). Therefore I can offer some expertise in the area of SVG generation while learning from that code on how the silhouette outline is computed.

My general vision for the task at hand is to preserve the SVG generation API as much as possible, but to offer additional options, e.g. the ability to specify camera position, zoom level, projection type, etc. In other words a firmer control of SVG output. In particular I want the SVG exporter to have a method of specifying a gimbal camera position. This is right up my alley.

And so, I will likely embark on a mission of replicating the SVG exporter in my personal space, and then sharing that code when it's "mature".

One of the other things that really draws me (no pun intended) to this task is that earlier in my life I struggled with the generation of patent application outline (black+white) drawings, where I was dealing with an extremely belligerent and obnoxiously abuse patent lawyer from Texas, who did everything in his power to not disclose any technical details to me of how to create proper patent application outline drawings (even though I already had all of my models defined in CAD software, albeit not in B-rep form). In the end I didn't achieve any patents (even though I was aspiring to file multiple ones), I ended up doing lots of unrelated personal work for this lawyer (for his personal business projects where he needed a savvy computer programmer to generate labeled lip balm animations), and I ended up getting paid only half of what we agreed to for the work I did for him. If I can enable the generation of nice SVGs for this type of patent application work, and if I can disclose it to the public and/or write tutorials about it, then in essence it's my retaliation for how I got cheated by these bastards in the past, whose way of living relies on hoarding information, hoarding money, and hoarding power, then abusing those who do not share in their spoils, who do not approve of their method of achieving those spoils.

I am not yet at a point where I have figured out how to modify the CadQuery and/or CQ-editor code directly and run it, so at this stage I tend to copy work into my personal projects, add the extra import statements, and work on the code in this fashion, which is in essence a sort of sandbox.

By the way some of the SVG-related work that I have had practice with in the past was again the work I did for this abusive Texan patent lawyer. So this is a really good way to retaliate.

Thanks.

neri-engineering avatar Feb 21 '24 16:02 neri-engineering

By the way here are my ideas regarding shading in the outline drawing.

First of all, regarding background, SVG does support a transparent background. So at the very least the SVG generation should support a black outline drawing with transparent background. This is sort of the basis for patent application drawings.

Next, there was mention of filling the part with color. If the outline is drawn all the way around the part ("silhouette") then filling that with any color in SVG is easy as pie. However the problem is that it'll be a flat color, with no gradient.

When we start considering gradients of filled colors which depend on angle of surface; in other words when you're trying to render a 3D image in SVG, this is where the difficulty starts. I don't know if we want to do that, or if it's worthwhile.

These are my thoughts on the subject of shading; at the very least we should focus on getting the line drawing correct, and any sort of shading can be discussed afterwards.

Thanks.

neri-engineering avatar Feb 21 '24 16:02 neri-engineering

PR is a pull request. Projection part starts here:

https://github.com/CadQuery/cadquery/blob/153ed3f667911e909ba8b93bddd297defc7cd42f/cadquery/occ_impl/exporters/svg.py#L189

BTW what you are describing sounds like it is beyond the scope of this issue. Current SVG exporter uses line segment when doing the brep->svg conversion. It'd be nice to at least do that with arcs.

adam-urbanczyk avatar Feb 21 '24 19:02 adam-urbanczyk

SVG natively supports cubic curves. I forget where variety it is, maybe both. There are Bezier curves where the curve does not pass through the control points and then there are splines where the curve DOES pass through the control points. In SVG, there is "move to", "line to", and "curve to". It is part of their language. So all we need to do to fix this issue is "sense" when the edge returned by OCCT is a curve, and then write the SVG as a "curve to" and NOT as "line to". This is a super simple fix unless there are roadblocks.

Still I want to embark on a much improved SVG generator because this is an extremely useful tool.

neri-engineering avatar Feb 21 '24 23:02 neri-engineering

For reference just so you have an example in front of you that works for sure, so that you can study the "curve to" language. Here is a fully working SVG with curves.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>

<svg width="1594"
     height="2594"
     version="1.1"
     xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink">

  <defs>

    <!--
      lipbalm-artist-label-image-width:   1594.0
      lipbalm-artist-label-image-height:  2594.0
      lipbalm-label-start-y:                39.0
      lipbalm-label-start-x:               225.0
      lipbalm-label-end-y:                2556.0
      lipbalm-label-end-x:                1352.0
      lipbalm-safety-seal-start-y:        2033.0
      lipbalm-safety-seal-start-x:          38.0
      lipbalm-safety-seal-end-y:          2408.0
      radius1:                              36.0
      radius2:                              73.0
    -->

    <!--
      kappa = tangent(90°/4)*(4/3) = 0.55228475
      radius1*kappa = 19.88251
      radius2*kappa = 40.31678675
    -->

    <path id="cutline"

          d="M 1316.00,   39.00
             C 1335.88,   39.00
               1352.00,   55.12
               1352.00,   75.00

             L 1352.00, 2520.00
             C 1352.00, 2539.88
               1336.12, 2556.00
               1316.00, 2556.00

             L  298.00, 2556.00
             C  257.68, 2556.00
                225.00, 2523.32
                225.00, 2483.00

             L  225.00, 2408.00

             L  111.00, 2408.00
             C   70.68, 2408.00
                 38.00, 2375.32
                 38.00, 2335.00

             L   38.00, 2106.00
             C   38.00, 2065.68
                 70.68, 2033.00
                111.00, 2033.00

             L  225.00, 2033.00

             L  225.00,   75.00
             C  225.00,   55.12
                241.12,   39.00
                261.00,   39.00 Z"/>

  </defs>

  <use href="#cutline"
       xlink:href="#cutline"
       stroke="red"
       stroke-width="2"
       stroke-opacity="1.0"
       fill="none"/>

</svg>

neri-engineering avatar Feb 21 '24 23:02 neri-engineering

Here is an insight that will help whoever is trying to solve this problem. This image shows what is going on.

overlay

The black lines are generated by the graphical user interface, the red lines are generated by the "SVG generation system".

The question in particular was, are the points being returned w/ curve approximations points that actually lie on the curve, or are they some other approximation method. The answer is: the underlying OCCT stuff, when it decides not to offer a high tesselation granularity, still chooses points that are on the curve that is to be drawn.

In other words, an elliptical curve function would solve this problem perfectly.

These are just notes to myself or to anyone else peeking in.

neri-engineering avatar Mar 05 '24 17:03 neri-engineering