Tips on improving techdraw performance

Post here for help on using FreeCAD's graphical user interface (GUI).
Forum rules
and Helpful information
IMPORTANT: Please click here and read this first, before asking for help

Also, be nice to others! Read the FreeCAD code of conduct!
freedman
Posts: 789
Joined: Thu Mar 22, 2018 3:02 am
Location: Washington State, USA

Re: Tips on improving techdraw performance

Postby freedman » Thu Aug 15, 2019 10:05 pm

Not trying to buzz kill, Isn't the hard part of this getting dimensions, end points, arcs, ect. And could this be a DXF instead of a SVG?

Thanks.
User avatar
yorik
Site Admin
Posts: 11398
Joined: Tue Feb 17, 2009 9:16 pm
Location: São Paulo, Brazil
Contact:

Re: Tips on improving techdraw performance

Postby yorik » Thu Aug 15, 2019 10:12 pm

One issue at a time please :D
freedman
Posts: 789
Joined: Thu Mar 22, 2018 3:02 am
Location: Washington State, USA

Re: Tips on improving techdraw performance

Postby freedman » Thu Aug 15, 2019 10:40 pm

Sorry, I was actually trying to make less work. I thought the DXF format was created to rid the issues of trying to convert dots into lines. The svg format is not very easy to use when it comes to creating dimensions. Please proceed.. :)
User avatar
yorik
Site Admin
Posts: 11398
Joined: Tue Feb 17, 2009 9:16 pm
Location: São Paulo, Brazil
Contact:

Re: Tips on improving techdraw performance

Postby yorik » Thu Aug 15, 2019 11:27 pm

Ok I found a way to scale!

New version of the script, it should now work with any FreeCAD model. Just open a model, set the camera to your liking, and fire the script! (it can be saved as a macro too)

Code: Select all

import FreeCAD,FreeCADGui
from pivy import coin
from PySide import QtCore,QtGui
import os,re,math,tempfile
import TechDraw

# a name to save a temp file
svgfile = tempfile.mkstemp(suffix=".svg")[1]

# set object lighting to single face to get black fills
# but this creates artifacts in svg output, triangulation gets visible...
#ldict = {}
#for o in FreeCAD.ActiveDocument.Objects:
#    if hasattr(o,"ViewObject") and hasattr(o.ViewObject,"Lighting"):
#        ldict[o.Name] = o.ViewObject.Lighting
#        o.ViewObject.Lighting = "One side"

# get nodes to render
sg = FreeCADGui.ActiveDocument.ActiveView.getSceneGraph()
ac = FreeCADGui.ActiveDocument.ActiveView.getCamera()
rn = coin.SoSeparator()
for n in sg.getChildren():
    if isinstance(n,coin.SoSeparator):
        ncopy = n.copy()
        rn.addChild(ncopy)

# reset lighting of objects
#for o in FreeCAD.ActiveDocument.Objects:
#    if o.Name in ldict:
#        o.ViewObject.Lighting = ldict[o.Name]

# create viewer
v = FreeCADGui.createViewer()
if hasattr(v,"setName"):
    v.setName("RenderViewer")

vv = v.getViewer()
vv.setBackgroundColor(1,1,1)
v.redraw()

# set clip plane
#clip = coin.SoClipPlane()
#clip.on = True
#plane = coin.SbPlane(coin.SbVec3f(1,0,0),5000) #dir, position on dir
#clip.plane.setValue(plane)
#rn.insertChild(clip,0)

# set scenegraph
vv.setSceneGraph(rn)

# set camera
#c = '#Inventor V2.1 ascii\n\n\nOrthographicCamera {\n  viewportMapping ADJUST_CAMERA\n  position -36733.652 -7668.0435 256.88913\n  orientation -0.57735038 0.57735038 0.57735038  4.1887903\n  nearDistance 5128.5186\n  farDistance 76777.062\n  aspectRatio 1\n  focalDistance 40917\n  height 11075.028\n\n}\n'
v.setCamera(ac)

# get the viewer's mdi subwindow
mdi = None
mw = FreeCADGui.getMainWindow()
for sw in mw.findChildren(QtGui.QMdiSubWindow):
    if sw.windowTitle() == "RenderViewer":
        mdi = sw
        break

# save view
v.saveVectorGraphic(svgfile,1) # number is pixel size

# fix linewidths - the 1px default one is a bit too big...
f = open(svgfile,"r")
svg = f.read()
f.close()
svg = svg.replace("stroke-width:1.0;","stroke-width:0.2;")
svg = svg.replace("stroke-width=\"1px","stroke-width=\"0.2px")

