Menu

Why the default line witdh used to draw the revelution skeleton line is so small in OpenGL render?

Help
ollydbg
2016-05-12
2016-05-18
  • ollydbg

    ollydbg - 2016-05-12

    Here is the test code:

    import solids;
    
    settings.render=-4;
    
    currentprojection=perspective(7,6,4);
    currentlight=(1,1,2);
    size(10cm);
    
    defaultpen(2);
    
    void f0()
    {
        revolution r=cylinder(O,0.3,1,Z);
        draw(r, black);
    }
    
    void f1()
    {
        revolution r=cylinder(X,0.3,1,Z);
        draw(r,red+linewidth(defaultpen));
    }
    
    f0();
    f1();
    

    Here is the screen shot:
    image shot

    This does not happens when I use the render=0 option in non OpenGL mode.
    Any reasons?

    I have looked at the code of solids.asy:

    // Draw on picture pic the skeleton of the surface of revolution r.
    // Draw the front portion of each of the m transverse slices with pen p and
    // the back portion with pen backpen. Rotational arcs are based on
    // n-point approximations to the unit circle.
    void draw(picture pic=currentpicture, revolution r, int m=0, int n=nslice,
          pen frontpen=currentpen, pen backpen=frontpen,
          pen longitudinalpen=frontpen, pen longitudinalbackpen=backpen,
          light light=currentlight, string name="",
              render render=defaultrender, projection P=currentprojection)
    {
      if(is3D()) {
        pen thin=thin();
        void drawskeleton(frame f, transform3 t, projection P) {
          skeleton s=r.skeleton(m,n,inverse(t)*P);
          if(frontpen != nullpen) {
            draw(f,t*s.transverse.back,thin+defaultbackpen+backpen,light);
            draw(f,t*s.transverse.front,thin+frontpen,light);
          }
          if(longitudinalpen != nullpen) {
            draw(f,t*s.longitudinal.back,thin+defaultbackpen+longitudinalbackpen,
                 light);
            draw(f,t*s.longitudinal.front,thin+longitudinalpen,light);
          }
        }
    
        bool group=name != "" || render.defaultnames;
        if(group)
          begingroup3(pic,name == "" ? "skeleton" : name,render);
        pic.add(new void(frame f, transform3 t, picture pic, projection P) {
            drawskeleton(f,t,P);
            if(pic != null)
              pic.addBox(min(f,P),max(f,P),min(frontpen),max(frontpen));
          });
        frame f;
        drawskeleton(f,identity4,P);
        pic.addBox(min3(f),max3(f));
        if(group)
          endgroup3(pic);
      }
    

    What does the code snippet:

          if(frontpen != nullpen) {
            draw(f,t*s.transverse.back,thin+defaultbackpen+backpen,light);
            draw(f,t*s.transverse.front,thin+frontpen,light);
          }
    

    means?

    In my test code, the value "red+linewidth(defaultpen)" is assign to the argument "pen frontpen=currentpen", am I right? Thanks.

     

    Last edit: ollydbg 2016-05-12
  • Charles Staats

    Charles Staats - 2016-05-12

    There are two kinds of paths drawn using the OpenGL renderer:

    • The default pen is thin(), which has line width exactly one "virtual" pixel. Unfortunately when you start using render != 1, you get more than one "virtual" pixel per "actual" pixel (for antialiasing), which means your black lines fade to gray and get really hard to see.
    • For any line width other than thin(), what's actually drawn is not a path but a tube. This is not the default because it's slower to render and also can have the sort of issues you describe in your next post.
     
    • ollydbg

      ollydbg - 2016-05-12

      Thanks for the reply.

      The default pen is thin(), which has line width exactly one "virtual" pixel. Unfortunately when you start using render != 1, you get more than one "virtual" pixel per "actual" pixel (for antialiasing), which means your black lines fade to gray and get really hard to see.

      I just follow the advice, and did the following things:

      import solids;
      
      settings.render=1;
      
      currentprojection=perspective(7,6,4);
      currentlight=(1,1,2);
      size(10cm);
      
      //defaultpen(4);
      
      void f0()
      {
          revolution r=cylinder(O,0.3,1,Z);
          draw(O--Z);
          draw(r);
      }
      
      void f1()
      {
          revolution r=cylinder(X,0.3,1,Z);
          draw(r,red+linewidth(defaultpen), light=nolight);
          draw(X--Z+X, red);
      }
      
      f0();
      f1();
      

      But I still get the color in gray, not pure black, I have tried both pdf and png output. So, is there any issue in my system?

      My settings:

          import settings;
          outformat="png";
          gs = "C:\Program Files\gs\gs9.16\bin\gswin32c.exe";
          pdfviewer = "D:\software\office\SumatraPDF-3.0\SumatraPDF.exe";
          texpath = "E:\sci\texlive2015\bin\win32";
          dvips="E:\sci\texlive2015\bin\win32\dvips";
          prc=false;
          interactiveView=false;
          batchView=false;
          maxtile=(512,512);
          render=1;
      

      For any line width other than thin(), what's actually drawn is not a path but a tube. This is not the default because it's slower to render and also can have the sort of issues you describe in your next post.

      So, as you suggested, I should use the "thin()", thanks.

       
  • John Bowman

    John Bowman - 2016-05-12

    Try adding the settings:

    -antialias=1 -multisample=0

     
    • ollydbg

      ollydbg - 2016-05-12

      Hi, John, thanks, but I get the error:

      NPP_SAVE: E:\my-solution\bug2.asy
      CD: E:\my-solution
      Current directory: E:\my-solution
      C:\Program Files\Asymptote\asy.exe   bug2.asy -antialias=1 -multisample=0
      Process started >>>
      Invalid Parameter - 72x72
      C:\Program Files\Asymptote/three.asy: 2905.13: runtime: shipout failed
      <<< Process finished. (Exit code 0)
      ================ READY ================
      
       
  • John Bowman

    John Bowman - 2016-05-12

    For now try -f pdf instead of -f png

    The error suggests that the MSDOS utility convert is being used instead of
    the one that comes with ImageMagick; you can specify a path to the correct convert program
    in your config file.

     
    • ollydbg

      ollydbg - 2016-05-12

      Hi, John, thanks, by adding the line:

      convert="C:\Program Files\ImageMagick-6.7.6-Q16\convert.exe";
      

      in the config files, this did solved the issue.
      Both pdf and png files generate black lines.

       
  • ollydbg

    ollydbg - 2016-05-13

    Hi, all, even the default thin() is used, I see the image is still look so bad, see the image shot below.

    Here is the source file:

    import solids;
    
    settings.render=1;
    
    size(10cm);
    
    currentprojection=perspective(
    camera=(10.616394929213,24.8854165175403,8.43854918823241),
    up=(0,0,1),
    target=(-0.497864209736424,0.00636376301098822,0.861716722522251));
    
    transform3 joint_r1(transform3 T, real angle=0.0)
    {
        path3 circle_path = circle(T*(-0.5Z), 0.5, T*Z-T*O);
        path3 circle_path2 = circle(T*(0.5Z), 0.5, T*Z-T*O);
        draw(surface(circle_path), red, light=nolight);
        draw(surface(circle_path2), red, light=nolight);
    
        revolution r=cylinder(T*(-0.5Z),0.5,1,T*Z-T*O);
        draw(surface(r),green, light=nolight);
        draw(r,frontpen=black, backpen=nullpen, light=nolight);
    
        return T*rotate(angle, Z);
    }
    
    transform3 joint_r2(transform3 T, real angle=0.0)
    {
        transform3 T3 = joint_r1(T, angle);
        path3 p1=(0,0,0.5)--(0,0,0.6)--(0.6,0,0.6)--(0.6,0,-0.6)--(0,0,-0.6)--(0,0,-0.5);
        p1 = T3*p1;
        draw(p1);
        return T3*shift(0.6, 0, 0);;
    }
    
    transform3 Tend=identity(4);
    
    transform3 Tbase = joint_r1(Tend, 45);
    Tend = Tbase*shift(0,0,2);
    draw(Tbase*O--Tend*O);
    
    Tend = Tend*rotate(-90, Y);
    Tbase = joint_r2(Tend,70);
    triple pend = Tbase*O;
    transform3 move = shift(2,0,0);
    triple pend2 = Tbase*move*O;
    draw(pend--pend2);
    
    Tbase = joint_r2(Tbase*move, 60);
    pend = Tbase*O;
    move = shift(3,0,0);
    pend2 = Tbase*move*O;
    draw(pend--pend2);
    

    and the config file:

        import settings;
        outformat="png";
        gs="C:\Program Files\gs\gs9.16\bin\gswin32c.exe";
        convert="C:\Program Files\ImageMagick-6.7.6-Q16\convert.exe";
        pdfviewer = "D:\software\office\SumatraPDF-3.0\SumatraPDF.exe";
        texpath = "E:\sci\texlive2015\bin\win32";
        dvips="E:\sci\texlive2015\bin\win32\dvips";
        prc=false;
        interactiveView=false;
        batchView=false;
        render=1;
    

    and the command line:

    C:\Program Files\Asymptote\asy.exe   bug3.asy -antialias=1 -multisample=0
    

    So, any workaround about this issue? I'm thinking to scale the cylinder and circle a bit, but I'm not sure what is the radius of the tube line? It looks like I need to reduce the radius of the cylinder by the radius of the tube. And reduce the height of the cylinder by 2 times of the tube's radius.

     
  • John Bowman

    John Bowman - 2016-05-13

    All you need to do is use a higher value of settings.render (for example, 4). This is the number of pixels per bp. You can avoid such rendering issues by producing vector graphics (PRC) output and embedding that in a pdf file.

     
    • ollydbg

      ollydbg - 2016-05-13

      Hi, with higher value of settings.render = 8, I get such result. (see in attachment)
      You see that all the skeleton lines of the cylinder get lost.

      You can avoid such rendering issues by producing vector graphics (PRC) output and embedding that in a pdf file.

      I does not have adobe reader install on my computer. But my image will finally embeded into exam or papers, I don't think PRC can solve my issue, because they will be printed to paper material。

       

      Last edit: ollydbg 2016-05-13
  • John Bowman

    John Bowman - 2016-05-13

    Sorry I forgot that these lines were being drawn as thin lines. You can override this by specifying
    frontpen=black+0.5mm.

     
    • ollydbg

      ollydbg - 2016-05-13

      Thanks, with the method you mentioned, I experienced another issue, just see another thread I posted here: partial of the edge lines are hidden by the surface, how to solve it?, I currently don't have a good way to let the image have unified line width.

       
  • ollydbg

    ollydbg - 2016-05-13

    When the thin enabled for darwing the 3D line, I think opengl use glLineWidth(1); or similar code is used to draw the 3D line, am I right? I just looked the asymptote's source code, but I can't find how a 3D line with it's linewidth=0 is draw by OpenGL.

    Then, I would suggest if we could have an option for glLineWidth(2); or even bigger value, because this make all the 3D line have unique width shown on the screen.

    Any good ideas?

     
  • ollydbg

    ollydbg - 2016-05-13

    If that is possible, I think my feature request: Is there a simple way to draw hidden line as dotted line can also be implemented, because OpenGL natived support this kind of drawings.
    Most of the 3D modeling software use the dotted line to show some hidden geometry.

     

    Last edit: ollydbg 2016-05-13
  • ollydbg

    ollydbg - 2016-05-13

    I just read the source code, found that some related code in drawpath3.cc

    void drawPath3::render(GLUnurbs *nurb, double, const triple&, const triple&,
                           double, bool lighton, bool transparent)
    {
    #ifdef HAVE_GL
      Int n=g.length();
      if(n == 0 || invisible || ((color.A < 1.0) ^ transparent))
        return;
    
      bool havebillboard=interaction == BILLBOARD;
    
      GLfloat Diffuse[]={0.0,0.0,0.0,(GLfloat) color.A};
      glMaterialfv(GL_FRONT,GL_DIFFUSE,Diffuse);
    
      static GLfloat Black[]={0.0,0.0,0.0,1.0};
      glMaterialfv(GL_FRONT,GL_AMBIENT,Black);
    
      GLfloat Emissive[]={(GLfloat) color.R,(GLfloat) color.G,(GLfloat) color.B,
                  (GLfloat) color.A};
      glMaterialfv(GL_FRONT,GL_EMISSION,Emissive);
    
      glMaterialfv(GL_FRONT,GL_SPECULAR,Black);
    
      glMaterialf(GL_FRONT,GL_SHININESS,128.0);
    
      if(havebillboard) BB.init();
    
      if(straight) {
        glBegin(GL_LINE_STRIP);
        for(Int i=0; i <= n; ++i) {
          triple v=g.point(i);
          if(havebillboard) {
            static GLfloat controlpoints[3];
            BB.store(controlpoints,v,center);
            glVertex3fv(controlpoints);
          } else
            glVertex3f(v.getx(),v.gety(),v.getz());
        }
        glEnd();
      } else {
        for(Int i=0; i < n; ++i) {
          static GLfloat knots[8]={0.0,0.0,0.0,0.0,1.0,1.0,1.0,1.0};
          static GLfloat controlpoints[12];
          if(havebillboard) {
            BB.store(controlpoints,g.point(i),center);
            BB.store(controlpoints+3,g.postcontrol(i),center);
            BB.store(controlpoints+6,g.precontrol(i+1),center);
            BB.store(controlpoints+9,g.point(i+1),center);
          } else {
            store(controlpoints,g.point(i));
            store(controlpoints+3,g.postcontrol(i));
            store(controlpoints+6,g.precontrol(i+1));
            store(controlpoints+9,g.point(i+1));
          }
    
          gluBeginCurve(nurb);
          gluNurbsCurve(nurb,8,knots,3,controlpoints,4,GL_MAP1_VERTEX_3);
          gluEndCurve(nurb);
        }
      }
    #endif
    }
    

    I guess this is the way OpenGL draw a one pixel line. So, is it possible to add an option to adjust the glLineWidth - OpenGL 4 Reference Pages function?

     
  • ollydbg

    ollydbg - 2016-05-13

    I just disabled the drawing of surface, and when render=1, I see that the default thin gives the expected result(I get the screen shot from OpenGL viewer).
    image shot 1

    But if I draw the surface, I get such bad image(also I get the screen shot from OpenGL viewer)
    image shot 2

    So, I think a wider unique witdh line is need here.

     
  • John Bowman

    John Bowman - 2016-05-13

    OpenGL doesn't guarantee support for thick lines. From https://www.opengl.org/sdk/docs/man/html/glLineWidth.xhtml:

    Only width 1 is guaranteed to be supported; others depend on the implementation.

    So we are forced to implement thick lines as tubes.
    This is what you get when you specify
    frontpen=black+0.5mm.
    The result is correctly drawn (remembering as Charles pointed out that thick lines are really tubes
    so you can only see half of the back tube).

    In OpenGL a linewidth of 0 forces a line that is always one pixel wide, which can be hard to see.

     
  • ollydbg

    ollydbg - 2016-05-15

    Is it possible at least add an option to tweak the glLineWidth? Though it is 100% supported, but this feature is good for technical drawing rendering output.

     
  • ollydbg

    ollydbg - 2016-05-16

    I just see that one of my Ubuntu computer's OpenGL support 1 to 10 pixels witdh 3D lines. So, I think this is doable, right? Thanks.

     
  • ollydbg

    ollydbg - 2016-05-17

    I just use the FreeCAD(a multi-platform free and opensource 3D CAD software) to create a cylinder object, and see the image shot below.
    There are five cylinders, and from left to right, I can set the line width from 1 to 5. This gives quite nice sketch output. I'm using FreeCAD in a very old PC on WinXP which I brought in year 2008. So, I think most OpenGL implementation has support such nice feature.

     

    Last edit: ollydbg 2016-05-17
  • John Bowman

    John Bowman - 2016-05-17

    Our reasoning in implementing tubes rather than relying on OpenGL is that for linewidths greater
    than the hardware limit (which could be 1 on some systems; we don't know), tubes are required.
    So now that tubes are implemented why not use them always? Are you perhaps concerned about file size? That is the only difference I can think of.

     
  • ollydbg

    ollydbg - 2016-05-18

    Our reasoning in implementing tubes rather than relying on OpenGL is that for linewidths greater than the hardware limit (which could be 1 on some systems; we don't know), tubes are required. So now that tubes are implemented why not use them always?

    The reason is that the tubes are shown badly if the tube line intersect with a face. I have another post stating this issue, see: https://sourceforge.net/p/asymptote/discussion/409349/thread/1946c31a/#3998, half of the line is hidden by the surface, then the generated image doesn't have equal width lines. That's not good for book or technique drawing. Charles Staats helped me a lot in that thread, but it is still hard to implement a good expected result.

    Well, with the native opengl line, we can simply get the expected result. Another thing is that the opengl native lines don't have the projection effect(I mean they don't get smaller when they are far from the camera), so I can achieve the equal width lines similar like in our render=0 condition.

    An extra feature reqeust, which some users expect is the dotted hidden line, this can also be done in OpenGL, I have posted another thread here:Is there a simple way to draw hidden line as dotted line. This can show hidden geometry without using transparent surfaces. I think transparent surfaces are not quite good to used in a printed paper or books.

    Thanks.

    By the way: there are two of my posts in the thread Asymptote / Discussion / Help:partial of the edge lines are hidden by the surface, how to solve it?, which showns "Post awaiting moderation.", so as an administrator, can you verify them? Thanks.

     

Log in to post a comment.