Blender-like grid

Need help, or want to share a macro? Post here!
User avatar
Chris_G
Posts: 1307
Joined: Tue Dec 31, 2013 4:10 pm
Location: France
Contact:

Blender-like grid

Postby Chris_G » Sat May 06, 2017 4:22 pm

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 1361 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
Posts: 9428
Joined: Mon Dec 12, 2011 4:45 pm

Re: Blender-like grid

Postby triplus » Sat May 06, 2017 10:04 pm

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

Re: Blender-like grid

Postby pablogil » Sun May 07, 2017 11:23 am

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
Posts: 1307
Joined: Tue Dec 31, 2013 4:10 pm
Location: France
Contact:

Re: Blender-like grid

Postby Chris_G » Sun May 07, 2017 11:58 am

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
Posts: 24284
Joined: Tue Mar 17, 2015 9:14 am

Re: Blender-like grid

Postby chrisb » Sun May 07, 2017 12:18 pm

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.
User avatar
looo
Posts: 3297
Joined: Mon Nov 11, 2013 5:29 pm

Re: Blender-like grid

Postby looo » Sun May 07, 2017 12:34 pm

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
Posts: 1307
Joined: Tue Dec 31, 2013 4:10 pm
Location: France
Contact:

Re: Blender-like grid

Postby Chris_G » Sun May 07, 2017 12:37 pm

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
Posts: 4690
Joined: Tue Nov 12, 2013 4:06 pm
Contact:

Re: Blender-like grid

Postby microelly2 » Sun May 07, 2017 1:17 pm

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

Re: Blender-like grid

Postby Chris_G » Sun May 07, 2017 2:44 pm

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:
pablogil
Posts: 820
Joined: Wed Nov 26, 2014 3:19 pm
Location: Badajoz (Spain)
Contact:

Re: Blender-like grid

Postby pablogil » Sun May 07, 2017 4:07 pm

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