# get 3D view dimensions, and calculate scale factor
factor = 1
if mdi:
    ww = mdi.width()
    wh = mdi.height()
    p1 = v.getPoint(0,0)
    p2 = v.getPoint(ww,wh)
    diag = (p2.sub(p1)).Length
    vpdim = re.findall("width=\"(.*?)\" height=\"(.*?)\"",svg)
    if vpdim:
        vpw = float(vpdim[0][0])
        vph = float(vpdim[0][1])
        vpdiag = math.hypot(vpw,vph)
        factor = diag/vpdiag
        #print("mdi:",ww,wh,"vp:",vpw,vph,"factor:",factor)

# remove background rectangle
svg = re.sub("<path.*?>","",svg,count=1,flags=re.MULTILINE|re.DOTALL)

# embed everything in a scale group and scale the viewport
if factor != 1:
    svg = svg.replace("<g>","<g transform=\"scale("+str(factor)+","+str(factor)+")\">\n<g>",1)
    svg = svg.replace("</svg>","</g>\n</svg>")
    svg = re.sub("width=\""+vpdim[0][0]+"\"","width=\""+str(vpw*factor)+"\"",svg,count=1,flags=re.MULTILINE|re.DOTALL)
    svg = re.sub("height=\""+vpdim[0][1]+"\"","height=\""+str(vph*factor)+"\"",svg,count=1,flags=re.MULTILINE|re.DOTALL)

# save result
#f = open(svgfile,"w")
#f.write(svg)
#f.close()

# close viewer
if mdi:
    mdi.close()

# find or create TD page
pages = FreeCAD.ActiveDocument.findObjects("TechDraw::DrawPage")
if pages:
    page = pages[0]
else:
    page = FreeCAD.ActiveDocument.addObject('TechDraw::DrawPage','Page')
    template = FreeCAD.ActiveDocument.addObject('TechDraw::DrawSVGTemplate','Template')
    template.Template = os.path.join(FreeCAD.getResourceDir(),'Mod/TechDraw/Templates/A4_LandscapeTD.svg')
    page.Template = template

view = FreeCAD.ActiveDocument.addObject('TechDraw::DrawViewSymbol','RenderedView')
view.Symbol = svg

# calculate a rough scale factor...
view.Scale = 1000.0/(vpw*factor)

page.addView(view)
FreeCAD.ActiveDocument.recompute()
It now scales things correctly (there might be imprecision due to the SVG viewport size being an int, needs further testing), and supports lines and texts and dimensions too, although the text get exploded/rendered. This will need to get bettered at some point. But it's becoming quite powerful..
Screenshot from 2019-08-15 20-19-01.png
Screenshot from 2019-08-15 20-19-01.png (146.44 KiB) Viewed 145 times
User avatar
Kunda1
Posts: 4812
Joined: Thu Jan 05, 2017 9:03 pm

Re: Tips on improving techdraw performance

Postby Kunda1 » Fri Aug 16, 2019 12:39 am

This is a whole new level of kickbutt-ness! :clap::clap::clap:
Want to contribute back to FC? Checkout:
#lowhangingfruit | Use the Source, Luke. | How to Help FreeCAD | How to report FC bugs and features
vocx
Posts: 736
Joined: Thu Oct 18, 2018 9:18 pm

Re: Tips on improving techdraw performance

Postby vocx » Fri Aug 16, 2019 2:46 am

freedman wrote:
Thu Aug 15, 2019 10:40 pm
Sorry, I was actually trying to make less work. I thought the DXF format was created to rid the issues of trying to convert dots into lines. The svg format is not very easy to use when it comes to creating dimensions. Please proceed.. :)
The way it's done by many in FreeCAD, is that you add the dimensions using Draft Dimension on the 3D model itself. Then, in TechDraw you just show those dimensions. You don't add more dimensions there.
freedman
Posts: 789
Joined: Thu Mar 22, 2018 3:02 am
Location: Washington State, USA

Re: Tips on improving techdraw performance

Postby freedman » Fri Aug 16, 2019 3:17 am

vocx » Fri Aug 16, 2019 2:46 am

The way it's done by many in FreeCAD, is that you add the dimensions using Draft Dimension on the 3D model itself. Then, in TechDraw you just show those dimensions. You don't add more dimensions there.
Thanks vocx, I was looking at those dims and I couldn't figure out how they were placed.

Yorik, when I ran the code as a macro I had to comment out.

Code: Select all

line 39, in <module>  v.setBackgroundColor(1,1,1) <type 'exceptions.AttributeError'>: setBackgroundColor
wandererfan
Posts: 2794
Joined: Tue Nov 06, 2012 5:42 pm

