Blender-like grid

Need help, or want to share a macro? Post here!
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
User avatar
Chris_G
Veteran
Posts: 2579
Joined: Tue Dec 31, 2013 4:10 pm
Location: France
Contact:

Blender-like grid

Post by Chris_G »

Hi,
For those who are missing the Blender grid, here is something pretty similar :
Screenshot_20170506_181138.jpg
Screenshot_20170506_181138.jpg (61.78 KiB) Viewed 3805 times
In order to keep the 3D view clear, and not messed up by the 3 grids ( XY, XZ and YZ ), the grid object watches the camera orientation and modifies the grids transparency accordingly.
Well ... not easy to explain, but worth a try.
Chris

Code: Select all

import FreeCAD
import FreeCADGui
import math
from pivy import coin

class gridNode(coin.SoSeparator):
    def __init__(self):
        super(gridNode, self).__init__()

        self.material1 = coin.SoMaterial()
        self.material2 = coin.SoMaterial()
        self.material3 = coin.SoMaterial()
        self.coord = coin.SoCoordinate3()
        self.line1 = coin.SoIndexedLineSet()
        self.line2 = coin.SoIndexedLineSet()
        self.lineSet = coin.SoIndexedLineSet()

        self.addChild(self.coord)
        self.addChild(self.material1)
        self.addChild(self.line1)
        self.addChild(self.material2)
        self.addChild(self.line2)
        self.addChild(self.material3)
        self.addChild(self.lineSet)
        
        self._vector1 = coin.SbVec3f(1,0,0)
        self._vector2 = coin.SbVec3f(0,1,0)
        self.normal = self._vector1.cross(self._vector2)

        self._mainDim = 100
        self._subDim = 10
        
        self._numGridLines = 4
        self.material1.diffuseColor = coin.SbColor(1,0,0)
        self.material2.diffuseColor = coin.SbColor(0,1,0)
        self.material3.diffuseColor = coin.SbColor(0.5,0.5,0.5)
        self.material3.transparency = 0.5

    @property
    def transparency(self):
        return self.material3.transparency.getValues()[0]

    @transparency.setter
    def transparency(self, tr):
        self.material3.transparency = tr
        self.material2.transparency = tr
        self.material3.transparency = tr

    @property
    def vector1color(self):
        return self.material1.diffuseColor.getValues()[0].getValue()

    @vector1color.setter
    def vector1color(self, color):
        self.material1.diffuseColor = (color[0], color[1], color[2])

    @property
    def vector2color(self):
        return self.material2.diffuseColor.getValues()[0].getValue()

    @vector2color.setter
    def vector2color(self, color):
        self.material2.diffuseColor = (color[0], color[1], color[2])

    @property
    def gridcolor(self):
        return self.material3.diffuseColor.getValues()[0].getValue()

    @gridcolor.setter
    def gridcolor(self, color):
        self.material3.diffuseColor = (color[0], color[1], color[2])

    @property
    def vector1dir(self):
        return self._vector1.getValue()

    @vector1dir.setter
    def vector1dir(self, vec):
        self._vector1 = coin.SbVec3f(vec)
        self.normal = self._vector1.cross(self._vector2)
        self.buildGrid()

    @property
    def vector2dir(self):
        return self._vector2.getValue()

    @vector2dir.setter
    def vector2dir(self, vec):
        self._vector2 = coin.SbVec3f(vec)
        self.normal = self._vector1.cross(self._vector2)
        self.buildGrid()

    @property
    def mainDim(self):
        return self._mainDim

    @mainDim.setter
    def mainDim(self, n):
        self._mainDim = n
        self.buildGrid()
        
    @property
    def subDim(self):
        return self._subDim

    @subDim.setter
    def subDim(self, n):
        self._subDim = n
        self.buildGrid()

    def buildGrid(self):
        r =  range( self._subDim,  self._mainDim,  self._subDim) + [self._mainDim]
        nr = range(-self._subDim, -self._mainDim, -self._subDim) + [-self._mainDim]
        nr.reverse()
        fullRange = nr + r
        pts = []
        pts.append(-self._mainDim * self._vector1)
        pts.append( self._mainDim * self._vector1)
        pts.append(-self._mainDim * self._vector2)
        pts.append( self._mainDim * self._vector2)
        for i in fullRange:
            pts.append(i * self._vector2 - self._mainDim * self._vector1)
            pts.append(i * self._vector2 + self._mainDim * self._vector1)
            pts.append(i * self._vector1 - self._mainDim * self._vector2)
            pts.append(i * self._vector1 + self._mainDim * self._vector2)
        self.coord.point.setValues(0,len(pts),pts)
        self._numGridLines = len(fullRange) * 2
        #self.gridcolor = self.gridcolor
        #self.transparency = self.transparency
        a = []
        l = len(pts)-4
        for i in range(l/2):
            a.append(2*i + 4)
            a.append(2*i + 5)
            a.append(-1)
        self.line1.coordIndex.setValue(0)
        self.line1.coordIndex.setValues(0, 3, [0,1,-1])
        self.line2.coordIndex.setValue(0)
        self.line2.coordIndex.setValues(0, 3, [2,3,-1])
        self.lineSet.coordIndex.setValue(0)
        self.lineSet.coordIndex.setValues(0, len(a), a)

