imported wrl: howto get a bounding box?

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!
User avatar
easyw-fc
Veteran
Posts: 3633
Joined: Thu Jul 09, 2015 9:34 am

imported wrl: howto get a bounding box?

Post by easyw-fc »

Hi,
is it possible to create and display a bounding box of a wrl model file imported in FreeCAD?

My target would be to create an 'envelope' for a wrl object, to be able to move & align the wrl object to other 3D shapes using its BBox as a reference.

Thanks in advance for any help.

Maurice
User avatar
easyw-fc
Veteran
Posts: 3633
Joined: Thu Jul 09, 2015 9:34 am

Re: imported wrl: howto get a bounding box?

Post by easyw-fc »

Hi again,
is there anyone that could give me a tip?

I found these threads that may also be relevant:
https://forum.freecadweb.org/viewtopic. ... rl#p208857
microelly2 wrote: Thu Jan 11, 2018 9:00 pm After the good results in testing the light and shadow nodes (https://forum.freecadweb.org/viewtopic. ... 20#p208588)
I'm optimistic to implement a VRML/Open Inventor GUI for non programmers.
https://forum.freecadweb.org/viewtopic. ... 626#p78106
wmayer wrote: Sat Feb 07, 2015 3:50 pm In the first place VRML is a modelling language representing a 3d scenegraph. It can contain any kind of primitive geometries like spheres, cylinders, planes or it can contain text or it can contain meshes. So, your VRML file *can* contain a mesh but it doesn't need to.
TheMarkster
Veteran
Posts: 5513
Joined: Thu Apr 05, 2018 1:53 am

Re: imported wrl: howto get a bounding box?

Post by TheMarkster »

Only thing I can think of that *might* work is to parse the ASCII representation of the IV object to acquire a list of points you could then sort through to find the MinX, MaxX, etc., and from those perhaps create a bounding box. Here is some messy bit of not optimized hackery I was playing around with to do this. At the end of it the points[] list will be a list of vectors.

Code: Select all

import FreeCAD
from FreeCAD import Base
df = App.ActiveDocument.getObject("spheres")
vo = df.ViewObject
iv = vo.IV
idx = iv.find("point [")
idx2 = iv.find("]",idx)
pts = iv[idx+len("point ["):idx2].replace('\n','').replace('  ','')
splt = pts.split(',')
pointStrings = []
points=[]
for s in splt:
    pointStrings.append(s.split(' '))
for ps in pointStrings:
    if len(ps)==4:
        if len(ps[3])>0:
            ps = ps[1:4]
        else:
            ps = ps[0:3]
    try:
        points.append(Base.Vector(float(ps[0]),float(ps[1]),float(ps[2])))
    except:
        FreeCAD.Console.PrintMessage('exception: '+str(ps)+'\n')
Next step is to go through the points[] list and save the largest and smallest x,y,z values (so you'd have 6 in all, smallest and largest of each).

In this case "spheres" is the label of an import wrl file, which was just 2 spheres side by side. If the placement has been changed, I don't know if the points[] list would contain values relative to that placement or not. You would probably need to offset your bounding box with the object's placement.
mario52
Veteran
Posts: 4692
Joined: Wed May 16, 2012 2:13 pm

Re: imported wrl: howto get a bounding box?

Post by mario52 »

hi

here by wmayer for my macro Macro_BoundingBox_Tracing

Code: Select all

# -*- coding: utf-8 -*-
##http://forum.freecadweb.org/viewtopic.php?f=13&t=22331

selEx = FreeCADGui.Selection.getSelectionEx()
objs  = [selobj.Object for selobj in selEx]

if len(objs) >= 1:
    if hasattr(objs[0],    "Shape"):
        s = objs[0].Shape
    elif hasattr(objs[0],   "Mesh"):
        s = objs[0].Mesh
    elif hasattr(objs[0], "Points"):
        s = objs[0].Points
    else:
        print "oups"
try:
    print s
except Exception:
    print "oups oups"
other see search def getTextSize(self,vobj):

Code: Select all

#https://code.alephobjects.com/file/data/die5vpa3cwqodz6poakk/PHID-FILE-v2tfm6wj7rylxg7mi2hs/file
def getTextSize(self,vobj):
         from pivy import coin
         if vobj.DisplayMode == "3D text":
             text = self.text3d
         else:
             text = self.text2d
         v = FreeCADGui.ActiveDocument.ActiveView.getViewer().getSoRenderManager().getViewportRegion()
         b = coin.SoGetBoundingBoxAction(v)
         
         return b.getBoundingBox().getSize().getValue()

i tray adapt for my macro , but ...

PS: i upgrade my macro for ver 0.17

mario
Maybe you need a special feature, go into Macros_recipes and Code_snippets, Topological_data_scripting.
My macros on Gist.github here complete macros Wiki and forum.
User avatar
easyw-fc
Veteran
Posts: 3633
Joined: Thu Jul 09, 2015 9:34 am

Re: imported wrl: howto get a bounding box?

Post by easyw-fc »

TheMarkster wrote: Sun Oct 07, 2018 7:44 pm Only thing I can think of that *might* work is to parse the ASCII representation of the IV object to acquire a list of points you could then sort through to find the MinX, MaxX, etc., and from those perhaps create a bounding box. Here is some messy bit of not optimized hackery I was playing around with to do this. At the end of it the points[] list will be a list of vectors.

Code: Select all

...
Next step is to go through the points[] list and save the largest and smallest x,y,z values (so you'd have 6 in all, smallest and largest of each).

In this case "spheres" is the label of an import wrl file, which was just 2 spheres side by side. If the placement has been changed, I don't know if the points[] list would contain values relative to that placement or not. You would probably need to offset your bounding box with the object's placement.
thanks a lot... this approach is fine! :D
here my WIP macro:

Code: Select all

import FreeCAD
from FreeCAD import Base

sel = FreeCADGui.Selection.getSelection()
if len (sel) > 0:
    df = App.ActiveDocument.getObject(sel[0].Name)
    vo = df.ViewObject
    iv = vo.IV
    idx = iv.find("point [")
    idx2 = iv.find("]",idx)
    pts = iv[idx+len("point ["):idx2].replace('\n','').replace('  ','')
    splt = pts.split(',')
    pointStrings = []
    points=[]
    for s in splt:
        pointStrings.append(s.split(' '))
    for ps in pointStrings:
        if len(ps)==4:
            if len(ps[3])>0:
                ps = ps[1:4]
            else:
                ps = ps[0:3]
        try:
            points.append(Base.Vector(float(ps[0]),float(ps[1]),float(ps[2])))
        except:
            FreeCAD.Console.PrintMessage('exception: '+str(ps)+'\n')
    #print (points)
    xmin=0;ymin=0;zmin=0;
    xmax=0;ymax=0;zmax=0;
    for p in points:
    #    print (p,p.x)
        if p.x > xmax:
            xmax=p.x
        if p.x < xmin:
            xmin=p.x
        if p.y > ymax:
            ymax=p.y
        if p.y < ymin:
            ymin=p.y
        if p.z > zmax:
            zmax=p.z
        if p.z < zmin:
            zmin=p.z
            
    print ('xmin=',xmin,' xmax=',xmax)
    print ('ymin=',ymin,' ymax=',ymax)
    print ('zmin=',zmin,' zmax=',zmax)
else:
    print('select a WRL model')
-----------------------------------------------------
Hi Mario, thanks for the tips...
mario52 wrote: Mon Oct 08, 2018 4:40 pm hi

here by wmayer for my macro Macro_BoundingBox_Tracing

Code: Select all

....
other see search def getTextSize(self,vobj):

Code: Select all

...
i tray adapt for my macro , but ...

PS: i upgrade my macro for ver 0.17

mario
but this approach doesn't seem to work with pure wrl files (no mesh inside)

here my non functional testing code:

Code: Select all

import FreeCAD
from FreeCAD import Base

def getWrlSize(vobj):
    from pivy import coin
    print (vobj.DisplayMode)
    #if vobj.DisplayMode == "3D text":
    #    text = self.text3d
    #else:
    #    text = self.text2d
    v = FreeCADGui.ActiveDocument.ActiveView.getViewer().getSoRenderManager().getViewportRegion()
    b = coin.SoGetBoundingBoxAction(v)
    
    return b.getBoundingBox().getSize().getValue()

sel = FreeCADGui.Selection.getSelection()

df = App.ActiveDocument.getObject(sel[0].Name)
vo = df.ViewObject

print (getWrlSize(vo))


I'm attaching a very simple wrl testing file coming from kicad (there is a 2.54 scale factor inside)
wrl-simple-test.FCStd
(4.22 KiB) Downloaded 83 times
TheMarkster
Veteran
Posts: 5513
Joined: Thu Apr 05, 2018 1:53 am

Re: imported wrl: howto get a bounding box?

Post by TheMarkster »

Excellent. I added these 3 lines to your code:

print ('xmin=',xmin,' xmax=',xmax)
print ('ymin=',ymin,' ymax=',ymax)
print ('zmin=',zmin,' zmax=',zmax)

Code: Select all

    Draft.makeLine(Base.Vector(xmin,ymin,zmin),Base.Vector(xmin,ymin,zmax))
    Draft.makeLine(Base.Vector(xmin,ymin,zmin),Base.Vector(xmax,ymin,zmin))
    Draft.makeLine(Base.Vector(xmin,ymin,zmin),Base.Vector(xmin,ymax,zmin))
to make a sort of half boundbox out of draft wires just for testing purposes.
dragon-bbox-scr.png
dragon-bbox-scr.png (124.58 KiB) Viewed 1764 times
This is a big file (the dragon) about 175 MB, and it takes a noticeable time to run (but not too bad, less than a minute). Should consider a progress indicator of some kind, and also optimizing. I think the finding of min/max points maybe could be done where the points[] list is being built instead of building it, might be faster.
User avatar
easyw-fc
Veteran
Posts: 3633
Joined: Thu Jul 09, 2015 9:34 am

Re: imported wrl: howto get a bounding box?

Post by easyw-fc »

TheMarkster wrote: Tue Oct 09, 2018 8:26 pm Excellent. I added these 3 lines to your code:

print ('xmin=',xmin,' xmax=',xmax)
print ('ymin=',ymin,' ymax=',ymax)
print ('zmin=',zmin,' zmax=',zmax)

Code: Select all

    Draft.makeLine(Base.Vector(xmin,ymin,zmin),Base.Vector(xmin,ymin,zmax))
    Draft.makeLine(Base.Vector(xmin,ymin,zmin),Base.Vector(xmax,ymin,zmin))
    Draft.makeLine(Base.Vector(xmin,ymin,zmin),Base.Vector(xmin,ymax,zmin))
to make a sort of half boundbox out of draft wires just for testing purposes.

...

I added a bounding box from @mario BBox Macro

Code: Select all

# -*- coding: utf-8 -*-

import FreeCAD, Draft
from FreeCAD import Base

sel = FreeCADGui.Selection.getSelection()
if len (sel) == 1:
    limit=1e10
    xmin=limit;ymin=limit;zmin=limit
    xmax=-limit;ymax=-limit;zmax=-limit
    
    df = App.ActiveDocument.getObject(sel[0].Name)
    vo = df.ViewObject
    print (vo.DisplayMode)
    if vo.DisplayMode == 'VRML':
        iv = vo.IV
        idx = iv.find("point [")
        idx2 = iv.find("]",idx)
        pts = iv[idx+len("point ["):idx2].replace('\n','').replace('  ','')
        splt = pts.split(',')
        pointStrings = []
        points=[]
        for s in splt:
            pointStrings.append(s.split(' '))
        for ps in pointStrings:
            if len(ps)==4:
                if len(ps[3])>0:
                    ps = ps[1:4]
                else:
                    ps = ps[0:3]
            try:
                px = float(ps[0]); py = float(ps[1]); pz = float(ps[2]);
                points.append(Base.Vector(px,py,pz))
                xmax = max(px,xmax);xmin = min(px,xmin)
                ymax = max(py,ymax);ymin = min(py,ymin)
                zmax = max(pz,zmax);zmin = min(pz,zmin)
            except:
                FreeCAD.Console.PrintMessage('exception: '+str(ps)+'\n')
        #print (points)        
        print ('xmin=',xmin,' xmax=',xmax)
        print ('ymin=',ymin,' ymax=',ymax)
        print ('zmin=',zmin,' zmax=',zmax)
        #Draft.makeLine(Base.Vector(xmin,ymin,zmin),Base.Vector(xmin,ymin,zmax))
        #Draft.makeLine(Base.Vector(xmin,ymin,zmin),Base.Vector(xmax,ymin,zmin))
        #Draft.makeLine(Base.Vector(xmin,ymin,zmin),Base.Vector(xmin,ymax,zmin))
        BBox = App.ActiveDocument.addObject("Part::Box",sel[0].Name + "_BBox")
        BBox.Label = sel[0].Label+'_BBox'
        BBox.Length.Value = abs(xmax-xmin)
        BBox.Width.Value  = abs(ymax-ymin)
        BBox.Height.Value = abs(zmax-zmin)
        BBox.Placement=App.Placement(App.Vector(xmin,ymin,zmin), App.Rotation(App.Vector(0,0,1),0), App.Vector(0,0,0))
        FreeCADGui.ActiveDocument.getObject(BBox.Name).LineColor  = (1., 0., 0.)
        FreeCADGui.ActiveDocument.getObject(BBox.Name).PointColor = (1., 0., 0.)
        FreeCADGui.ActiveDocument.getObject(BBox.Name).ShapeColor = (1., 0., 0.)
        FreeCADGui.ActiveDocument.getObject(BBox.Name).Transparency = 80
        App.Console.PrintMessage(BBox.Label + " Volume : " + str(BBox.Shape.Volume)+"\n")
    else:
        print('select a WRL model')
else:
    print('select a WRL model')
vrml-bbox.png
vrml-bbox.png (365.62 KiB) Viewed 1758 times
The result is very nice :D
TheMarkster wrote: Tue Oct 09, 2018 8:26 pm This is a big file (the dragon) about 175 MB, and it takes a noticeable time to run (but not too bad, less than a minute). Should consider a progress indicator of some kind, and also optimizing.
I wouldn't know how to add the progress indicator ... but it would be nice :)

BTW the macro should also handle
- scale
- translation
- rotation
if present in the VRML (but this would be more difficult to achieve (because I don't think they are available at python level).

Thanks all for the very nice suggestions.
Maurice
TheMarkster
Veteran
Posts: 5513
Joined: Thu Apr 05, 2018 1:53 am

Re: imported wrl: howto get a bounding box?

Post by TheMarkster »

The progress indicator could be as simple as some text in the report view.

Code: Select all

import FreeCAD
from PySide import QtGui
from time import sleep

for ii in range (0,10):
    print("step "+str(ii)+" of 10 \n")
    QtGui.QApplication.processEvents()
    sleep(.001) #1 millisecond
    #do lengthy processing here
If you want to see what the IV ASCII text representation looks like:

Code: Select all

print(iv)
There is a transform section, which could be parsed:

Code: Select all

  Transform {
    translation 0 0 0
    rotation 0 0 1  0
    scaleFactor 1 1 1
    center 0 0 0

  }
User avatar
easyw-fc
Veteran
Posts: 3633
Joined: Thu Jul 09, 2015 9:34 am

Re: imported wrl: howto get a bounding box?

Post by easyw-fc »

TheMarkster wrote: Tue Oct 09, 2018 11:14 pm If you want to see what the IV ASCII text representation looks like:

Code: Select all

print(iv)
There is a transform section, which could be parsed:

Code: Select all

  Transform {
    translation 0 0 0
    rotation 0 0 1  0
    scaleFactor 1 1 1
    center 0 0 0

  }
Thanks,
some little improvements:
(Rotation doesn't seem to work correctly ATM :( )
Attached a test file (Dinosaur.wrl is fine, Dragon.wrl is not)
test-wrl-bboxes.FCStd
(12.24 KiB) Downloaded 22 times

Code: Select all

# -*- coding: utf-8 -*-

import FreeCAD, Draft
from FreeCAD import Base
import math

sel = FreeCADGui.Selection.getSelection()
if len (sel) == 1:
    limit=1e10
    xmin=limit;ymin=limit;zmin=limit
    xmax=-limit;ymax=-limit;zmax=-limit
    
    df = App.ActiveDocument.getObject(sel[0].Name)
    vo = df.ViewObject
    print (vo.DisplayMode)
    if vo.DisplayMode == 'VRML':
        iv = vo.IV
        if 1:
            with open(r'c:\Temp\mywrldump.txt', 'w') as the_file:
                the_file.write(iv)
        #print(iv)
        idx = iv.find("point [")
        idx2 = iv.find("]",idx)
        pts = iv[idx+len("point ["):idx2].replace('\n','').replace('  ','')
        idx = iv.find('VRMLTransform')
        idx2 = iv.find("}",idx)
        VTransf = iv[idx:idx2]
        #print(VTransf)
        scale_f = (1.,1.,1.)
        idx = VTransf.find('scale ')
        #print ('idx scale ',idx)
        if idx != -1:
            idx2 = VTransf.find("\n",idx)
            scale = VTransf[idx:idx2]
            #print (scale)
            scale_list = scale[6:].split(' ')
            scale_f = (float(scale_list[0]),float(scale_list[1]),float(scale_list[2]))
        print ('scale ', scale_f)
        translation_f = (0.,0.,0.)
        idx = VTransf.find("translation ")
        #print ('idx translation ',idx)
        if idx != -1:
            idx2 = VTransf.find("\n",idx)
            translation = VTransf[idx:idx2]
            #print (translation)
            translation_list = translation[12:].split(' ')
            translation_f = (float(translation_list[0]),float(translation_list[1]),float(translation_list[2]))
        print('translation ',translation_f)
        rotation_f=(0.,0.,0.,0.)
        idx = VTransf.find("rotation ")
        #print ('idx rotation ',idx)
        if idx != -1:
            idx2 = VTransf.find("\n",idx)
            rotation = VTransf[idx:idx2]
            #print (rotation)
            rotation_list = rotation[9:].replace('  ',' ').split(' ')
            #print (rotation_list)
            rotation_f = (float(rotation_list[0]),float(rotation_list[1]),float(rotation_list[2]),float(rotation_list[3]))
        print('rotation ',rotation_f)
        splt = pts.split(',')
        pointStrings = []
        points=[]
        for s in splt:
            pointStrings.append(s.split(' '))
        for ps in pointStrings:
            if len(ps)==4:
                if len(ps[3])>0:
                    ps = ps[1:4]
                else:
                    ps = ps[0:3]
            try:
                px = float(ps[0]); py = float(ps[1]); pz = float(ps[2]);
                points.append(Base.Vector(px,py,pz))
                xmax = max(px,xmax);xmin = min(px,xmin)
                ymax = max(py,ymax);ymin = min(py,ymin)
                zmax = max(pz,zmax);zmin = min(pz,zmin)
            except:
                FreeCAD.Console.PrintMessage('exception: '+str(ps)+'\n')
        #print (points)        
        print ('xmin=',xmin,' xmax=',xmax)
        print ('ymin=',ymin,' ymax=',ymax)
        print ('zmin=',zmin,' zmax=',zmax)
        #Draft.makeLine(Base.Vector(xmin,ymin,zmin),Base.Vector(xmin,ymin,zmax))
        #Draft.makeLine(Base.Vector(xmin,ymin,zmin),Base.Vector(xmax,ymin,zmin))
        #Draft.makeLine(Base.Vector(xmin,ymin,zmin),Base.Vector(xmin,ymax,zmin))
        BBox = App.ActiveDocument.addObject("Part::Box",sel[0].Name + "_BBox")
        BBox.Label = sel[0].Label+'_BBox'
        BBox.Length.Value = abs(xmax-xmin)*scale_f[0];
        BBox.Width.Value  = abs(ymax-ymin)*scale_f[1];
        BBox.Height.Value = abs(zmax-zmin)*scale_f[2];
        BBox.Placement=App.Placement(App.Vector(xmin*scale_f[0]+translation_f[0],ymin*scale_f[1]+translation_f[1],zmin*scale_f[2]+translation_f[2]), \
                       App.Rotation(App.Vector(rotation_f[0],rotation_f[1],rotation_f[2]),(rotation_f[3])), App.Vector(0,0,0))
        #BBox.Placement=App.Placement(App.Vector(xmin,ymin,zmin), App.Rotation(App.Vector(0,0,1),0), App.Vector(0,0,0))
        FreeCADGui.ActiveDocument.getObject(BBox.Name).LineColor  = (1., 0., 0.)
        FreeCADGui.ActiveDocument.getObject(BBox.Name).PointColor = (1., 0., 0.)
        FreeCADGui.ActiveDocument.getObject(BBox.Name).ShapeColor = (1., 0., 0.)
        FreeCADGui.ActiveDocument.getObject(BBox.Name).Transparency = 80
        App.Console.PrintMessage(BBox.Label + " Volume : " + str(BBox.Shape.Volume)+"\n")
    else:
        print('select a WRL model')
else:
    print('select a WRL model')
herbk
Veteran
Posts: 2660
Joined: Mon Nov 03, 2014 3:45 pm
Location: Windsbach, Bavarya (Germany)

Re: imported wrl: howto get a bounding box?

Post by herbk »

Hi Maurice,

Path creates a bounding box of a selected object.
Gruß Herbert
Post Reply