Thought not seen here, I work in the sheet metal workbench from time to time (last update), being my greater effort coding the unfold algorithm (related post). But I also work at the surrounding tools that this workbench needs, one of them is almost usable and is the reason of this post.
Add wall tool:
When I create the "dummy" shapes for testing the unfolding algorithm, the process is somewhat tedious. So I created this tool and developed it a bit to be easily integrated in the workbench in a future.
The utility of the tool is explained with this video:
The script contains the function "addWall", when calling it this are the possible input parameters:
thk = float: Sheet thickness, 1.0 is the default value
bendR = float: Bend radius, default value is 3.0
alpha = float: Angle of the new wall plane in reference to previous shape plane. 0 will give error (infinite bending radius needed ). By default 90º
L1 and L2 are better explained with a picture:
L1 and L2 values (float) modify the lengths noted by the white dimension marks. By default 0.0
Relief = boolean: Create or not square relief slots (I think this is the correct name, but tell me if not) like this:
Relief slots have two parameters:
rlfWidth = float: The width of the relief, by default 0.5
rlfDepth = float: The depth of the relif, by default 1.0
Other parameters are:
inverted = boolean: Reverse bend direction, false by default
create = boolean: This is to merge new wall with current object, true by default. Workbench utility
sketch = boolean: Automatically places a new sketch on new wall, false by default.
To use it, copy-paste the code at FreeCAD's python console, select one suitable face to place a new wall and then type "addWall( arguments )".
Imagine that you draw a house or a building in FreeCAD with all its details and interior elements. Wouldn't it be awesome if you could get into just like if you were walking inside?
This "tour camera" macro makes it possible.
This is the small "town" I've created for testing this macro. Download it here, get the full macro
code at the bottom of the post, and follow this microtutorial
(notice the camera track in orange)
How does it work?
Very simple, create a sketch over the surface you want to travel and exit. Then, switch view mode to "perspective view", and finally, at the previous sketch, select one line and paste this at the python console:
-Gets camera node and sketch track as a list of edges
-Iterates over the list of edges, positioning the camera from edge start to the edge length position, looking at the edge endpoint.
If you did the above, you should be travelling like this:
But is not only your screen the one that shakes badly when you go from one line to the next, mine too.
When camera jumps from one line to the next, it is orientated to the new endpoint in one step. That's the origin of the shaking behavior.
So, with a bit more of complication, I've improved this (new "town" included :):
In this version, camera walks along the actual edge pointing its view to a vector, with a given length and the same direction than the movement. Once this vector reaches the intersection with the next line, the camera rotates to align with the end point of that line.
In conclusion, you get a smooth transition and a nice walk around.
But that's the behavior roughly speaking, for the real thing, please look the code below.
# JMG November 2014from pivy import coin
import time
from FreeCAD import Base
camera = FreeCADGui.ActiveDocument.ActiveView.getCameraNode()
trajectory = Gui.Selection.getSelectionEx()[0].Object.Shape.Edges
camHeight =10# Height of the camera above the track
lookVectorLength =80# Distance from next line start where the camera starts to align with new directionfor i inrange( len( trajectory ) -1):
currEdge = trajectory[i]
currEdgeDir = ( currEdge.valueAt( currEdge.Length ) - currEdge.valueAt( 0.0 ) ).normalize()
nextEdge = trajectory[i+1]
nextEdgeDir = ( nextEdge.valueAt( nextEdge.Length ) - nextEdge.valueAt( 0.0 ) ).normalize()
currPos = currEdge.valueAt( 0.0 )
while (currPos - currEdge.valueAt( 0.0 ) ).Length < currEdge.Length:
currPos = currPos + currEdgeDir
camera.position.setValue( currPos + Base.Vector( 0, 0, camHeight ) )
cameraLookVector = currEdgeDir*lookVectorLength
if (cameraLookVector + currPos - currEdge.valueAt(0.0) ).Length > currEdge.Length:
L = ( cameraLookVector + ( currPos - currEdge.valueAt( 0.0 ) ) ).Length - currEdge.Length
lookVector = nextEdgeDir*L + nextEdge.valueAt( 0.0 )
else:
lookVector = currEdge.valueAt( currEdge.Length )
camera.pointAt( coin.SbVec3f( lookVector[0], lookVector[1], lookVector[2] + camHeight ), coin.SbVec3f( 0, 0, 1 ) )
Gui.updateGui()
time.sleep( 0.004 )
It features two configurable parameters:
- camHeight: height of the camera above the track ( over z axis )
-lookVectorLength: distance from the endpoint of the current line at witch the camera starts to rotate to align with the next line. Try values, the more it approaches to 0, the more shake you get. But too big values will shake too!
Before leaving, a funny thing: instead of the sketch track, select the 3d model and paste the code.
Code update:
A complex track may involve booleans, and , as every time you operate with a shape their subelements get reorganized, the result is something like what .rpv got trying to tour inside a 3d printer. Curious as a background video in presentations, but not the expected behavior.
The solution is to rearrange the list of edges so next edge start begins at the current edge end. It is achieved this way:
SelectedEdge = Gui.Selection.getSelectionEx()[0].SubObjects[0]
RawTrajectory = Gui.Selection.getSelectionEx()[0].Object.Shape.Edges
# Edge rearrangement inside trajectory list
trajectory = []
trajectory.append( SelectedEdge )
currentEdge = SelectedEdge
for n in range( len( RawTrajectory ) ):
for edge in RawTrajectory:
if edge.valueAt(0.0) == currentEdge.valueAt( currentEdge.Length ):
trajectory.append( edge )
currentEdge = edge
break
Full code:
Complete version featuring everything:
# JMG November2014
from pivy import coin
import time
from FreeCADimportBase
camera =FreeCADGui.ActiveDocument.ActiveView.getCameraNode()SelectedEdge=Gui.Selection.getSelectionEx()[0].SubObjects[0]
RawTrajectory=Gui.Selection.getSelectionEx()[0].Object.Shape.Edges
# Edge rearrangement inside trajectory list
trajectory =[]
trajectory.append( SelectedEdge )
currentEdge =SelectedEdge
for n in range( len( RawTrajectory ) ):
for edge inRawTrajectory:if edge.valueAt(0.0) == currentEdge.valueAt( currentEdge.Length ):
trajectory.append( edge )
currentEdge = edge
break
camHeight =10 # Heightof the camera above the track
lookVectorLength =80 # Distance from next line start where the camera starts to align with new direction
for i in range( len( trajectory ) -1):
currEdge = trajectory[i]
currEdgeDir = ( currEdge.valueAt( currEdge.Length ) - currEdge.valueAt( 0.0 ) ).normalize()
nextEdge = trajectory[i+1]
nextEdgeDir = ( nextEdge.valueAt( nextEdge.Length ) - nextEdge.valueAt( 0.0 ) ).normalize()
currPos = currEdge.valueAt( 0.0 )
while (currPos - currEdge.valueAt( 0.0 ) ).Length< currEdge.Length:
currPos = currPos + currEdgeDir
camera.position.setValue( currPos +Base.Vector( 0, 0, camHeight ) )
cameraLookVector = currEdgeDir*lookVectorLength
if (cameraLookVector + currPos - currEdge.valueAt(0.0) ).Length> currEdge.Length:L= ( cameraLookVector + ( currPos - currEdge.valueAt( 0.0 ) ) ).Length- currEdge.Length
lookVector = nextEdgeDir*L+ nextEdge.valueAt( 0.0 )
else:
lookVector = currEdge.valueAt( currEdge.Length )
camera.pointAt( coin.SbVec3f( lookVector[0], lookVector[1], lookVector[2] + camHeight ), coin.SbVec3f( 0, 0, 1 ) )
Gui.updateGui()
time.sleep( 0.004 )
Some time ago, when I was investigating about octrees and voxels to create a true CNC simulator, I played a bit with Coin3d, the scene graph render of FreeCAD.
Coin3d is coded in C++, but can be accesed with Python using Pivy.
While I didn't find anything specially useful, I realized that one could easily plot math equations like this:
...which have a nice look and permit you to take curious snapshots, for example:
The code is:
from pivy import coin
sg = FreeCADGui.ActiveDocument.ActiveView.getSceneGraph()
print sg
for s inrange(5):
for n inrange(-25,25):
for i inrange(-25,25):
col = coin.SoBaseColor()
col.rgb=(i*2,n*2,s*2)
trans = coin.SoTranslation()
# equation: z = f( i, n ) s is to plot at several heights
trans.translation.setValue([i*2.5,n*2.5,(i/2.0)**2+(n/2.0)**2+s*5])
cub = coin.SoSphere()
myCustomNode = coin.SoSeparator()
myCustomNode.addChild(col)
myCustomNode.addChild(trans)
myCustomNode.addChild(cub)
sg.addChild(myCustomNode)