class sensorGridNode(gridNode):
    def __init__(self):
        super(sensorGridNode, self).__init__()
        self.factor = 1.0

    def linkTo(self, cam):
        self.sensor = coin.SoFieldSensor(self.updateCB, cam)
        self.sensor.setPriority(0)
        self.sensor.attach(cam.orientation)
        
    def unlink(self):
        self.sensor.detach()

    def updateCB(self, *args):
        ori = self.sensor.getTriggerField().getValue()
        lookat = coin.SbVec3f(0, 0, -1)
        viewdir = ori.multVec(lookat)  #ori.getAxisAngle()[0]
        viewdir.normalize()
        self.normal.normalize()
        val = viewdir.dot(self.normal)
        self.transparency = 1 - math.pow(abs(val),self.factor)

class gridObject:
    def __init__(self, obj):
        obj.Proxy = self

class gridVP:
    def __init__(self, obj ):
        obj.addProperty("App::PropertyDistance",  "Total",         "Size",   "Size of a grid quadrant").Total = '100mm'
        obj.addProperty("App::PropertyDistance",  "Subdivision",   "Size",   "Size of subdivisions").Subdivision = '10mm'
        obj.addProperty("App::PropertyFloat",     "XYAttenuation", "View",   "XY plane attenuation").XYAttenuation = 1.0
        obj.addProperty("App::PropertyFloat",     "XZAttenuation", "View",   "XZ plane attenuation").XZAttenuation = 20.0
        obj.addProperty("App::PropertyFloat",     "YZAttenuation", "View",   "YZ plane attenuation").YZAttenuation = 20.0
        obj.Proxy = self

    def attach(self, obj):
        self.xy = sensorGridNode()
        self.xy.vector1dir = (1,0,0)
        self.xy.vector1color = (1,0,0)
        self.xy.vector2dir = (0,1,0)
        self.xy.vector2color = (0,1,0)
        self.xy.mainDim = 100
        self.xy.subDim = 10
    
        self.xz = sensorGridNode()
        self.xz.vector1dir = (1,0,0)
        self.xz.vector1color = (1,0,0)
        self.xz.vector2dir = (0,0,1)
        self.xz.vector2color = (0,0,1)
        self.xz.mainDim = 100
        self.xz.subDim = 10
    
        self.yz = sensorGridNode()
        self.yz.vector1dir = (0,1,0)
        self.yz.vector1color = (0,1,0)
        self.yz.vector2dir = (0,0,1)
        self.yz.vector2color = (0,0,1)
        self.yz.mainDim = 100
        self.yz.subDim = 10
    
        self.sg = FreeCADGui.ActiveDocument.ActiveView.getSceneGraph()
        self.cam = FreeCADGui.ActiveDocument.ActiveView.getCameraNode()
    
        self.xy.linkTo(self.cam)
        self.xy.factor = 2
        self.xz.linkTo(self.cam)
        self.xz.factor = 5
        self.yz.linkTo(self.cam)
        self.yz.factor = 5

        self.grid = coin.SoSeparator()

        self.grid.addChild(self.xy)
        self.grid.addChild(self.xz)
        self.grid.addChild(self.yz)
        self.sg.addChild(self.grid)

    def onChanged(self, vp, prop):
        if prop == 'Total':
            if int(vp.Total) >= int(vp.Subdivision):
                self.xy.mainDim = int(vp.Total)
                self.xz.mainDim = int(vp.Total)
                self.yz.mainDim = int(vp.Total)
            else:
                vp.Total = vp.Subdivision
        if prop == 'Subdivision':
            if int(vp.Total) >= int(vp.Subdivision):
                self.xy.subDim = int(vp.Subdivision)
                self.xz.subDim = int(vp.Subdivision)
                self.yz.subDim = int(vp.Subdivision)
            else:
                vp.Subdivision = vp.Total
        if prop == 'XYAttenuation':
            if vp.XYAttenuation < 0.1:
                vp.XYAttenuation = 0.1
            elif vp.XYAttenuation > 100:
                vp.XYAttenuation = 100
            self.xy.factor = vp.XYAttenuation
        if prop == 'XZAttenuation':
            if vp.XZAttenuation < 0.1:
                vp.XZAttenuation = 0.1
            elif vp.XZAttenuation > 100:
                vp.XZAttenuation = 100
            self.xz.factor = vp.XZAttenuation
        if prop == 'YZAttenuation':
            if vp.YZAttenuation < 0.1:
                vp.YZAttenuation = 0.1
            elif vp.YZAttenuation > 100:
                vp.YZAttenuation = 100
            self.yz.factor = vp.YZAttenuation