Re: Tips on improving techdraw performance

Postby wandererfan » Fri Aug 16, 2019 1:28 pm

yorik wrote:
Thu Aug 15, 2019 3:31 pm
So I think before passing the SVG to the ViewSymbol, we'll need to scale it properly, but I suppose it shouldn't be hard to either convert the C++ code you're referring to, or make a python binding for it. Only, I couldn't locate that code, could you give me some direction?
If I remember rightly, this code determines the real world size of the viewport in mm, then computes a scale that corrects the generated image to be the right size in TD. It is working on bit maps, but the principle should be the same for Svg.

Code: Select all

        Gui::View3DInventor* view3d = qobject_cast<Gui::View3DInventor*>(mdi3d);
        if (view3d != nullptr) {
            Base::Console().Message("G3d::grab3dView - have a view3d\n");
            Gui::View3DInventorViewer* viewer = view3d->getViewer();
            if (viewer == nullptr) {
                Base::Console().Message("G3d::grab3dView - no V3DIV\n");
                return result;
            }
            
            SbVec2s vpsize = viewer->getSoRenderManager()->getViewportRegion().getViewportSizePixels();
            vpWpx= vpsize[0];  //viewport size in pixels
            vpHpx = vpsize[1];
            //save picture as vpWpxx x vpHpx rectangle.  ie same as screen 
            viewer->savePicture(vpWpx, vpHpx, 8, bg, img);     //what is magic number 8? a page size?

            //determine scale of 3dView bitmap
            SoCamera * const camera = viewer->getSoRenderManager()->getCamera();
            if (!camera) {
                Base::Console().Message("G3d::grab3dView - no camera\n");
                return result;
            }
            double heightmm = 0.0;
            double angle = 0.0;
            double focalDistance = camera->focalDistance.getValue();
            if (camera->getTypeId() == SoOrthographicCamera::getClassTypeId()) {
                Base::Console().Message("G3d::grab3dView - camera is ortho\n");
                SoOrthographicCamera* oCam = dynamic_cast<SoOrthographicCamera*>(camera);
                heightmm = oCam->height.getValue();
                angle = M_PI / 4.0;              //45*
            } else if (camera->getTypeId() == SoPerspectiveCamera::getClassTypeId()) { 
                Base::Console().Message("G3d::grab3dView - camera is persp\n");
                SoPerspectiveCamera* pCam = dynamic_cast<SoPerspectiveCamera*>(camera);
                angle = pCam->heightAngle.getValue();
                heightmm = tan(angle / 2.0) * focalDistance * 2.0;
            } else {
                msg = QT_TR_NOOP("Grabber::grab3dView - camera is Unknown");
                msg += "\n";
                Base::Console().Warning(msg.c_str());
                return result;
            }
            double vpmmppx = heightmm/vpHpx;  //height in mm / viewport height in px  = mm/px
            double preScale = vpmmppx/tdmmppx;  //this is pretty good as a prescaler to size for TD
            feat->PreScale.setValue(preScale);
wandererfan
Posts: 2794
Joined: Tue Nov 06, 2012 5:42 pm

Re: Tips on improving techdraw performance

Postby wandererfan » Fri Aug 16, 2019 1:33 pm

yorik wrote:
Thu Aug 15, 2019 11:27 pm
Ok I found a way to scale!
I'm late to the party again. :(
User avatar
yorik
Site Admin
Posts: 11398
Joined: Tue Feb 17, 2009 9:16 pm
Location: São Paulo, Brazil
Contact:

Re: Tips on improving techdraw performance

Postby yorik » Fri Aug 16, 2019 1:43 pm

wandererfan wrote:
Fri Aug 16, 2019 1:28 pm
If I remember rightly, this code determines the real world size of the viewport in mm, then computes a scale that corrects the generated image to be the right size in TD. It is working on bit maps, but the principle should be the same for Svg.
Great thanks!! Easy to convert to python. One thing, though, this code has the same limitation as the one I'm using now: the view size is obtained in pixels, which could give quite a lot of imprecision (enough to change the value of a dimension, for ex). But I suppose we'll have to live with it for now, there might be further checks we can apply later on to refine the scale factor.
freedman wrote:
Fri Aug 16, 2019 3:17 am
Yorik, when I ran the code as a macro I had to comment out.
You are running 0.18 probably... This was added later I think.

One issue with the code above, is that texts are decomposed into many triangles (on the image in my last post, turning the dimension on doubles the size of the svg file and makes TD significantly slower). However, adding texts in SVG is simple and fast. Maybe we need some extra processing there, remove texts from the coin graph and re-add them manually... Lots of room for improvement!