def main():

    obj=FreeCAD.ActiveDocument.addObject("App::FeaturePython","Grid")
    gridObject(obj)
    gridVP(obj.ViewObject)


if __name__ == '__main__':
    main()
triplus
Veteran
Posts: 9471
Joined: Mon Dec 12, 2011 4:45 pm

Re: Blender-like grid

Post by triplus »

Nice!
User avatar
pablogil
Posts: 881
Joined: Wed Nov 26, 2014 3:19 pm
Location: Badajoz (Spain)
Contact:

Re: Blender-like grid

Post by pablogil »

Really great idea! I do love Blender grid...

Anyway, I have tried on macOS and it crashes when I move the 3D view camera, that is, it shows the grid when I run the macro but when I move the viewport FreeCAD crashes...
Any idea?

Also, as a suggestion, I think it could be enough if it works as Blender grid does, you configure it and it doesn't change depending of your camera position... I usually like just to have the XY grid and a thin line representing Z axis

OS: Mac OS X
Word size of OS: 64-bit
Word size of FreeCAD: 64-bit
Version: 0.16.6703 (Git)
Build type: Release
Branch: (detached from 2ce5c8d)
Hash: 2ce5c8d2e3020d05005ed71f710e09e9aa561f40
Python version: 2.7.11
Qt version: 4.8.7
Coin version: 3.1.3
OCC version: 6.8.0.oce-0.17
Dark and Light stylesheets v2.0 to theme your FreeCAD UI, more information here
User avatar
Chris_G
Veteran
Posts: 2579
Joined: Tue Dec 31, 2013 4:10 pm
Location: France
Contact:

Re: Blender-like grid

Post by Chris_G »

Hi,
Thanks for the test.
I just realized this morning, while testing on my work PC, that the code crashes with standard pivy.
It needs looo's pivy branch (https://forum.freecadweb.org/viewtopic.php?f=22&t=21994)
Such a shame pivy isn't maintained ... these coin.Sensors look very useful.
Also, as a suggestion, I think it could be enough if it works as Blender grid does, you configure it and it doesn't change depending of your camera position... I usually like just to have the XY grid and a thin line representing Z axis
I see what you mean.
On the other hand, in Blender, if you switch to side-view, with orthographic camera, you have another grid ( YZ ).
This is why I used the attenuation settings : it can make a grid almost always visible with <1 value, or make it only appear on almost normal view with high values (around 50 - 100)
chrisb
Veteran
Posts: 53924
Joined: Tue Mar 17, 2015 9:14 am

Re: Blender-like grid

Post by chrisb »

pablogil wrote: Version: 0.16.6703 (Git)
I remember having problems with that version as well. Please update to current 0.16, it might help.
A Sketcher Lecture with in-depth information is available in English, auf Deutsch, en français, en español.
User avatar
looo
Veteran
Posts: 3941
Joined: Mon Nov 11, 2013 5:29 pm

Re: Blender-like grid

Post by looo »

looks very good.
nice to see the possebilities pivy/coin give.

ot: regarding maintenance of pivy: With python3 there is the need to redistribute pivy. So also fixes regarding sensors should be available. I have no contacts to people currently distributing the pivy package. Maybe someone jumps in and create a package for the freecad-ppa... I will create a new topic for this.
User avatar
Chris_G
Veteran
Posts: 2579
Joined: Tue Dec 31, 2013 4:10 pm
Location: France
Contact:

Re: Blender-like grid

Post by Chris_G »

chrisb wrote:I remember having problems with that version as well. Please update to current 0.16, it might help.
Unfortunately, I am afraid it won't. Definitely a pivy bug.
Here is a little video to show the behaviour (XY attenuation is set to 1; XZ and YZ are set to 50 ):
phpBB [video]
User avatar
microelly2
Veteran
Posts: 4688
Joined: Tue Nov 12, 2013 4:06 pm
Contact:

Re: Blender-like grid

Post by microelly2 »

What is the difference between your grid and the Draft.Trackers.gridTracker?
User avatar
Chris_G
Veteran
Posts: 2579
Joined: Tue Dec 31, 2013 4:10 pm
Location: France
Contact:

Re: Blender-like grid

Post by Chris_G »

microelly2 wrote:What is the difference between your grid and the Draft.Trackers.gridTracker?
Look at the video. The XZ and YZ grids appear when the view gets aligned with their normal vector.
I'm not sure it is much useful, but that looks really cool :lol:
User avatar
pablogil
Posts: 881
Joined: Wed Nov 26, 2014 3:19 pm
Location: Badajoz (Spain)
Contact:

Re: Blender-like grid

Post by pablogil »

Nice video, I understand now and I feel it's even better than I firstly thought.

I have updated to v0.16.6706 (Git) and it still crashes. I have also tried with last v0.17 snapshot and it continues to crash.
As you said it could be something related with Pivy... so, is there any possibility to use it in any official build? maybe a simpler version?

Thanks!
Dark and Light stylesheets v2.0 to theme your FreeCAD UI, more information here
Post Reply